@ably/ai-transport 0.2.0 → 0.3.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/README.md +10 -19
- package/dist/ably-ai-transport.js +1790 -1091
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/core/agent.d.ts +20 -5
- package/dist/core/channel-options.d.ts +57 -0
- package/dist/core/codec/codec-event.d.ts +9 -0
- package/dist/core/codec/decoder.d.ts +4 -1
- package/dist/core/codec/define-codec.d.ts +100 -0
- package/dist/core/codec/encoder.d.ts +2 -7
- package/dist/core/codec/field-bag.d.ts +85 -0
- package/dist/core/codec/fields.d.ts +141 -0
- package/dist/core/codec/index.d.ts +8 -1
- package/dist/core/codec/input-descriptor-decoder.d.ts +19 -0
- package/dist/core/codec/input-descriptor-encoder.d.ts +22 -0
- package/dist/core/codec/input-descriptors.d.ts +281 -0
- package/dist/core/codec/output-descriptor-decoder.d.ts +29 -0
- package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
- package/dist/core/codec/output-descriptors.d.ts +237 -0
- package/dist/core/codec/types.d.ts +95 -36
- package/dist/core/codec/well-known-inputs.d.ts +52 -0
- package/dist/core/transport/agent-view.d.ts +296 -0
- package/dist/core/transport/decode-fold.d.ts +40 -32
- package/dist/core/transport/headers.d.ts +30 -1
- package/dist/core/transport/index.d.ts +1 -1
- package/dist/core/transport/invocation.d.ts +1 -1
- package/dist/core/transport/load-history-pages.d.ts +71 -0
- package/dist/core/transport/load-history.d.ts +21 -16
- package/dist/core/transport/run-manager.d.ts +9 -11
- package/dist/core/transport/session-support.d.ts +55 -0
- package/dist/core/transport/tree.d.ts +165 -15
- package/dist/core/transport/types/agent.d.ts +120 -98
- package/dist/core/transport/types/client.d.ts +45 -12
- package/dist/core/transport/types/tree.d.ts +52 -10
- package/dist/core/transport/types/view.d.ts +55 -28
- package/dist/core/transport/view.d.ts +176 -58
- package/dist/core/transport/wire-log.d.ts +102 -0
- package/dist/errors.d.ts +10 -4
- package/dist/index.d.ts +6 -5
- package/dist/react/ably-ai-transport-react.js +784 -415
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/client-session-context.d.ts +2 -1
- package/dist/react/contexts/client-session-provider.d.ts +3 -0
- package/dist/react/index.d.ts +2 -1
- package/dist/react/internal/skipped-session.d.ts +8 -0
- package/dist/react/use-view.d.ts +3 -3
- package/dist/utils.d.ts +22 -54
- package/dist/vercel/ably-ai-transport-vercel.js +2297 -2026
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/decode-lifecycle.d.ts +9 -0
- package/dist/vercel/codec/events.d.ts +1 -2
- package/dist/vercel/codec/fields.d.ts +44 -0
- package/dist/vercel/codec/fold-content.d.ts +16 -0
- package/dist/vercel/codec/fold-data.d.ts +16 -0
- package/dist/vercel/codec/fold-input.d.ts +67 -0
- package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
- package/dist/vercel/codec/fold-text.d.ts +16 -0
- package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
- package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
- package/dist/vercel/codec/index.d.ts +5 -30
- package/dist/vercel/codec/inputs.d.ts +11 -0
- package/dist/vercel/codec/outputs.d.ts +11 -0
- package/dist/vercel/codec/reducer-state.d.ts +121 -0
- package/dist/vercel/codec/reducer.d.ts +20 -102
- package/dist/vercel/codec/tool-transitions.d.ts +0 -6
- package/dist/vercel/codec/wire-data.d.ts +34 -0
- package/dist/vercel/index.d.ts +1 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +2013 -9500
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -70
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +2 -1
- package/dist/vercel/run-end-reason.d.ts +66 -11
- package/dist/vercel/tool-part.d.ts +21 -0
- package/dist/vercel/transport/chat-transport.d.ts +0 -2
- package/dist/vercel/transport/index.d.ts +1 -1
- package/dist/vercel/transport/run-output-stream.d.ts +6 -8
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/src/constants.ts +2 -2
- package/src/core/agent.ts +43 -19
- package/src/core/channel-options.ts +89 -0
- package/src/core/codec/codec-event.ts +27 -0
- package/src/core/codec/decoder.ts +145 -21
- package/src/core/codec/define-codec.ts +432 -0
- package/src/core/codec/encoder.ts +13 -54
- package/src/core/codec/field-bag.ts +142 -0
- package/src/core/codec/fields.ts +193 -0
- package/src/core/codec/index.ts +43 -0
- package/src/core/codec/input-descriptor-decoder.ts +97 -0
- package/src/core/codec/input-descriptor-encoder.ts +150 -0
- package/src/core/codec/input-descriptors.ts +373 -0
- package/src/core/codec/output-descriptor-decoder.ts +139 -0
- package/src/core/codec/output-descriptor-encoder.ts +101 -0
- package/src/core/codec/output-descriptors.ts +307 -0
- package/src/core/codec/types.ts +99 -36
- package/src/core/codec/well-known-inputs.ts +96 -0
- package/src/core/transport/agent-session.ts +330 -589
- package/src/core/transport/agent-view.ts +738 -0
- package/src/core/transport/client-session.ts +74 -69
- package/src/core/transport/decode-fold.ts +57 -47
- package/src/core/transport/headers.ts +57 -4
- package/src/core/transport/index.ts +2 -1
- package/src/core/transport/invocation.ts +1 -1
- package/src/core/transport/load-history-pages.ts +220 -0
- package/src/core/transport/load-history.ts +63 -61
- package/src/core/transport/pipe-stream.ts +10 -1
- package/src/core/transport/run-manager.ts +25 -31
- package/src/core/transport/session-support.ts +96 -0
- package/src/core/transport/tree.ts +414 -47
- package/src/core/transport/types/agent.ts +129 -102
- package/src/core/transport/types/client.ts +49 -13
- package/src/core/transport/types/tree.ts +61 -12
- package/src/core/transport/types/view.ts +57 -28
- package/src/core/transport/view.ts +520 -172
- package/src/core/transport/wire-log.ts +189 -0
- package/src/errors.ts +10 -3
- package/src/index.ts +44 -11
- package/src/react/contexts/client-session-context.ts +1 -1
- package/src/react/contexts/client-session-provider.tsx +38 -2
- package/src/react/index.ts +2 -1
- package/src/react/internal/skipped-session.ts +62 -0
- package/src/react/use-client-session.ts +7 -30
- package/src/react/use-view.ts +3 -3
- package/src/utils.ts +31 -97
- package/src/vercel/codec/decode-lifecycle.ts +70 -0
- package/src/vercel/codec/events.ts +1 -3
- package/src/vercel/codec/fields.ts +58 -0
- package/src/vercel/codec/fold-content.ts +54 -0
- package/src/vercel/codec/fold-data.ts +46 -0
- package/src/vercel/codec/fold-input.ts +255 -0
- package/src/vercel/codec/fold-lifecycle.ts +85 -0
- package/src/vercel/codec/fold-text.ts +55 -0
- package/src/vercel/codec/fold-tool-input.ts +86 -0
- package/src/vercel/codec/fold-tool-output.ts +79 -0
- package/src/vercel/codec/index.ts +23 -63
- package/src/vercel/codec/inputs.ts +116 -0
- package/src/vercel/codec/outputs.ts +207 -0
- package/src/vercel/codec/reducer-state.ts +169 -0
- package/src/vercel/codec/reducer.ts +52 -838
- package/src/vercel/codec/tool-transitions.ts +1 -12
- package/src/vercel/codec/wire-data.ts +64 -0
- package/src/vercel/index.ts +1 -0
- package/src/vercel/react/contexts/chat-transport-context.ts +1 -1
- package/src/vercel/react/use-chat-transport.ts +8 -28
- package/src/vercel/react/use-message-sync.ts +5 -10
- package/src/vercel/run-end-reason.ts +95 -16
- package/src/vercel/tool-part.ts +25 -0
- package/src/vercel/transport/chat-transport.ts +10 -22
- package/src/vercel/transport/index.ts +1 -1
- package/src/vercel/transport/run-output-stream.ts +7 -8
- package/src/version.ts +1 -1
- package/dist/core/transport/branch-chain.d.ts +0 -43
- package/dist/core/transport/load-conversation.d.ts +0 -128
- package/dist/vercel/codec/decoder.d.ts +0 -9
- package/dist/vercel/codec/encoder.d.ts +0 -11
- package/src/core/transport/branch-chain.ts +0 -58
- package/src/core/transport/load-conversation.ts +0 -355
- package/src/vercel/codec/decoder.ts +0 -696
- package/src/vercel/codec/encoder.ts +0 -548
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ably-ai-transport-vercel.js","names":[],"sources":["../../src/constants.ts","../../src/utils.ts","../../src/core/codec/decoder.ts","../../src/core/codec/lifecycle-tracker.ts","../../src/vercel/codec/decoder.ts","../../src/errors.ts","../../src/core/codec/encoder.ts","../../src/vercel/codec/encoder.ts","../../src/vercel/codec/tool-transitions.ts","../../src/vercel/codec/reducer.ts","../../src/vercel/codec/index.ts","../../src/event-emitter.ts","../../src/logger.ts","../../src/vercel/transport/run-output-stream.ts","../../src/vercel/transport/chat-transport.ts","../../src/version.ts","../../src/core/agent.ts","../../src/core/transport/headers.ts","../../src/core/transport/internal/bounded-map.ts","../../src/core/transport/branch-chain.ts","../../src/core/transport/decode-fold.ts","../../src/core/transport/load-conversation.ts","../../src/core/transport/pipe-stream.ts","../../src/core/transport/run-manager.ts","../../src/core/transport/agent-session.ts","../../src/core/transport/invocation.ts","../../src/core/transport/tree.ts","../../src/core/transport/load-history.ts","../../src/core/transport/view.ts","../../src/core/transport/client-session.ts","../../src/vercel/transport/index.ts","../../src/vercel/run-end-reason.ts"],"sourcesContent":["/**\n * Shared constants used by both codec and transport layers.\n *\n * Header constants define the transport wire header names. Message and event\n * name constants define the session lifecycle signals on the channel.\n *\n * These live at the top level (not in codec/ or transport/) because both\n * layers need them — the codec core reads/writes stream and status headers,\n * while the transport layer reads/writes run, cancel, and role headers.\n */\n\n// ---------------------------------------------------------------------------\n// Stream headers (used by codec encoder/decoder core)\n// ---------------------------------------------------------------------------\n\n/** Header: whether this Ably message uses streaming (message appends) or is discrete. Always \"true\" or \"false\". */\nexport const HEADER_STREAM = 'stream';\n\n/** Header: lifecycle status of a streamed message. Only set when stream is \"true\". One of \"streaming\", \"complete\", or \"cancelled\". */\nexport const HEADER_STATUS = 'status';\n\n/** Header: stream identity. Set by the encoder on every streamed message; read by the decoder to correlate streams. */\nexport const HEADER_STREAM_ID = 'stream-id';\n\n/** Header: marks a message as a discrete message part (from writeMessages). Set by publishDiscreteBatch; not set on lifecycle events from publishDiscrete. */\nexport const HEADER_DISCRETE = 'discrete';\n\n// ---------------------------------------------------------------------------\n// Identity headers (used by transport for run correlation)\n// ---------------------------------------------------------------------------\n\n/** Header: run correlation ID. Set on every agent-published message and on continuation client inputs, but omitted from the originating fresh client input (the agent mints the run-id at run-start). */\nexport const HEADER_RUN_ID = 'run-id';\n\n/** Header: invocation correlation ID; identifies a specific invocation under a run. Agent-minted and stamped by the agent on every event it publishes for the invocation — run lifecycle (run-start/resume/suspend/end) and assistant outputs. Never set by the client on its input. */\nexport const HEADER_INVOCATION_ID = 'invocation-id';\n\n/**\n * Header: per-event identifier stamped by the client on every\n * client-published event in a send — user-message events AND amend\n * events (tool-approval responses, client tool outputs). Distinct from\n * `codec-message-id` so it survives edits/retries that reuse the same\n * codec-message-id, and so amend events that target an existing message can\n * carry their own per-send identity. The invocation body lists every\n * inputEventId the agent must observe on the channel before starting LLM\n * work — see `Run.start()`'s input-event lookup.\n */\nexport const HEADER_EVENT_ID = 'event-id';\n\n/** Header: message identity. Assigned per message (user or assistant). Used for optimistic reconciliation on the client. */\nexport const HEADER_CODEC_MESSAGE_ID = 'codec-message-id';\n\n/** Header: clientId of the user who initiated the run. Stamped by the client on its user input and re-stamped by the agent on the run's lifecycle and stream messages. */\nexport const HEADER_RUN_CLIENT_ID = 'run-client-id';\n\n/**\n * Header: clientId of the input event (the `ai-input`) that drove the\n * current invocation. The agent reads the publisher's Ably-level `clientId`\n * from the triggering input event on the channel and re-stamps it as\n * `input-client-id` on every event it publishes for that invocation\n * (run lifecycle and assistant outputs). May differ from\n * `run-client-id` on continuation invocations driven by an input\n * from a non-owner (e.g. a tool-result publish from a different client).\n * Not stamped on `ai-input` events themselves — the wire publisher's\n * Ably `clientId` already conveys that.\n */\nexport const HEADER_INPUT_CLIENT_ID = 'input-client-id';\n\n/** Header: message role (e.g. \"user\", \"assistant\"). */\nexport const HEADER_ROLE = 'role';\n\n// ---------------------------------------------------------------------------\n// Fork / branching headers\n// ---------------------------------------------------------------------------\n\n/** Header: the codec-message-id of the immediately preceding message in this branch. */\nexport const HEADER_PARENT = 'parent';\n\n/** Header: the codec-message-id of the message this one replaces (creates a fork). */\nexport const HEADER_FORK_OF = 'fork-of';\n\n/**\n * Header: the codec-message-id of the assistant message this run regenerates.\n *\n * Stamped on the regenerate wire (and echoed on `run-start`) when the\n * client requested a regeneration. A regenerate run parents at the SAME input\n * node as the reply it regenerates, so it joins that input's reply runs as a\n * same-parent sibling (no fork-of). The View consults this header to resolve\n * the message-level sibling group and to drop the regenerated message from\n * earlier Runs in the visible chain (Spec: AIT-CT13d).\n */\nexport const HEADER_MSG_REGENERATE = 'msg-regenerate';\n\n// ---------------------------------------------------------------------------\n// Run lifecycle headers\n// ---------------------------------------------------------------------------\n\n/** Header: reason a run ended (on ai-run-end messages). */\nexport const HEADER_RUN_REASON = 'run-reason';\n\n/**\n * Header: the `codec-message-id` of the input event that triggered the run.\n * The triggering input is the one whose `event-id` matches the invocation's\n * `inputEventId` (the last input of the originating send). The agent\n * re-stamps it on every event it publishes for the invocation (run\n * lifecycle + assistant outputs), mirroring `input-client-id`. This is the\n * codec-message-id the client owns at send time, so it lets the client\n * correlate any of those events back to the originating input without\n * depending on a client-minted run-id or invocation-id.\n */\nexport const HEADER_INPUT_CODEC_MESSAGE_ID = 'input-codec-message-id';\n\n// ---------------------------------------------------------------------------\n// Run-end error headers (set on `ai-run-end` when `run-reason: error`)\n// ---------------------------------------------------------------------------\n\n/** Header: numeric error code accompanying an `ai-run-end` with reason `error`. */\nexport const HEADER_ERROR_CODE = 'error-code';\n\n/** Header: human-readable error message accompanying an `ai-run-end` with reason `error`. */\nexport const HEADER_ERROR_MESSAGE = 'error-message';\n\n// ---------------------------------------------------------------------------\n// Message / event names\n// ---------------------------------------------------------------------------\n\n/**\n * Message name: client->agent cancel intent. Targets a run by `run-id` (a\n * continuation, whose run-id the client already knows) and/or by\n * `input-codec-message-id` (a fresh send, whose run-id the agent mints at\n * run-start — so the client can only key the cancel by the triggering input's\n * codec-message-id it owns at send time). The agent resolves whichever is\n * present to the registered run; a cancel that arrives before the run is known\n * (the input-event lookup hasn't resolved the input id to a run yet) is\n * buffered by `input-codec-message-id` and honoured when the run resolves it.\n * Also carries an `event-id` so channel rewind redelivers it to a per-request /\n * serverless agent that attaches after the cancel was published.\n */\nexport const EVENT_CANCEL = 'ai-cancel';\n\n/** Message name: server publishes this to signal a run has started. */\nexport const EVENT_RUN_START = 'ai-run-start';\n\n/**\n * Message name: server publishes this to signal a run has suspended — paused\n * awaiting participant input (e.g. a client tool result or approval) without\n * ending. The run stays live and may be resumed under the same `runId`.\n * Distinct from `ai-run-end`, which is terminal.\n */\nexport const EVENT_RUN_SUSPEND = 'ai-run-suspend';\n\n/**\n * Message name: server publishes this when a subsequent invocation re-enters an\n * already-started run (e.g. a tool-result follow-up under the same `runId`).\n * A pure re-entry signal: unlike `ai-run-start` it carries no `parent` / `fork-of`\n * (the original `ai-run-start` already established the run's structure).\n */\nexport const EVENT_RUN_RESUME = 'ai-run-resume';\n\n/** Message name: server publishes this to signal a run has ended. */\nexport const EVENT_RUN_END = 'ai-run-end';\n\n/**\n * Message name: every agent-published codec event (text, reasoning, tool calls,\n * tool outputs, lifecycle helpers, file / source parts, data-* chunks) rides\n * this single wire name. The codec event's own `type` is carried in the\n * codec-level `type` header so the decoder can dispatch.\n */\nexport const EVENT_AI_OUTPUT = 'ai-output';\n\n/**\n * Message name: every client-published codec event (user-message parts,\n * tool-approval responses, regenerate signals) rides this single wire\n * name. The codec event's own kind is carried in the codec-level `type`\n * header so the decoder can dispatch.\n */\nexport const EVENT_AI_INPUT = 'ai-input';\n","/**\n * Shared utilities for working with Ably messages.\n *\n * These are general-purpose helpers used by both the codec and transport\n * layers. They live at the top level to avoid either layer depending on\n * the other.\n */\n\nimport type * as Ably from 'ably';\n\n/**\n * Read one tier of the SDK's `extras.ai` namespace from an Ably message.\n * `extras.ai` is the SDK's reserved corner of the message envelope, split into\n * a `transport` tier (generic transport headers) and a `codec` tier (codec\n * headers). The application's own `extras.headers` is deliberately left\n * untouched.\n * @param message - The Ably message to read from.\n * @param tier - Which `extras.ai` sub-namespace to read.\n * @returns The tier's headers record, or an empty object if absent.\n */\nconst getAiTier = (message: Ably.InboundMessage, tier: 'transport' | 'codec'): Record<string, string> => {\n // CAST: Ably SDK types `extras` as `any`; runtime checks below guard access.\n const extras = message.extras as unknown;\n if (!extras || typeof extras !== 'object') return {};\n const ai = (extras as { ai?: unknown }).ai;\n if (!ai || typeof ai !== 'object') return {};\n const sub = (ai as Record<string, unknown>)[tier];\n if (!sub || typeof sub !== 'object') return {};\n // CAST: Ably wire protocol guarantees the tier is Record<string, string>\n // when present, verified by the runtime guards above.\n return sub as Record<string, string>;\n};\n\n/**\n * Extract the transport-tier headers (`extras.ai.transport`) from an Ably\n * InboundMessage. These are the generic transport headers (run/stream/identity/\n * branching), set and read by the transport layer.\n * @param message - The Ably message to extract headers from.\n * @returns The transport headers record, or an empty object if absent.\n */\nexport const getTransportHeaders = (message: Ably.InboundMessage): Record<string, string> =>\n getAiTier(message, 'transport');\n\n/**\n * Extract the codec-tier headers (`extras.ai.codec`) from an Ably\n * InboundMessage. These are the codec's own headers, with no prefix — the\n * tier isolates them from transport headers.\n * @param message - The Ably message to extract headers from.\n * @returns The codec headers record, or an empty object if absent.\n */\nexport const getCodecHeaders = (message: Ably.InboundMessage): Record<string, string> => getAiTier(message, 'codec');\n\n/**\n * Parse a JSON string, returning undefined on failure.\n * @param value - The JSON string to parse.\n * @returns The parsed value, or undefined if parsing fails.\n */\nexport const parseJson = (value: string | undefined): unknown => {\n if (value === undefined) return undefined;\n try {\n return JSON.parse(value) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Set a header value if defined, skipping undefined and null. Strings are set directly,\n * booleans and numbers are stringified, objects are JSON-serialized.\n * @param headers - The headers object to mutate.\n * @param key - The header key.\n * @param value - The value to set.\n */\nexport const setIfPresent = (headers: Record<string, string>, key: string, value: unknown): void => {\n if (value === undefined || value === null) return;\n if (typeof value === 'string') {\n headers[key] = value;\n } else if (typeof value === 'boolean' || typeof value === 'number') {\n headers[key] = String(value);\n } else if (typeof value === 'object') {\n headers[key] = JSON.stringify(value);\n }\n};\n\n/**\n * Merge two header records into a new object. Later values override earlier ones.\n * Undefined inputs are treated as empty.\n * @param base - Base headers (lower priority).\n * @param overrides - Override headers (higher priority).\n * @returns A new merged headers object.\n */\nexport const mergeHeaders = (\n base: Record<string, string> | undefined,\n overrides: Record<string, string> | undefined,\n): Record<string, string> => ({\n ...base,\n ...overrides,\n});\n\n/**\n * Parse a boolean header (\"true\"/\"false\"), returning undefined if absent.\n * @param value - The header string to parse.\n * @returns True if \"true\", false for any other string, or undefined if absent.\n */\nexport const parseBool = (value: string | undefined): boolean | undefined => {\n if (value === undefined) return undefined;\n return value === 'true';\n};\n\n/** A record carrying an optional Ably `serial`, orderable by {@link compareBySerial}. */\ninterface HasSerial {\n /** Ably serial, or undefined if the server has not yet assigned one. */\n readonly serial?: string;\n}\n\n/**\n * Comparator that orders records by their Ably `serial` ascending\n * (chronological). Serials are lexicographically comparable; records whose\n * serial is undefined sort last. Pass directly to `Array.prototype.sort`.\n * @param a - First record to compare.\n * @param b - Second record to compare.\n * @returns Negative if `a` precedes `b`, positive if `a` follows `b`, 0 if equal.\n */\nexport const compareBySerial = (a: HasSerial, b: HasSerial): number => {\n if (a.serial === undefined && b.serial === undefined) return 0;\n if (a.serial === undefined) return 1;\n if (b.serial === undefined) return -1;\n if (a.serial < b.serial) return -1;\n if (a.serial > b.serial) return 1;\n return 0;\n};\n\n/**\n * Read a domain header value from a codec-tier headers record.\n * @param headers - The codec headers record to read from.\n * @param key - The domain key (e.g. `'toolCallId'`).\n * @returns The header value, or undefined if absent.\n */\nexport const getDomainHeader = (headers: Record<string, string>, key: string): string | undefined => headers[key];\n\n/**\n * Mapped type that converts properties whose type includes `undefined`\n * into optional properties with `undefined` excluded from the value.\n * Properties typed as `unknown` are kept required (since `undefined extends unknown`\n * is always true, but `unknown` fields are intentionally broad, not optional).\n */\nexport type Stripped<T> = {\n [K in keyof T as undefined extends T[K] ? (unknown extends T[K] ? K : never) : K]: T[K];\n} & {\n [K in keyof T as undefined extends T[K] ? (unknown extends T[K] ? never : K) : never]?: Exclude<T[K], undefined>;\n};\n\n/**\n * Remove all keys whose value is `undefined` from a shallow object.\n * Returns a new object — the input is not mutated. Useful for building\n * chunk literals with optional fields without conditional spread noise.\n *\n * The return type converts `{ foo: T | undefined }` to `{ foo?: T }`,\n * matching the optional-field pattern used by the AI SDK chunk types.\n * @param obj - The object to strip undefined values from.\n * @returns A shallow copy with undefined-valued keys removed.\n */\nexport const stripUndefined = <T extends Record<string, unknown>>(obj: T): Stripped<T> => {\n const result = {} as Record<string, unknown>;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n // CAST: The runtime strip guarantees the Stripped<T> contract —\n // required keys are always present, optional keys are absent when undefined.\n return result as Stripped<T>;\n};\n\n// ---------------------------------------------------------------------------\n// DomainHeaderReader — typed accessors for domain headers\n// ---------------------------------------------------------------------------\n\n/**\n * Typed accessor wrapper around a headers record for reading domain headers.\n * Reduces repetitive `getDomainHeader` + `parseBool` / `parseJson` chains.\n */\nexport interface DomainHeaderReader {\n /** Read a domain header as a string, or undefined if absent. */\n str(key: string): string | undefined;\n /** Read a domain header as a string, falling back to a default if absent. */\n strOr(key: string, fallback: string): string;\n /** Read a domain header as a boolean: `true` only for the exact string \"true\", `false` for any other present value, or undefined if absent. */\n bool(key: string): boolean | undefined;\n /** Read a domain header as parsed JSON, or undefined if absent or invalid. */\n json(key: string): unknown;\n}\n\n/**\n * Create a {@link DomainHeaderReader} over a headers record.\n * @param headers - The raw headers record to read domain headers from.\n * @returns A typed accessor for domain header values.\n */\nexport const headerReader = (headers: Record<string, string>): DomainHeaderReader => ({\n str: (key: string) => getDomainHeader(headers, key),\n strOr: (key: string, fallback: string) => getDomainHeader(headers, key) ?? fallback,\n bool: (key: string) => parseBool(getDomainHeader(headers, key)),\n json: (key: string) => parseJson(getDomainHeader(headers, key)),\n});\n\n// ---------------------------------------------------------------------------\n// DomainHeaderWriter — typed builder for domain headers\n// ---------------------------------------------------------------------------\n\n/**\n * Fluent builder for constructing domain header records with typed setters.\n * Mirrors {@link DomainHeaderReader} with the same method names for symmetry.\n * Undefined values are silently skipped on all setters.\n */\nexport interface DomainHeaderWriter {\n /** Set a string domain header. Skips if value is undefined. */\n str(key: string, value: string | undefined): DomainHeaderWriter;\n /** Set a boolean domain header (serialized as \"true\"/\"false\"). Skips if value is undefined. */\n bool(key: string, value: boolean | undefined): DomainHeaderWriter;\n /** Set a JSON-serialized domain header. Skips if value is undefined or null. */\n json(key: string, value: unknown): DomainHeaderWriter;\n /** Return the accumulated headers record. */\n build(): Record<string, string>;\n}\n\n/**\n * Create a {@link DomainHeaderWriter} for building a codec-tier headers record.\n * @returns A fluent builder that accumulates codec headers under their bare keys.\n */\nexport const headerWriter = (): DomainHeaderWriter => {\n const h: Record<string, string> = {};\n const writer: DomainHeaderWriter = {\n str: (key: string, value: string | undefined) => {\n if (value !== undefined) h[key] = value;\n return writer;\n },\n bool: (key: string, value: boolean | undefined) => {\n if (value !== undefined) h[key] = String(value);\n return writer;\n },\n json: (key: string, value: unknown) => {\n if (value !== undefined && value !== null) h[key] = JSON.stringify(value);\n return writer;\n },\n build: () => h,\n };\n return writer;\n};\n","/**\n * Decoder core — action dispatch and serial tracking machinery.\n *\n * Handles the Ably message action patterns (create, append, update, delete)\n * and delegates to domain-specific hooks for event building and discrete\n * event decoding.\n *\n * Domain decoders call `createDecoderCore(hooks, options)` and provide hooks\n * for stream classification, event building, and discrete decoding. Hooks\n * return a flat `TEvent[]` — no event-vs-message union. Per-message routing\n * concerns (`codec-message-id`) are handled by the SDK via `ReducerMeta`, not\n * here.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { getCodecHeaders, getTransportHeaders } from '../../utils.js';\nimport type { MessagePayload, StreamTrackerState } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for creating a decoder core. */\nexport interface DecoderCoreOptions {\n /** Called when a tracked stream is replaced (non-prefix update). Receives the tracker with updated state. */\n onStreamUpdate?: (tracker: StreamTrackerState) => void;\n /** Called when a message is deleted. Receives the serial and tracker (if one exists). */\n onStreamDelete?: (serial: string, tracker: StreamTrackerState | undefined) => void;\n /** Logger instance for diagnostic output. */\n logger?: Logger;\n}\n\n// ---------------------------------------------------------------------------\n// Domain hooks\n// ---------------------------------------------------------------------------\n\n/** Hooks that a domain codec provides to the decoder core for stream classification and event building. */\nexport interface DecoderCoreHooks<TEvent> {\n /**\n * Build domain events emitted when a new stream starts. May return multiple\n * events (e.g. a start event and a start-step event).\n */\n buildStartEvents(tracker: StreamTrackerState): TEvent[];\n\n /** Build domain events for a text delta received on a stream. */\n buildDeltaEvents(tracker: StreamTrackerState, delta: string): TEvent[];\n\n /**\n * Build domain events emitted when a stream completes (status:complete).\n * Not called for cancelled streams. The closing codec headers may differ\n * from tracker.codecHeaders if the closing append carried updated headers.\n */\n buildEndEvents(tracker: StreamTrackerState, closingCodecHeaders: Record<string, string>): TEvent[];\n\n /**\n * Decode a discrete message (a `message.create` whose stream header is not\n * \"true\", or a non-streamable first-contact update). Handles user messages,\n * tool lifecycle, data-*, etc.\n */\n decodeDiscrete(input: MessagePayload): TEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\n/** The decoder core returned by {@link createDecoderCore}. */\nexport interface DecoderCore<TEvent> {\n /** Decode a single Ably message into zero or more domain TEvents. */\n decode(message: Ably.InboundMessage): TEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD7\nclass DefaultDecoderCore<TEvent> implements DecoderCore<TEvent> {\n private readonly _hooks: DecoderCoreHooks<TEvent>;\n private readonly _logger: Logger | undefined;\n private readonly _onStreamUpdate: ((tracker: StreamTrackerState) => void) | undefined;\n private readonly _onStreamDelete: ((serial: string, tracker: StreamTrackerState | undefined) => void) | undefined;\n private readonly _serialState = new Map<string, StreamTrackerState>();\n\n constructor(hooks: DecoderCoreHooks<TEvent>, options: DecoderCoreOptions = {}) {\n this._hooks = hooks;\n this._onStreamUpdate = options.onStreamUpdate;\n this._onStreamDelete = options.onStreamDelete;\n this._logger = options.logger?.withContext({ component: 'DecoderCore' });\n }\n\n decode(message: Ably.InboundMessage): TEvent[] {\n const action = message.action;\n\n this._logger?.trace('DefaultDecoderCore.decode();', { action, serial: message.serial, name: message.name });\n\n switch (action) {\n // Spec: AIT-CD7a\n case 'message.create': {\n const payload = this._toPayload(message);\n return payload.transportHeaders?.[HEADER_STREAM] === 'true'\n ? this._decodeStreamedCreate(payload, message.serial)\n : this._hooks.decodeDiscrete(payload);\n }\n\n case 'message.append': {\n return this._decodeAppend(message);\n }\n\n case 'message.update': {\n return this._decodeUpdate(message);\n }\n\n case 'message.delete': {\n return this._decodeDelete(message);\n }\n\n default: {\n return [];\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: extract MessagePayload\n // -------------------------------------------------------------------------\n\n private _toPayload(message: Ably.InboundMessage): MessagePayload {\n return {\n name: message.name ?? '',\n // CAST: Ably SDK types `data` as `any`; cast to unknown is the safe boundary type.\n data: message.data as unknown,\n transportHeaders: getTransportHeaders(message),\n codecHeaders: getCodecHeaders(message),\n };\n }\n\n /**\n * Extract string data from an Ably message, for stream accumulation paths.\n * @param message - The Ably message to extract string data from.\n * @returns The string data, or empty string if data is not a string.\n */\n private _stringData(message: Ably.InboundMessage): string {\n return typeof message.data === 'string' ? message.data : '';\n }\n\n // -------------------------------------------------------------------------\n // Private: safe callback invocation\n // -------------------------------------------------------------------------\n\n private _invokeOnStreamUpdate(tracker: StreamTrackerState): void {\n if (!this._onStreamUpdate) return;\n try {\n this._onStreamUpdate(tracker);\n } catch (error) {\n this._logger?.error('DefaultDecoderCore._invokeOnStreamUpdate(); callback threw', { error });\n }\n }\n\n private _invokeOnStreamDelete(serial: string, tracker: StreamTrackerState | undefined): void {\n if (!this._onStreamDelete) return;\n try {\n this._onStreamDelete(serial, tracker);\n } catch (error) {\n this._logger?.error('DefaultDecoderCore._invokeOnStreamDelete(); callback threw', { error });\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: streamed message create\n // -------------------------------------------------------------------------\n\n private _decodeStreamedCreate(payload: MessagePayload, serial: string | undefined): TEvent[] {\n if (!serial) return [];\n\n const streamId = payload.transportHeaders?.[HEADER_STREAM_ID] ?? '';\n\n const tracker: StreamTrackerState = {\n name: payload.name,\n streamId,\n accumulated: '',\n codecHeaders: { ...payload.codecHeaders },\n transportHeaders: { ...payload.transportHeaders },\n closed: false,\n };\n this._serialState.set(serial, tracker);\n\n this._logger?.debug('DefaultDecoderCore._decodeStreamedCreate(); new stream', {\n name: payload.name,\n streamId,\n serial,\n });\n\n return this._hooks.buildStartEvents(tracker);\n }\n\n // -------------------------------------------------------------------------\n // Private: append handling\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD8\n private _decodeAppend(message: Ably.InboundMessage): TEvent[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const tracker = this._serialState.get(serial);\n if (!tracker) {\n // Unknown serial on append — treat as first-contact update\n return this._decodeUpdate(message);\n }\n\n const transport = getTransportHeaders(message);\n const closingCodec = getCodecHeaders(message);\n const delta = typeof message.data === 'string' ? message.data : '';\n const status = transport[HEADER_STATUS];\n const outputs: TEvent[] = [];\n\n if (delta.length > 0) {\n tracker.accumulated += delta;\n outputs.push(...this._hooks.buildDeltaEvents(tracker, delta));\n }\n\n if (status === 'complete' && !tracker.closed) {\n tracker.closed = true;\n outputs.push(...this._hooks.buildEndEvents(tracker, closingCodec));\n this._logger?.debug('DefaultDecoderCore._decodeAppend(); stream complete', { streamId: tracker.streamId });\n } else if (status === 'cancelled' && !tracker.closed) {\n tracker.closed = true;\n this._logger?.debug('DefaultDecoderCore._decodeAppend(); stream cancelled', { streamId: tracker.streamId });\n }\n\n return outputs;\n }\n\n // -------------------------------------------------------------------------\n // Private: update handling (first-contact, prefix-match, replacement)\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD9\n private _decodeUpdate(message: Ably.InboundMessage): TEvent[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const payload = this._toPayload(message);\n const transport = payload.transportHeaders ?? {};\n const codec = payload.codecHeaders ?? {};\n const isStreamed = transport[HEADER_STREAM] === 'true';\n const status = transport[HEADER_STATUS];\n\n const tracker = this._serialState.get(serial);\n\n if (!tracker) {\n return this._decodeFirstContact(payload, isStreamed, status, serial);\n }\n\n // Updates to tracked streams use string data for prefix-match accumulation\n const data = this._stringData(message);\n\n // --- Tracker exists: prefix-match or replacement ---\n if (data.startsWith(tracker.accumulated)) {\n const delta = data.slice(tracker.accumulated.length);\n const outputs: TEvent[] = [];\n\n if (delta.length > 0) {\n tracker.accumulated = data;\n outputs.push(...this._hooks.buildDeltaEvents(tracker, delta));\n }\n\n if (status === 'complete' && !tracker.closed) {\n tracker.closed = true;\n outputs.push(...this._hooks.buildEndEvents(tracker, codec));\n } else if (status === 'cancelled' && !tracker.closed) {\n tracker.closed = true;\n }\n\n return outputs;\n }\n\n // --- Replacement (NOT a prefix match) ---\n tracker.accumulated = data;\n tracker.codecHeaders = { ...codec };\n tracker.transportHeaders = { ...transport };\n\n this._invokeOnStreamUpdate(tracker);\n\n return [];\n }\n\n private _decodeFirstContact(\n payload: MessagePayload,\n isStreamed: boolean,\n status: string | undefined,\n serial: string,\n ): TEvent[] {\n // Non-streamed messages are discrete\n if (!isStreamed) {\n return this._hooks.decodeDiscrete(payload);\n }\n\n const streamId = payload.transportHeaders?.[HEADER_STREAM_ID] ?? '';\n const codec = payload.codecHeaders ?? {};\n const data = typeof payload.data === 'string' ? payload.data : '';\n\n this._logger?.debug('DefaultDecoderCore._decodeFirstContact(); first-contact stream', {\n name: payload.name,\n streamId,\n serial,\n });\n\n // Create tracker\n const newTracker: StreamTrackerState = {\n name: payload.name,\n streamId,\n accumulated: data,\n codecHeaders: { ...codec },\n transportHeaders: { ...payload.transportHeaders },\n closed: status === 'complete' || status === 'cancelled',\n };\n this._serialState.set(serial, newTracker);\n\n // Emit start + delta (if any) + end (if complete)\n const outputs = this._hooks.buildStartEvents(newTracker);\n\n if (data.length > 0) {\n outputs.push(...this._hooks.buildDeltaEvents(newTracker, data));\n }\n\n if (status === 'complete') {\n outputs.push(...this._hooks.buildEndEvents(newTracker, codec));\n }\n\n return outputs;\n }\n\n // -------------------------------------------------------------------------\n // Private: delete handling\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD10\n private _decodeDelete(message: Ably.InboundMessage): TEvent[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const tracker = this._serialState.get(serial);\n\n this._invokeOnStreamDelete(serial, tracker);\n\n if (tracker) {\n tracker.accumulated = '';\n tracker.closed = true;\n }\n\n this._logger?.debug('DefaultDecoderCore._decodeDelete();', { serial });\n\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a decoder core with the given domain hooks.\n * @param hooks - Domain-specific hooks for stream classification, event building, and discrete decoding.\n * @param options - Decoder configuration (callbacks, logger).\n * @returns A new {@link DecoderCore} instance.\n */\nexport const createDecoderCore = <TEvent>(\n hooks: DecoderCoreHooks<TEvent>,\n options: DecoderCoreOptions = {},\n): DecoderCore<TEvent> => new DefaultDecoderCore(hooks, options);\n","/**\n * Generic lifecycle tracker for codec decoders.\n *\n * Manages per-scope (typically per-run) tracking of lifecycle phases that\n * must be emitted before content events. When a phase has not been emitted\n * (e.g. mid-stream join), the tracker synthesizes the missing events using\n * codec-provided build functions.\n *\n * Codecs configure the tracker with an ordered list of phases, then compose\n * it into their decoder hooks. The tracker is independent of any specific\n * codec or event type.\n */\n\n// ---------------------------------------------------------------------------\n// Phase configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for a single lifecycle phase that may need to be\n * synthesized when missing from the wire stream.\n */\nexport interface PhaseConfig<TEvent> {\n /** Unique key identifying this phase (e.g. \"start\", \"start-step\"). */\n key: string;\n /**\n * Build the synthetic event(s) for this phase. Called with a context\n * record that codecs populate at the call site — the tracker passes\n * it through without interpreting it.\n * @param context - Key-value pairs from the call site (e.g. headers).\n * @returns One or more synthetic events to emit for this phase.\n */\n build(context: Record<string, string | undefined>): TEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Tracker interface\n// ---------------------------------------------------------------------------\n\n/**\n * Per-scope lifecycle tracker that ensures required phases are emitted\n * before content events, synthesizing missing ones for mid-stream joins.\n *\n * Scoped by an arbitrary string key (typically a run ID). Each scope\n * tracks independently which phases have been emitted.\n */\nexport interface LifecycleTracker<TEvent> {\n /**\n * Ensure all configured phases have been emitted for the given scope.\n * Synthesizes and returns the events for any phases not yet marked as\n * emitted, marking each as emitted so it is not synthesized again.\n * Returns an empty array if all phases are already emitted.\n * @param scopeId - The scope to check (e.g. run ID).\n * @param context - Key-value pairs passed through to phase build functions.\n * @returns Synthetic events for missing phases, in configuration order.\n */\n ensurePhases(scopeId: string, context: Record<string, string | undefined>): TEvent[];\n\n /**\n * Mark a phase as emitted from the wire (not synthetic). Call this\n * when the real event arrives so the tracker does not re-synthesize it.\n * @param scopeId - The scope (e.g. run ID).\n * @param phaseKey - The phase key to mark.\n */\n markEmitted(scopeId: string, phaseKey: string): void;\n\n /**\n * Reset a phase so it will be re-synthesized on the next\n * {@link ensurePhases} call. Used for repeating phases (e.g. \"start-step\"\n * resets after \"finish-step\").\n * @param scopeId - The scope (e.g. run ID).\n * @param phaseKey - The phase key to reset.\n */\n resetPhase(scopeId: string, phaseKey: string): void;\n\n /**\n * Remove all tracking state for a scope. Call on run completion\n * (finish, cancel) to free memory.\n * @param scopeId - The scope to clear.\n */\n clearScope(scopeId: string): void;\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD13\nclass DefaultLifecycleTracker<TEvent> implements LifecycleTracker<TEvent> {\n private readonly _phases: PhaseConfig<TEvent>[];\n private readonly _emitted = new Map<string, Set<string>>();\n\n constructor(phases: PhaseConfig<TEvent>[]) {\n this._phases = phases;\n }\n\n ensurePhases(scopeId: string, context: Record<string, string | undefined>): TEvent[] {\n const emitted = this._getOrCreate(scopeId);\n const events: TEvent[] = [];\n for (const phase of this._phases) {\n if (!emitted.has(phase.key)) {\n emitted.add(phase.key);\n events.push(...phase.build(context));\n }\n }\n return events;\n }\n\n markEmitted(scopeId: string, phaseKey: string): void {\n this._getOrCreate(scopeId).add(phaseKey);\n }\n\n resetPhase(scopeId: string, phaseKey: string): void {\n this._emitted.get(scopeId)?.delete(phaseKey);\n }\n\n clearScope(scopeId: string): void {\n this._emitted.delete(scopeId);\n }\n\n private _getOrCreate(scopeId: string): Set<string> {\n let set = this._emitted.get(scopeId);\n if (!set) {\n set = new Set();\n this._emitted.set(scopeId, set);\n }\n return set;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a lifecycle tracker configured with the given phases.\n * Phases are checked and synthesized in array order.\n * @param phases - Ordered phase configurations.\n * @returns A new {@link LifecycleTracker} instance.\n */\nexport const createLifecycleTracker = <TEvent>(phases: PhaseConfig<TEvent>[]): LifecycleTracker<TEvent> =>\n new DefaultLifecycleTracker(phases);\n","/**\n * Vercel AI SDK Decoder.\n *\n * Maps Ably inbound messages to {@link DecodedMessage} — a `{ inputs,\n * outputs }` tagged result. The decoder routes by the wire `name`\n * (`ai-input` vs `ai-output`) so the SDK never has to inspect direction:\n * input-side messages produce `VercelInput` variants; output-side\n * messages produce `VercelOutput` (`UIMessageChunk`) variants.\n *\n * The `LifecycleTracker` is an internal helper used to pre-roll missing\n * `start` / `start-step` chunks on mid-stream join (history compaction,\n * rewind miss, partial page) so the reducer always sees a clean event\n * sequence for streamed output.\n *\n * Receive-side dispatch reads the wire `name` first and then routes by\n * the codec `type` header carrying the codec event type. Codec headers live\n * under `extras.ai.codec` and transport headers under `extras.ai.transport`;\n * both are read unprefixed from their respective tier.\n */\n\nimport type * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport {\n EVENT_AI_INPUT,\n EVENT_AI_OUTPUT,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_DISCRETE,\n HEADER_ROLE,\n HEADER_RUN_ID,\n} from '../../constants.js';\nimport type { DecoderCore, DecoderCoreHooks, DecoderCoreOptions } from '../../core/codec/decoder.js';\nimport { createDecoderCore } from '../../core/codec/decoder.js';\nimport type { LifecycleTracker } from '../../core/codec/lifecycle-tracker.js';\nimport { createLifecycleTracker } from '../../core/codec/lifecycle-tracker.js';\nimport type {\n DecodedMessage,\n Decoder,\n MessagePayload,\n StreamTrackerState,\n UserMessage,\n} from '../../core/codec/types.js';\nimport { type DomainHeaderReader, headerReader as rawHeaderReader, stripUndefined } from '../../utils.js';\nimport type { VercelInput, VercelOutput } from './events.js';\n\n// Decoder-internal union — the codec emits inputs and outputs through the\n// same flat list from the underlying core and partitions on the way out.\ntype AnyEvent = VercelInput | VercelOutput;\n\n// ---------------------------------------------------------------------------\n// Vercel-specific header reader (casts providerMetadata to AI.ProviderMetadata)\n// ---------------------------------------------------------------------------\n\ninterface VercelHeaderReader extends DomainHeaderReader {\n /** Read the `providerMetadata` domain header, cast to the AI SDK type. */\n providerMetadata(): AI.ProviderMetadata | undefined;\n}\n\n/**\n * Create a header reader that adds Vercel-specific `providerMetadata` typing.\n * @param headers - The raw headers record to read domain headers from.\n * @returns A typed accessor with Vercel-specific providerMetadata typing.\n */\nconst headerReader = (headers: Record<string, string>): VercelHeaderReader => {\n const base = rawHeaderReader(headers);\n return {\n ...base,\n // CAST: Trust boundary — the encoder serialized a valid ProviderMetadata value.\n providerMetadata: () => base.json('providerMetadata') as AI.ProviderMetadata | undefined,\n };\n};\n\n// ---------------------------------------------------------------------------\n// Wire format types (trust boundaries for JSON-parsed data)\n// ---------------------------------------------------------------------------\n\n/** Wire format for the agent-side `tool-input-error` chunk data payload. */\ninterface ToolInputErrorWireData {\n errorText?: string;\n input?: unknown;\n}\n\n/** Wire format for the `tool-output-available` (agent) / `tool-result` (client) data payload. */\ninterface ToolOutputAvailableWireData {\n output?: unknown;\n}\n\n/** Wire format for the agent-side `tool-output-error` chunk data payload. */\ninterface AgentToolOutputErrorWireData {\n errorText?: string;\n}\n\n/** Wire format for the client-side `tool-result-error` input data payload. */\ninterface ClientToolResultErrorWireData {\n message?: string;\n}\n\n// ---------------------------------------------------------------------------\n// JSON boundary helpers\n// ---------------------------------------------------------------------------\n\nconst parseFinishReason = (value: string | undefined, fallback: AI.FinishReason): AI.FinishReason => {\n if (\n value === 'stop' ||\n value === 'length' ||\n value === 'content-filter' ||\n value === 'tool-calls' ||\n value === 'error' ||\n value === 'other'\n ) {\n return value;\n }\n return fallback;\n};\n\nconst isDataEventName = (name: string): name is `data-${string}` => name.startsWith('data-');\n\nconst parseJsonOrString = (value: string): unknown => {\n if (!value) return undefined;\n try {\n // CAST: JSON.parse returns any; unknown is the safe trust-boundary type.\n return JSON.parse(value) as unknown;\n } catch {\n return value;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Streamed message event builders (output-side)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the codec event type from a tracker's codec headers. The encoder\n * stamps the codec `type` header on every `ai-output` publish; the value\n * carries the AI-SDK chunk family (`text` / `reasoning` / `tool-input`)\n * that the stream represents.\n * @param tracker - The stream tracker carrying the persistent headers.\n * @returns The codec event type, or the empty string when absent.\n */\nconst codecTypeOf = (tracker: StreamTrackerState): string => headerReader(tracker.codecHeaders).strOr('type', '');\n\nconst buildStartChunk = (tracker: StreamTrackerState): AI.UIMessageChunk => {\n const r = headerReader(tracker.codecHeaders);\n switch (codecTypeOf(tracker)) {\n case 'text': {\n return stripUndefined({\n type: 'text-start' as const,\n id: tracker.streamId,\n providerMetadata: r.providerMetadata(),\n });\n }\n case 'reasoning': {\n return stripUndefined({\n type: 'reasoning-start' as const,\n id: tracker.streamId,\n providerMetadata: r.providerMetadata(),\n });\n }\n case 'tool-input': {\n return stripUndefined({\n type: 'tool-input-start' as const,\n toolCallId: tracker.streamId,\n toolName: r.strOr('toolName', ''),\n dynamic: r.bool('dynamic'),\n title: r.str('title'),\n providerExecuted: r.bool('providerExecuted'),\n providerMetadata: r.providerMetadata(),\n });\n }\n default: {\n return { type: 'text-start', id: tracker.streamId };\n }\n }\n};\n\nconst buildDeltaChunk = (tracker: StreamTrackerState, delta: string): AI.UIMessageChunk => {\n switch (codecTypeOf(tracker)) {\n case 'text': {\n return { type: 'text-delta', id: tracker.streamId, delta };\n }\n case 'reasoning': {\n return { type: 'reasoning-delta', id: tracker.streamId, delta };\n }\n case 'tool-input': {\n return { type: 'tool-input-delta', toolCallId: tracker.streamId, inputTextDelta: delta };\n }\n default: {\n return { type: 'text-delta', id: tracker.streamId, delta };\n }\n }\n};\n\nconst buildEndChunk = (tracker: StreamTrackerState, closingHeaders: Record<string, string>): AI.UIMessageChunk => {\n const r = headerReader(closingHeaders);\n switch (codecTypeOf(tracker)) {\n case 'text': {\n return stripUndefined({\n type: 'text-end' as const,\n id: tracker.streamId,\n providerMetadata: r.providerMetadata(),\n });\n }\n case 'reasoning': {\n return stripUndefined({\n type: 'reasoning-end' as const,\n id: tracker.streamId,\n providerMetadata: r.providerMetadata(),\n });\n }\n case 'tool-input': {\n return stripUndefined({\n type: 'tool-input-available' as const,\n toolCallId: tracker.streamId,\n toolName: r.strOr('toolName', headerReader(tracker.codecHeaders).strOr('toolName', '')),\n input: parseJsonOrString(tracker.accumulated),\n providerMetadata: r.providerMetadata(),\n });\n }\n default: {\n return { type: 'text-end', id: tracker.streamId };\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Lifecycle tracker configuration (synthetic event phases on mid-stream join)\n// ---------------------------------------------------------------------------\n\nconst createVercelLifecycleTracker = (): LifecycleTracker<AI.UIMessageChunk> =>\n createLifecycleTracker<AI.UIMessageChunk>([\n {\n key: 'start',\n build: (ctx) => [stripUndefined({ type: 'start' as const, messageId: ctx.messageId })],\n },\n {\n key: 'start-step',\n build: () => [{ type: 'start-step' as const }],\n },\n ]);\n\n// ---------------------------------------------------------------------------\n// Discrete output decoders (ai-output → UIMessageChunk)\n// ---------------------------------------------------------------------------\n\nconst decodeStart = (\n r: VercelHeaderReader,\n runId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): AI.UIMessageChunk[] => {\n lifecycle.markEmitted(runId, 'start');\n return [\n stripUndefined({\n type: 'start' as const,\n messageId: r.str('messageId'),\n messageMetadata: r.json('messageMetadata'),\n }),\n ];\n};\n\nconst decodeStartStep = (runId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): AI.UIMessageChunk[] => {\n lifecycle.markEmitted(runId, 'start-step');\n return [{ type: 'start-step' }];\n};\n\nconst decodeFinishStep = (runId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): AI.UIMessageChunk[] => {\n lifecycle.resetPhase(runId, 'start-step');\n return [{ type: 'finish-step' }];\n};\n\nconst decodeFinish = (\n r: VercelHeaderReader,\n runId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): AI.UIMessageChunk[] => {\n lifecycle.clearScope(runId);\n return [\n stripUndefined({\n type: 'finish' as const,\n finishReason: parseFinishReason(r.str('finishReason'), 'stop'),\n messageMetadata: r.json('messageMetadata'),\n }),\n ];\n};\n\nconst decodeError = (\n data: unknown,\n runId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): AI.UIMessageChunk[] => {\n lifecycle.clearScope(runId);\n const errorText = typeof data === 'string' ? data : '';\n return [{ type: 'error', errorText }];\n};\n\nconst decodeAbort = (\n data: unknown,\n runId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): AI.UIMessageChunk[] => {\n lifecycle.clearScope(runId);\n const reason = typeof data === 'string' && data ? data : undefined;\n return [stripUndefined({ type: 'abort' as const, reason })];\n};\n\nconst decodeMessageMetadata = (r: VercelHeaderReader): AI.UIMessageChunk[] => [\n { type: 'message-metadata', messageMetadata: r.json('messageMetadata') },\n];\n\nconst decodeFile = (r: VercelHeaderReader, data: unknown): AI.UIMessageChunk[] => [\n stripUndefined({\n type: 'file' as const,\n url: typeof data === 'string' ? data : '',\n mediaType: r.strOr('mediaType', ''),\n providerMetadata: r.providerMetadata(),\n }),\n];\n\nconst decodeSourceUrl = (r: VercelHeaderReader, data: unknown): AI.UIMessageChunk[] => [\n stripUndefined({\n type: 'source-url' as const,\n sourceId: r.strOr('sourceId', ''),\n url: typeof data === 'string' ? data : '',\n title: r.str('title'),\n providerMetadata: r.providerMetadata(),\n }),\n];\n\nconst decodeSourceDocument = (r: VercelHeaderReader): AI.UIMessageChunk[] => [\n stripUndefined({\n type: 'source-document' as const,\n sourceId: r.strOr('sourceId', ''),\n mediaType: r.strOr('mediaType', ''),\n title: r.strOr('title', ''),\n filename: r.str('filename'),\n providerMetadata: r.providerMetadata(),\n }),\n];\n\nconst decodeToolInputError = (r: VercelHeaderReader, data: unknown): AI.UIMessageChunk[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ToolInputErrorWireData | undefined;\n return [\n stripUndefined({\n type: 'tool-input-error' as const,\n toolCallId: r.strOr('toolCallId', ''),\n toolName: r.strOr('toolName', ''),\n errorText: parsed?.errorText ?? '',\n input: parsed?.input,\n dynamic: r.bool('dynamic'),\n title: r.str('title'),\n providerExecuted: r.bool('providerExecuted'),\n providerMetadata: r.providerMetadata(),\n }),\n ];\n};\n\nconst decodeAgentToolOutputAvailable = (r: VercelHeaderReader, data: unknown): AI.UIMessageChunk[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ToolOutputAvailableWireData | undefined;\n return [\n stripUndefined({\n type: 'tool-output-available' as const,\n toolCallId: r.strOr('toolCallId', ''),\n output: parsed?.output,\n dynamic: r.bool('dynamic'),\n providerExecuted: r.bool('providerExecuted'),\n preliminary: r.bool('preliminary'),\n }),\n ];\n};\n\nconst decodeAgentToolOutputError = (r: VercelHeaderReader, data: unknown): AI.UIMessageChunk[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as AgentToolOutputErrorWireData | undefined;\n return [\n stripUndefined({\n type: 'tool-output-error' as const,\n toolCallId: r.strOr('toolCallId', ''),\n errorText: parsed?.errorText ?? '',\n dynamic: r.bool('dynamic'),\n providerExecuted: r.bool('providerExecuted'),\n }),\n ];\n};\n\nconst decodeToolApprovalRequest = (r: VercelHeaderReader): AI.UIMessageChunk[] => [\n {\n type: 'tool-approval-request',\n toolCallId: r.strOr('toolCallId', ''),\n approvalId: r.strOr('approvalId', ''),\n },\n];\n\nconst decodeToolOutputDenied = (r: VercelHeaderReader): AI.UIMessageChunk[] => [\n { type: 'tool-output-denied', toolCallId: r.strOr('toolCallId', '') },\n];\n\nconst decodeDataEvent = (name: `data-${string}`, r: VercelHeaderReader, data: unknown): AI.UIMessageChunk[] => [\n stripUndefined({\n type: name,\n data,\n id: r.str('id'),\n transient: r.bool('transient'),\n }),\n];\n\n// ---------------------------------------------------------------------------\n// Non-streaming tool-input helper (agent-side)\n// ---------------------------------------------------------------------------\n\nconst decodeNonStreamingToolInput = (\n r: VercelHeaderReader,\n data: unknown,\n runId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): AI.UIMessageChunk[] => [\n ...lifecycle.ensurePhases(runId, { messageId: r.str('messageId') }),\n stripUndefined({\n type: 'tool-input-start' as const,\n toolCallId: r.strOr('toolCallId', ''),\n toolName: r.strOr('toolName', ''),\n dynamic: r.bool('dynamic'),\n title: r.str('title'),\n providerExecuted: r.bool('providerExecuted'),\n providerMetadata: r.providerMetadata(),\n }),\n stripUndefined({\n type: 'tool-input-available' as const,\n toolCallId: r.strOr('toolCallId', ''),\n toolName: r.strOr('toolName', ''),\n input: data,\n providerMetadata: r.providerMetadata(),\n }),\n];\n\n// ---------------------------------------------------------------------------\n// Input-side decoders (ai-input → VercelInput)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode a single discrete message part (from the user-message multi-part\n * wire format) into a {@link UserMessage} carrying a one-part\n * UIMessage. The reducer's `_foldUserMessage` merges parts that share\n * the same codec-message-id.\n * @param input - The discrete message payload (name, data, headers).\n * @returns A single `user-message` input, or an empty array when the part type is unrecognised.\n */\nconst decodeDiscreteMessagePart = (input: MessagePayload): VercelInput[] => {\n const r = headerReader(input.codecHeaders ?? {});\n const role = (input.transportHeaders?.[HEADER_ROLE] ?? 'user') as AI.UIMessage['role'];\n const messageId = r.str('messageId') ?? '';\n const codecType = r.strOr('type', '');\n\n let part: AI.UIMessage['parts'][number] | undefined;\n\n switch (codecType) {\n case 'text': {\n part = { type: 'text', text: typeof input.data === 'string' ? input.data : '' };\n break;\n }\n case 'file': {\n part = {\n type: 'file',\n mediaType: r.strOr('mediaType', ''),\n url: typeof input.data === 'string' ? input.data : '',\n };\n break;\n }\n default: {\n if (isDataEventName(codecType)) {\n part = stripUndefined({ type: codecType, id: r.str('id'), data: input.data });\n }\n break;\n }\n }\n\n if (!part) return [];\n\n const message: AI.UIMessage = { id: messageId, role, parts: [part] };\n const userMessage: UserMessage<AI.UIMessage> = { kind: 'user-message', message };\n return [userMessage];\n};\n\nconst isDiscreteMessagePart = (codecType: string, headers: Record<string, string>): boolean =>\n (codecType === 'text' || codecType === 'file' || isDataEventName(codecType)) && HEADER_DISCRETE in headers;\n\nconst decodeClientToolResult = (codecMessageId: string, r: VercelHeaderReader, data: unknown): VercelInput[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ToolOutputAvailableWireData | undefined;\n return [\n {\n kind: 'tool-result',\n codecMessageId,\n payload: { toolCallId: r.strOr('toolCallId', ''), output: parsed?.output },\n },\n ];\n};\n\nconst decodeClientToolResultError = (codecMessageId: string, r: VercelHeaderReader, data: unknown): VercelInput[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ClientToolResultErrorWireData | undefined;\n return [\n {\n kind: 'tool-result-error',\n codecMessageId,\n payload: { toolCallId: r.strOr('toolCallId', ''), message: parsed?.message ?? '' },\n },\n ];\n};\n\nconst decodeClientToolApprovalResponse = (codecMessageId: string, r: VercelHeaderReader): VercelInput[] => [\n {\n kind: 'tool-approval-response',\n codecMessageId,\n payload: stripUndefined({\n toolCallId: r.strOr('toolCallId', ''),\n approved: r.bool('approved') ?? false,\n reason: r.str('reason'),\n }),\n },\n];\n\n// ---------------------------------------------------------------------------\n// Discrete payload dispatch\n// ---------------------------------------------------------------------------\n\nconst decodeAiOutputPayload = (\n codecType: string,\n r: VercelHeaderReader,\n data: unknown,\n runId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): AnyEvent[] => {\n switch (codecType) {\n case 'start': {\n return decodeStart(r, runId, lifecycle);\n }\n case 'start-step': {\n return decodeStartStep(runId, lifecycle);\n }\n case 'finish-step': {\n return decodeFinishStep(runId, lifecycle);\n }\n case 'finish': {\n return decodeFinish(r, runId, lifecycle);\n }\n case 'error': {\n return decodeError(data, runId, lifecycle);\n }\n case 'abort': {\n return decodeAbort(data, runId, lifecycle);\n }\n case 'message-metadata': {\n return decodeMessageMetadata(r);\n }\n case 'file': {\n return decodeFile(r, data);\n }\n case 'source-url': {\n return decodeSourceUrl(r, data);\n }\n case 'source-document': {\n return decodeSourceDocument(r);\n }\n case 'tool-input': {\n return decodeNonStreamingToolInput(r, data, runId, lifecycle);\n }\n case 'tool-input-error': {\n return decodeToolInputError(r, data);\n }\n case 'tool-output-available': {\n return decodeAgentToolOutputAvailable(r, data);\n }\n case 'tool-output-error': {\n return decodeAgentToolOutputError(r, data);\n }\n case 'tool-approval-request': {\n return decodeToolApprovalRequest(r);\n }\n case 'tool-output-denied': {\n return decodeToolOutputDenied(r);\n }\n default: {\n return isDataEventName(codecType) ? decodeDataEvent(codecType, r, data) : [];\n }\n }\n};\n\nconst decodeAiInputPayload = (codecType: string, input: MessagePayload, r: VercelHeaderReader): AnyEvent[] => {\n // Multi-part user-message parts (text / file / data-*) carry discrete\n // because they ride publishDiscreteBatch; the receive-side fans them back\n // out into a UserMessage.\n if (isDiscreteMessagePart(codecType, input.transportHeaders ?? {})) {\n return decodeDiscreteMessagePart(input);\n }\n\n const codecMessageId = input.transportHeaders?.[HEADER_CODEC_MESSAGE_ID] ?? '';\n\n switch (codecType) {\n case 'tool-result': {\n return decodeClientToolResult(codecMessageId, r, input.data);\n }\n case 'tool-result-error': {\n return decodeClientToolResultError(codecMessageId, r, input.data);\n }\n case 'tool-approval-response': {\n return decodeClientToolApprovalResponse(codecMessageId, r);\n }\n case 'regenerate': {\n // Wire-only signal — carries `parent` / `msg-regenerate` on transport\n // headers, no domain payload. The agent's input-event lookup reads\n // transport headers directly from the inbound Ably message; no\n // projection fold is needed here.\n return [];\n }\n default: {\n return [];\n }\n }\n};\n\nconst decodeDiscretePayload = (input: MessagePayload, lifecycle: LifecycleTracker<AI.UIMessageChunk>): AnyEvent[] => {\n const r = headerReader(input.codecHeaders ?? {});\n const runId = input.transportHeaders?.[HEADER_RUN_ID] ?? '';\n const codecType = r.strOr('type', '');\n\n if (input.name === EVENT_AI_INPUT) {\n return decodeAiInputPayload(codecType, input, r);\n }\n\n if (input.name === EVENT_AI_OUTPUT) {\n return decodeAiOutputPayload(codecType, r, input.data, runId, lifecycle);\n }\n\n return [];\n};\n\n// ---------------------------------------------------------------------------\n// Decoder core hooks\n// ---------------------------------------------------------------------------\n\nconst createHooks = (lifecycle: LifecycleTracker<AI.UIMessageChunk>): DecoderCoreHooks<AnyEvent> => ({\n buildStartEvents: (tracker: StreamTrackerState): AnyEvent[] => {\n const runId = tracker.transportHeaders[HEADER_RUN_ID] ?? '';\n const messageId = headerReader(tracker.codecHeaders).str('messageId');\n return [...lifecycle.ensurePhases(runId, { messageId }), buildStartChunk(tracker)];\n },\n\n buildDeltaEvents: (tracker: StreamTrackerState, delta: string): AnyEvent[] => [buildDeltaChunk(tracker, delta)],\n\n buildEndEvents: (tracker: StreamTrackerState, closingHeaders: Record<string, string>): AnyEvent[] => [\n buildEndChunk(tracker, closingHeaders),\n ],\n\n decodeDiscrete: (payload: MessagePayload): AnyEvent[] => decodeDiscretePayload(payload, lifecycle),\n});\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\nconst isInput = (event: AnyEvent): event is VercelInput => 'kind' in event;\n\nclass DefaultUIMessageDecoder implements Decoder<VercelInput, VercelOutput> {\n private readonly _core: DecoderCore<AnyEvent>;\n\n constructor(options: DecoderCoreOptions = {}) {\n this._core = createDecoderCore<AnyEvent>(createHooks(createVercelLifecycleTracker()), options);\n }\n\n decode(message: Ably.InboundMessage): DecodedMessage<VercelInput, VercelOutput> {\n const events = this._core.decode(message);\n const inputs: VercelInput[] = [];\n const outputs: VercelOutput[] = [];\n for (const event of events) {\n if (isInput(event)) {\n inputs.push(event);\n } else {\n outputs.push(event);\n }\n }\n return { inputs, outputs };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Vercel AI SDK decoder that maps Ably messages to {@link DecodedMessage}.\n * @param options - Decoder configuration (callbacks, logger).\n * @returns A {@link Decoder} typed in both directions for the Vercel codec.\n */\nexport const createDecoder = (options: DecoderCoreOptions = {}): Decoder<VercelInput, VercelOutput> =>\n new DefaultUIMessageDecoder(options);\n","import * as Ably from 'ably';\n\n/**\n * Error codes for the AI Transport SDK.\n */\nexport enum ErrorCode {\n /**\n * The request was invalid.\n */\n BadRequest = 40000,\n\n /**\n * Invalid argument provided.\n */\n InvalidArgument = 40003,\n\n /**\n * Operation not permitted with the provided capability (Ably 40160).\n * Used when the Ably channel rejects a publish for a capability reason.\n */\n InsufficientCapability = 40160,\n\n // 104000 - 104999 are reserved for AI Transport SDK errors\n\n /**\n * Encoder recovery failed during flush — one or more updateMessage calls\n * could not recover a failed append pipeline.\n */\n EncoderRecoveryFailed = 104000,\n\n /**\n * A session-level channel subscription callback threw unexpectedly.\n */\n SessionSubscriptionError = 104001,\n\n /**\n * Cancel listener or onCancel hook threw while processing a cancel message.\n */\n CancelListenerError = 104002,\n\n /**\n * A publish within a run failed (lifecycle event, message, or event).\n */\n RunLifecycleError = 104003,\n\n /**\n * An operation was attempted on a session that has already been closed.\n */\n SessionClosed = 104004,\n\n /**\n * The HTTP POST to the agent endpoint failed (network error or non-2xx response).\n */\n SessionSendFailed = 104005,\n\n /**\n * The Ably channel lost message continuity — the channel entered FAILED,\n * SUSPENDED, or DETACHED, or re-attached with `resumed: false`. Active\n * streams can no longer be guaranteed to receive all events.\n */\n ChannelContinuityLost = 104006,\n\n /**\n * An operation was attempted but the channel is not in a usable state\n * (not ATTACHED or ATTACHING).\n */\n ChannelNotReady = 104007,\n\n /**\n * An error occurred while piping a response stream to the channel — either\n * the source event stream threw (e.g. LLM provider rate limit, model error,\n * network failure) or an underlying publish failed mid-stream.\n */\n StreamError = 104008,\n\n /**\n * The agent attached to the channel and waited for the input event(s) the\n * invocation points at (rewind + live wait) but `inputEventLookupTimeoutMs`\n * lapsed without seeing them.\n */\n InputEventNotFound = 104010,\n}\n\n/**\n * Returns true if the {@link Ably.ErrorInfo} code matches the provided ErrorCode value.\n * @param errorInfo The error info to check.\n * @param error The error code to compare against.\n * @returns true if the error code matches, false otherwise.\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison\nexport const errorInfoIs = (errorInfo: Ably.ErrorInfo, error: ErrorCode): boolean => errorInfo.code === error;\n","/**\n * Encoder core — message append lifecycle machinery.\n *\n * Provides Ably primitives (publish, append, close, cancel, flush) that\n * domain-specific encoders wire their event types to.\n *\n * Domain encoders call `createEncoderCore(writer, options)` and use the\n * returned core to map domain events to Ably operations without\n * reimplementing the message append lifecycle.\n */\n\nimport * as Ably from 'ably';\n\nimport {\n HEADER_CODEC_MESSAGE_ID,\n HEADER_DISCRETE,\n HEADER_STATUS,\n HEADER_STREAM,\n HEADER_STREAM_ID,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport { mergeHeaders } from '../../utils.js';\nimport type { ChannelWriter, EncoderOptions, Extras, MessagePayload, StreamPayload, WriteOptions } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for creating an encoder core. Extends {@link EncoderOptions} with a logger. */\nexport interface EncoderCoreOptions extends EncoderOptions {\n /** Logger instance for diagnostic output. */\n logger?: Logger;\n}\n\n// ---------------------------------------------------------------------------\n// Stream tracker (internal)\n// ---------------------------------------------------------------------------\n\ninterface StreamState {\n serial: string;\n name: string;\n streamId: string;\n accumulated: string;\n /** Transport-tier headers repeated on every append (`extras.ai.transport`). */\n persistentTransport: Record<string, string>;\n /** Codec-tier headers repeated on every append (`extras.ai.codec`). */\n persistentCodec: Record<string, string>;\n cancelled: boolean;\n}\n\n/**\n * The SDK's `extras.ai` namespace as written to the wire: a `transport` tier\n * (always present on SDK-published messages) and an optional `codec` tier.\n */\ninterface AiExtras {\n transport: Record<string, string>;\n codec?: Record<string, string>;\n}\n\ninterface PendingAppend {\n promise: Promise<Ably.UpdateDeleteResult>;\n streamId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Encoder core interface\n// ---------------------------------------------------------------------------\n\n/** The core encoder primitives that domain codec encoders delegate to. */\nexport interface EncoderCore {\n /** Publish a single discrete (non-streaming) message described by a payload. */\n publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult>;\n\n /** Publish multiple discrete messages atomically in a single channel publish. */\n publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult>;\n\n /** Start a streamed message with status:streaming. */\n startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void>;\n\n /**\n * Append data to an in-flight streamed message. Fire-and-forget: errors are\n * collected internally and surfaced by {@link closeStream}, {@link cancelStream},\n * {@link cancelAllStreams} or {@link close}.\n * @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId` or the core is closed.\n */\n appendStream(streamId: string, data: string): void;\n\n /**\n * Close a streamed message with status:complete. Flushes all pending\n * appends for recovery before returning. Repeats persistent and payload headers.\n * @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId`, or the encoder has been closed; EncoderRecoveryFailed if a failed append cannot be recovered during the flush.\n */\n closeStream(streamId: string, payload: StreamPayload): Promise<void>;\n\n /**\n * Cancel a single in-progress stream (status:cancelled) and flush all\n * pending appends for recovery before returning.\n */\n cancelStream(streamId: string, opts?: WriteOptions): Promise<void>;\n\n /**\n * Cancel all in-progress streams (status:cancelled) and flush all\n * pending appends for recovery before returning.\n */\n cancelAllStreams(opts?: WriteOptions): Promise<void>;\n\n /** Flush + clear trackers. Idempotent. */\n close(): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD1\nclass DefaultEncoderCore implements EncoderCore {\n private readonly _writer: ChannelWriter;\n private readonly _defaultClientId: string | undefined;\n private readonly _defaultExtras: Extras | undefined;\n private readonly _onMessageHook: (message: Ably.Message) => void;\n private readonly _logger: Logger | undefined;\n private readonly _trackers = new Map<string, StreamState>();\n private _pending: PendingAppend[] = [];\n private _flushPromise: Promise<void> | undefined;\n private _closed = false;\n\n constructor(writer: ChannelWriter, options: EncoderCoreOptions = {}) {\n this._writer = writer;\n this._defaultClientId = options.clientId;\n this._defaultExtras = options.extras;\n this._onMessageHook =\n options.onMessage ??\n (() => {\n /* noop */\n });\n this._logger = options.logger?.withContext({ component: 'EncoderCore' });\n }\n\n // Spec: AIT-CD11\n async publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.publishDiscrete();', { name: payload.name });\n const msg = this._buildDiscreteMessage(payload, opts);\n return this._writer.publish(msg);\n }\n\n // Spec: AIT-CD11a\n async publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.publishDiscreteBatch();', { count: payloads.length });\n const msgs = payloads.map((p) => this._buildDiscreteMessage(p, opts, true));\n return this._writer.publish(msgs);\n }\n\n // Spec: AIT-CD2\n async startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.startStream();', { name: payload.name, streamId });\n\n const transport = this._buildTransport(payload.transportHeaders, opts);\n transport[HEADER_STREAM] = 'true';\n transport[HEADER_STATUS] = 'streaming';\n transport[HEADER_STREAM_ID] = streamId;\n const codec = payload.codecHeaders ?? {};\n\n const clientId = this._resolveClientId(opts);\n const msg: Ably.Message = {\n name: payload.name,\n data: payload.data,\n extras: { ai: this._aiExtras(transport, codec) },\n ...(clientId ? { clientId } : {}),\n };\n\n this._invokeOnMessage(msg);\n const result = await this._writer.publish(msg);\n const serial = result.serials[0];\n\n // Spec: AIT-CD2a\n if (!serial) {\n throw new Ably.ErrorInfo(\n `unable to start stream; no serial returned for stream '${payload.name}' (streamId: ${streamId})`,\n ErrorCode.BadRequest,\n 400,\n );\n }\n\n this._trackers.set(streamId, {\n serial,\n name: payload.name,\n streamId,\n accumulated: payload.data,\n persistentTransport: transport,\n persistentCodec: codec,\n cancelled: false,\n });\n\n this._logger?.debug('DefaultEncoderCore.startStream(); stream started', {\n name: payload.name,\n streamId,\n serial,\n });\n }\n\n // Spec: AIT-CD3\n appendStream(streamId: string, data: string): void {\n this._assertNotClosed();\n // Spec: AIT-CD3a\n const tracker = this._trackers.get(streamId);\n if (!tracker) {\n throw new Ably.ErrorInfo(\n `unable to append to stream; no active stream for streamId '${streamId}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n tracker.accumulated += data;\n\n const appendMsg: Ably.Message = {\n serial: tracker.serial,\n data,\n extras: { ai: this._aiExtras({ ...tracker.persistentTransport }, { ...tracker.persistentCodec }) },\n };\n\n this._invokeOnMessage(appendMsg);\n const p = this._writer.appendMessage(appendMsg);\n this._pending.push({ promise: p, streamId });\n }\n\n // Spec: AIT-CD4, AIT-CD4a\n async closeStream(streamId: string, payload: StreamPayload): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.closeStream();', { streamId });\n\n const tracker = this._trackers.get(streamId);\n if (!tracker) {\n throw new Ably.ErrorInfo(\n `unable to close stream; no active stream for streamId '${streamId}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n // Accumulate closing data so recovery has the full content\n tracker.accumulated += payload.data;\n\n const { transport, codec } = this._buildClosing(tracker, payload);\n transport[HEADER_STATUS] = 'complete';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: payload.data,\n extras: { ai: this._aiExtras(transport, codec) },\n };\n\n this._invokeOnMessage(msg);\n const p = this._writer.appendMessage(msg);\n this._pending.push({ promise: p, streamId });\n\n await this._flushPending();\n\n this._logger?.debug('DefaultEncoderCore.closeStream(); stream closed', { streamId });\n }\n\n // Spec: AIT-CD5, AIT-CD5b\n async cancelStream(streamId: string, opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.cancelStream();', { streamId });\n\n const tracker = this._trackers.get(streamId);\n if (!tracker) {\n throw new Ably.ErrorInfo(\n `unable to cancel stream; no active stream for streamId '${streamId}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n tracker.cancelled = true;\n\n const { transport, codec } = this._buildClosing(tracker, undefined, opts);\n transport[HEADER_STATUS] = 'cancelled';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: '',\n extras: { ai: this._aiExtras(transport, codec) },\n };\n\n this._invokeOnMessage(msg);\n const p = this._writer.appendMessage(msg);\n this._pending.push({ promise: p, streamId });\n\n await this._flushPending();\n\n this._logger?.debug('DefaultEncoderCore.cancelStream(); stream cancelled', { streamId });\n }\n\n // Spec: AIT-CD5a\n async cancelAllStreams(opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.cancelAllStreams();', { streamCount: this._trackers.size });\n\n for (const tracker of this._trackers.values()) {\n tracker.cancelled = true;\n\n const { transport, codec } = this._buildClosing(tracker, undefined, opts);\n transport[HEADER_STATUS] = 'cancelled';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: '',\n extras: { ai: this._aiExtras(transport, codec) },\n };\n\n this._invokeOnMessage(msg);\n const p = this._writer.appendMessage(msg);\n this._pending.push({ promise: p, streamId: tracker.streamId });\n }\n\n await this._flushPending();\n }\n\n // Spec: AIT-CD6\n private async _flushPending(): Promise<void> {\n // Re-entrancy guard: if a flush is already in progress, await it instead of starting a new one.\n if (this._flushPromise) {\n return this._flushPromise;\n }\n\n const snapshot = this._pending;\n this._pending = [];\n\n if (snapshot.length === 0) return;\n\n this._logger?.trace('DefaultEncoderCore._flushPending();', { count: snapshot.length });\n\n this._flushPromise = this._doFlush(snapshot);\n try {\n await this._flushPromise;\n } finally {\n this._flushPromise = undefined;\n }\n }\n\n private async _doFlush(snapshot: PendingAppend[]): Promise<void> {\n const results = await Promise.allSettled(snapshot.map(async (p) => p.promise));\n const failures = new Set<string>();\n\n for (const [i, result] of results.entries()) {\n const entry = snapshot[i];\n if (entry && result.status === 'rejected') {\n failures.add(entry.streamId);\n }\n }\n\n if (failures.size === 0) {\n this._logger?.debug('DefaultEncoderCore._flushPending(); all appends succeeded');\n return;\n }\n\n this._logger?.warn('DefaultEncoderCore._flushPending(); recovering failed appends', {\n failedStreams: [...failures],\n });\n\n const recoveryErrors: { streamId: string; error: unknown }[] = [];\n\n for (const streamId of failures) {\n const tracker = this._trackers.get(streamId);\n if (!tracker) continue;\n\n const recoveryStatus = tracker.cancelled ? 'cancelled' : 'complete';\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: tracker.accumulated,\n extras: {\n ai: this._aiExtras(\n { ...tracker.persistentTransport, [HEADER_STATUS]: recoveryStatus },\n { ...tracker.persistentCodec },\n ),\n },\n };\n\n try {\n await this._writer.updateMessage(msg);\n } catch (error) {\n recoveryErrors.push({ streamId, error });\n }\n }\n\n if (recoveryErrors.length > 0) {\n const ids = recoveryErrors.map((e) => e.streamId).join(', ');\n this._logger?.error('DefaultEncoderCore._flushPending(); recovery failed', { failedStreams: ids });\n throw new Ably.ErrorInfo(\n `unable to flush pending appends; recovery failed for stream(s): ${ids}`,\n ErrorCode.EncoderRecoveryFailed,\n 500,\n );\n }\n }\n\n // Spec: AIT-CD12\n async close(): Promise<void> {\n if (this._closed) return;\n this._logger?.trace('DefaultEncoderCore.close();');\n this._closed = true;\n try {\n await this._flushPending();\n } finally {\n this._trackers.clear();\n }\n this._logger?.debug('DefaultEncoderCore.close(); encoder closed');\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD14\n private _invokeOnMessage(msg: Ably.Message): void {\n try {\n this._onMessageHook(msg);\n } catch (error) {\n this._logger?.error('DefaultEncoderCore._invokeOnMessage(); hook threw', { error });\n }\n }\n\n private _assertNotClosed(): void {\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to write to encoder; encoder has been closed', ErrorCode.InvalidArgument, 400);\n }\n }\n\n private _resolveClientId(opts?: WriteOptions): string | undefined {\n return opts?.clientId ?? this._defaultClientId;\n }\n\n /**\n * Build the transport-tier header record for a message: caller-configured\n * transport headers (default extras + per-write overrides) layered with any\n * transport headers the codec payload stamps directly, plus the message-id.\n * @param payloadTransport - Transport headers carried on the codec payload.\n * @param opts - Optional per-write overrides.\n * @returns The transport-tier headers record (`extras.ai.transport`).\n */\n private _buildTransport(\n payloadTransport: Record<string, string> | undefined,\n opts?: WriteOptions,\n ): Record<string, string> {\n const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);\n const transport = { ...callerHeaders, ...payloadTransport };\n if (opts?.messageId !== undefined) {\n transport[HEADER_CODEC_MESSAGE_ID] = opts.messageId;\n }\n return transport;\n }\n\n /**\n * Assemble the `extras.ai` namespace from its two tiers, omitting the codec\n * tier when empty.\n * @param transport - Transport-tier headers (always present on SDK messages).\n * @param codec - Codec-tier headers; omitted from the wire when empty.\n * @returns The `extras.ai` object.\n */\n private _aiExtras(transport: Record<string, string>, codec: Record<string, string>): AiExtras {\n return Object.keys(codec).length > 0 ? { transport, codec } : { transport };\n }\n\n private _buildDiscreteMessage(payload: MessagePayload, opts?: WriteOptions, discrete = false): Ably.Message {\n const transport = this._buildTransport(payload.transportHeaders, opts);\n transport[HEADER_STREAM] = 'false';\n if (discrete) {\n // Mark batch-published payloads as discrete message parts (from writeMessages).\n // The decoder relies on this header to distinguish message parts from lifecycle\n // events that also happen to be discrete (stream: false).\n transport[HEADER_DISCRETE] = 'true';\n }\n const clientId = this._resolveClientId(opts);\n\n const msg: Ably.Message = {\n name: payload.name,\n data: payload.data,\n extras: {\n ai: this._aiExtras(transport, payload.codecHeaders ?? {}),\n ...(payload.ephemeral ? { ephemeral: true } : {}),\n },\n ...(clientId ? { clientId } : {}),\n };\n\n this._invokeOnMessage(msg);\n return msg;\n }\n\n /**\n * Build both header tiers for a closing append. Closing appends must repeat\n * ALL persistent headers (Ably replaces the entire extras object on append).\n * Then layer caller and codec overrides.\n * @param tracker - The stream tracker with persistent headers.\n * @param payload - The closing stream payload (codec + transport headers).\n * @param opts - Optional per-write overrides.\n * @returns The two tiers for the closing append.\n */\n private _buildClosing(\n tracker: StreamState,\n payload: StreamPayload | undefined,\n opts?: WriteOptions,\n ): { transport: Record<string, string>; codec: Record<string, string> } {\n const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);\n const transport = { ...tracker.persistentTransport, ...callerHeaders, ...payload?.transportHeaders };\n const codec = { ...tracker.persistentCodec, ...payload?.codecHeaders };\n return { transport, codec };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an encoder core bound to the given channel writer.\n * @param writer - The channel writer to publish messages through.\n * @param options - Encoder configuration (clientId, extras, hooks, logger).\n * @returns A new {@link EncoderCore} instance.\n */\nexport const createEncoderCore = (writer: ChannelWriter, options: EncoderCoreOptions = {}): EncoderCore =>\n new DefaultEncoderCore(writer, options);\n","/**\n * Vercel AI SDK encoder.\n *\n * Two publish methods enforce direction at the call site:\n *\n * - {@link DefaultUIMessageEncoder.publishInput} encodes a `VercelInput`\n * variant and publishes it on the `ai-input` wire.\n * - {@link DefaultUIMessageEncoder.publishOutput} encodes a `VercelOutput`\n * (`AI.UIMessageChunk`) and publishes it on the `ai-output` wire,\n * driving the underlying stream-tracker for streamed chunks\n * (text / reasoning / tool-input) and falling back to discrete\n * publishes for everything else.\n *\n * The codec event's own discriminator (`kind` for inputs, `type` for\n * outputs) is carried in the codec tier's `type` header so the\n * decoder can dispatch. Stream-tracker state lives inside the encoder\n * core; only the output direction (text / reasoning / tool-input chunks)\n * drives it — inputs are always published as discrete messages.\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\nimport { isDataUIPart } from 'ai';\n\nimport { EVENT_AI_INPUT, EVENT_AI_OUTPUT, HEADER_ROLE, HEADER_STATUS } from '../../constants.js';\nimport type { EncoderCore, EncoderCoreOptions } from '../../core/codec/encoder.js';\nimport { createEncoderCore } from '../../core/codec/encoder.js';\nimport type {\n ChannelWriter,\n Encoder,\n MessagePayload,\n ToolApprovalResponse,\n ToolResult,\n ToolResultError,\n UserMessage,\n WriteOptions,\n} from '../../core/codec/types.js';\nimport { ErrorCode, errorInfoIs } from '../../errors.js';\nimport { headerWriter } from '../../utils.js';\nimport type {\n VercelInput,\n VercelOutput,\n VercelToolApprovalResponsePayload,\n VercelToolResultErrorPayload,\n VercelToolResultPayload,\n} from './events.js';\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\nclass DefaultUIMessageEncoder implements Encoder<VercelInput, VercelOutput> {\n private readonly _core: EncoderCore;\n private readonly _messageId: string | undefined;\n private _cancelled = false;\n\n constructor(writer: ChannelWriter, options: EncoderCoreOptions = {}) {\n this._core = createEncoderCore(writer, options);\n this._messageId = options.messageId;\n }\n\n async publishInput(input: VercelInput, options?: WriteOptions): Promise<void> {\n switch (input.kind) {\n case 'user-message': {\n await this._publishUserMessage(input, options);\n return;\n }\n case 'regenerate': {\n await this._publishRegenerate(options);\n return;\n }\n case 'tool-result': {\n await this._publishToolResult(input, options);\n return;\n }\n case 'tool-result-error': {\n await this._publishToolResultError(input, options);\n return;\n }\n case 'tool-approval-response': {\n await this._publishToolApprovalResponse(input, options);\n return;\n }\n }\n }\n\n async publishOutput(output: VercelOutput, options?: WriteOptions): Promise<void> {\n await this._publishChunk(output, options);\n }\n\n async cancel(reason?: string): Promise<void> {\n if (this._cancelled) return;\n this._cancelled = true;\n await this._core.cancelAllStreams();\n await this._core.publishDiscrete({\n name: EVENT_AI_OUTPUT,\n data: reason ?? '',\n codecHeaders: headerWriter().str('type', 'abort').build(),\n transportHeaders: { [HEADER_STATUS]: 'cancelled' },\n });\n }\n\n async close(): Promise<void> {\n await this._core.close();\n }\n\n // -------------------------------------------------------------------------\n // VercelOutput routing — UIMessageChunk\n // -------------------------------------------------------------------------\n\n private async _publishChunk(chunk: AI.UIMessageChunk, perWrite?: WriteOptions): Promise<void> {\n switch (chunk.type) {\n // -- Stream start -----------------------------------------------------\n case 'text-start': {\n const h = headerWriter()\n .str('type', 'text')\n .str('id', chunk.id)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.startStream(chunk.id, { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n case 'reasoning-start': {\n const h = headerWriter()\n .str('type', 'reasoning')\n .str('id', chunk.id)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.startStream(chunk.id, { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n case 'tool-input-start': {\n const h = headerWriter()\n .str('type', 'tool-input')\n .str('toolCallId', chunk.toolCallId)\n .str('toolName', chunk.toolName)\n .bool('dynamic', chunk.dynamic)\n .str('title', chunk.title)\n .bool('providerExecuted', chunk.providerExecuted)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.startStream(chunk.toolCallId, { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n\n // -- Stream append ----------------------------------------------------\n case 'text-delta': {\n this._core.appendStream(chunk.id, chunk.delta);\n return;\n }\n case 'reasoning-delta': {\n this._core.appendStream(chunk.id, chunk.delta);\n return;\n }\n case 'tool-input-delta': {\n this._core.appendStream(chunk.toolCallId, chunk.inputTextDelta);\n return;\n }\n\n // -- Stream close -----------------------------------------------------\n case 'text-end': {\n const h = headerWriter()\n .str('type', 'text')\n .str('id', chunk.id)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.closeStream(chunk.id, { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h });\n return;\n }\n case 'reasoning-end': {\n const h = headerWriter()\n .str('type', 'reasoning')\n .str('id', chunk.id)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.closeStream(chunk.id, { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h });\n return;\n }\n case 'tool-input-available': {\n try {\n const h = headerWriter()\n .str('type', 'tool-input')\n .str('toolCallId', chunk.toolCallId)\n .str('toolName', chunk.toolName)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.closeStream(chunk.toolCallId, { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h });\n } catch (error: unknown) {\n // closeStream raises ErrorCode.InvalidArgument when there is no active stream for this id; fall through to a discrete publish in that case and rethrow any other error.\n if (!(error instanceof Ably.ErrorInfo && errorInfoIs(error, ErrorCode.InvalidArgument))) {\n throw error;\n }\n const h = headerWriter()\n .str('type', 'tool-input')\n .str('toolCallId', chunk.toolCallId)\n .str('toolName', chunk.toolName)\n .bool('dynamic', chunk.dynamic)\n .str('title', chunk.title)\n .bool('providerExecuted', chunk.providerExecuted)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: chunk.input, codecHeaders: h });\n }\n return;\n }\n\n // -- Lifecycle (discrete) ---------------------------------------------\n case 'start': {\n const h = headerWriter()\n .str('type', 'start')\n .str('messageId', chunk.messageId ?? this._messageId)\n .json('messageMetadata', chunk.messageMetadata)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n case 'start-step': {\n const h = headerWriter().str('type', 'start-step').build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n case 'finish-step': {\n const h = headerWriter().str('type', 'finish-step').build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n case 'finish': {\n const h = headerWriter()\n .str('type', 'finish')\n .str('finishReason', chunk.finishReason)\n .json('messageMetadata', chunk.messageMetadata)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n case 'error': {\n const h = headerWriter().str('type', 'error').build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: chunk.errorText, codecHeaders: h }, perWrite);\n return;\n }\n case 'abort': {\n this._cancelled = true;\n await this._core.cancelAllStreams(perWrite);\n await this._core.publishDiscrete(\n {\n name: EVENT_AI_OUTPUT,\n data: chunk.reason ?? '',\n codecHeaders: headerWriter().str('type', 'abort').build(),\n transportHeaders: { [HEADER_STATUS]: 'cancelled' },\n },\n perWrite,\n );\n return;\n }\n case 'message-metadata': {\n const h = headerWriter().str('type', 'message-metadata').json('messageMetadata', chunk.messageMetadata).build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n\n // -- Tool lifecycle (discrete) ----------------------------------------\n case 'tool-input-error': {\n const h = headerWriter()\n .str('type', 'tool-input-error')\n .str('toolCallId', chunk.toolCallId)\n .str('toolName', chunk.toolName)\n .bool('dynamic', chunk.dynamic)\n .str('title', chunk.title)\n .bool('providerExecuted', chunk.providerExecuted)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete(\n { name: EVENT_AI_OUTPUT, data: { errorText: chunk.errorText, input: chunk.input }, codecHeaders: h },\n perWrite,\n );\n return;\n }\n case 'tool-output-available':\n case 'tool-output-error':\n case 'tool-approval-request':\n case 'tool-output-denied': {\n await this._core.publishDiscrete(buildToolOutputPayload(chunk), perWrite);\n return;\n }\n\n // -- Content parts (discrete) -----------------------------------------\n case 'file': {\n const h = headerWriter()\n .str('type', 'file')\n .str('mediaType', chunk.mediaType)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: chunk.url, codecHeaders: h }, perWrite);\n return;\n }\n case 'source-url': {\n const h = headerWriter()\n .str('type', 'source-url')\n .str('sourceId', chunk.sourceId)\n .str('title', chunk.title)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: chunk.url, codecHeaders: h }, perWrite);\n return;\n }\n case 'source-document': {\n const h = headerWriter()\n .str('type', 'source-document')\n .str('sourceId', chunk.sourceId)\n .str('mediaType', chunk.mediaType)\n .str('title', chunk.title)\n .str('filename', chunk.filename)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_OUTPUT, data: '', codecHeaders: h }, perWrite);\n return;\n }\n\n // -- data-* (discrete) ------------------------------------------------\n default: {\n if (chunk.type.startsWith('data-')) {\n // CAST: data-* chunks always have id, transient, and data fields per AI SDK types.\n // TypeScript can't narrow the template literal union in a default case.\n const dataChunk = chunk;\n const h = headerWriter()\n .str('type', dataChunk.type)\n .str('id', dataChunk.id)\n .bool('transient', dataChunk.transient)\n .build();\n const ephemeral = dataChunk.transient === true;\n await this._core.publishDiscrete(\n { name: EVENT_AI_OUTPUT, data: dataChunk.data, codecHeaders: h, ephemeral },\n perWrite,\n );\n return;\n }\n throw new Ably.ErrorInfo(\n `unable to publish output; unsupported chunk type '${chunk.type}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // VercelInput routing\n // -------------------------------------------------------------------------\n\n /**\n * Publish a user-message input as a batch of per-part discrete Ably\n * messages on the `ai-input` wire. Wire format matches the multi-part\n * user-message convention; the receive-side decoder fans the parts back\n * out into a single `UserMessage`.\n * @param input - The user-message input carrying the UIMessage to encode.\n * @param perWrite - Optional per-write overrides.\n */\n private async _publishUserMessage(input: UserMessage<AI.UIMessage>, perWrite?: WriteOptions): Promise<void> {\n const payloads = encodeMessagePayloads(input.message);\n // Stamp role (a transport header) on every payload so the decoder can\n // reconstruct a `role: 'user'` UIMessage.\n for (const payload of payloads) {\n payload.transportHeaders = { ...payload.transportHeaders, [HEADER_ROLE]: 'user' };\n }\n await this._core.publishDiscreteBatch(payloads, perWrite);\n }\n\n /**\n * Publish a regenerate input as a discrete `ai-input` Ably message\n * carrying codec `type: 'regenerate'`. The wire carries no domain\n * payload — `parent` / `target` are stamped on the transport headers by\n * the client-session (it reads them off the input directly and builds\n * `buildTransportHeaders`).\n * @param perWrite - Per-write overrides carrying the transport headers built by client-session.\n */\n private async _publishRegenerate(perWrite?: WriteOptions): Promise<void> {\n const h = headerWriter().str('type', 'regenerate').build();\n await this._core.publishDiscrete({ name: EVENT_AI_INPUT, data: '', codecHeaders: h }, perWrite);\n }\n\n /**\n * Publish a client-side tool output on the `ai-input` wire. Targets the\n * assistant addressed by `input.codecMessageId`; the wire's\n * `codec-message-id` is stamped via `perWrite.messageId` by the\n * client-session.\n * @param input - The tool-output input.\n * @param perWrite - Per-write overrides carrying the wire codecMessageId.\n */\n private async _publishToolResult(input: ToolResult<VercelToolResultPayload>, perWrite?: WriteOptions): Promise<void> {\n const h = headerWriter().str('type', 'tool-result').str('toolCallId', input.payload.toolCallId).build();\n await this._core.publishDiscrete(\n { name: EVENT_AI_INPUT, data: { output: input.payload.output }, codecHeaders: h },\n perWrite,\n );\n }\n\n /**\n * Publish a client-side tool error on the `ai-input` wire. Targets the\n * assistant addressed by `input.codecMessageId`.\n * @param input - The tool-result-error input.\n * @param perWrite - Per-write overrides.\n */\n private async _publishToolResultError(\n input: ToolResultError<VercelToolResultErrorPayload>,\n perWrite?: WriteOptions,\n ): Promise<void> {\n const h = headerWriter().str('type', 'tool-result-error').str('toolCallId', input.payload.toolCallId).build();\n await this._core.publishDiscrete(\n { name: EVENT_AI_INPUT, data: { message: input.payload.message }, codecHeaders: h },\n perWrite,\n );\n }\n\n /**\n * Publish a client-side tool approval response on the `ai-input` wire.\n * Targets the assistant addressed by `input.codecMessageId`.\n * @param input - The approval-response input.\n * @param perWrite - Per-write overrides.\n */\n private async _publishToolApprovalResponse(\n input: ToolApprovalResponse<VercelToolApprovalResponsePayload>,\n perWrite?: WriteOptions,\n ): Promise<void> {\n const h = headerWriter()\n .str('type', 'tool-approval-response')\n .str('toolCallId', input.payload.toolCallId)\n .bool('approved', input.payload.approved)\n .str('reason', input.payload.reason)\n .build();\n await this._core.publishDiscrete({ name: EVENT_AI_INPUT, data: '', codecHeaders: h }, perWrite);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool output discrete payload builder (agent-side `ai-output` wire)\n// ---------------------------------------------------------------------------\n\nconst buildToolOutputPayload = (\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'tool-output-available' | 'tool-output-error' | 'tool-approval-request' | 'tool-output-denied' }\n >,\n): MessagePayload => {\n switch (chunk.type) {\n case 'tool-output-available': {\n const h = headerWriter()\n .str('type', 'tool-output-available')\n .str('toolCallId', chunk.toolCallId)\n .bool('dynamic', chunk.dynamic)\n .bool('providerExecuted', chunk.providerExecuted)\n .bool('preliminary', chunk.preliminary)\n .build();\n return { name: EVENT_AI_OUTPUT, data: { output: chunk.output }, codecHeaders: h };\n }\n case 'tool-output-error': {\n const h = headerWriter()\n .str('type', 'tool-output-error')\n .str('toolCallId', chunk.toolCallId)\n .bool('dynamic', chunk.dynamic)\n .bool('providerExecuted', chunk.providerExecuted)\n .build();\n return { name: EVENT_AI_OUTPUT, data: { errorText: chunk.errorText }, codecHeaders: h };\n }\n case 'tool-approval-request': {\n const h = headerWriter()\n .str('type', 'tool-approval-request')\n .str('toolCallId', chunk.toolCallId)\n .str('approvalId', chunk.approvalId)\n .build();\n return { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h };\n }\n case 'tool-output-denied': {\n const h = headerWriter().str('type', 'tool-output-denied').str('toolCallId', chunk.toolCallId).build();\n return { name: EVENT_AI_OUTPUT, data: '', codecHeaders: h };\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// User-message per-part payload encoding\n// ---------------------------------------------------------------------------\n\nconst encodeMessagePayloads = (message: AI.UIMessage): MessagePayload[] => {\n const messageId = message.id;\n const payloads: MessagePayload[] = [];\n\n for (const part of message.parts) {\n switch (part.type) {\n case 'text': {\n payloads.push({\n name: EVENT_AI_INPUT,\n data: part.text,\n codecHeaders: headerWriter().str('type', 'text').str('messageId', messageId).build(),\n });\n break;\n }\n case 'file': {\n payloads.push({\n name: EVENT_AI_INPUT,\n data: part.url,\n codecHeaders: headerWriter()\n .str('type', 'file')\n .str('messageId', messageId)\n .str('mediaType', part.mediaType)\n .build(),\n });\n break;\n }\n default: {\n if (isDataUIPart(part)) {\n payloads.push({\n name: EVENT_AI_INPUT,\n data: part.data,\n codecHeaders: headerWriter().str('type', part.type).str('messageId', messageId).str('id', part.id).build(),\n });\n }\n break;\n }\n }\n }\n\n if (payloads.length === 0) {\n // Always emit at least one part so the decoder can reconstruct the codec-message-id and role from headers, even when the user-message carried no encodable parts.\n payloads.push({\n name: EVENT_AI_INPUT,\n data: '',\n codecHeaders: headerWriter().str('type', 'text').str('messageId', messageId).build(),\n });\n }\n\n return payloads;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Vercel AI SDK encoder that maps VercelInput / VercelOutput to\n * Ably channel operations via the encoder core.\n * @param writer - The channel writer to publish messages through.\n * @param options - Encoder configuration (clientId, extras, hooks, logger).\n * @returns An {@link Encoder} typed in both directions for the Vercel codec.\n */\nexport const createEncoder = (\n writer: ChannelWriter,\n options: EncoderCoreOptions = {},\n): Encoder<VercelInput, VercelOutput> => new DefaultUIMessageEncoder(writer, options);\n","/**\n * Shared tool part transition logic for the Vercel AI SDK codec.\n *\n * Keeps the tool output state transition logic in one place, reusable by the\n * Vercel codec reducer and any other callers.\n */\n\nimport type * as AI from 'ai';\n\nimport { stripUndefined } from '../../utils.js';\n\n// ---------------------------------------------------------------------------\n// Tool output chunk type guard\n// ---------------------------------------------------------------------------\n\n/** The set of UIMessageChunk types that represent tool output transitions. */\nexport type ToolOutputChunk = Extract<\n AI.UIMessageChunk,\n { type: 'tool-output-available' | 'tool-output-error' | 'tool-output-denied' | 'tool-approval-request' }\n>;\n\n/**\n * Whether a UIMessageChunk is a tool output transition event.\n * @param chunk - The chunk to test.\n * @returns True if the chunk is a tool output transition type.\n */\nexport const isToolOutputChunk = (chunk: AI.UIMessageChunk): chunk is ToolOutputChunk =>\n chunk.type === 'tool-output-available' ||\n chunk.type === 'tool-output-error' ||\n chunk.type === 'tool-output-denied' ||\n chunk.type === 'tool-approval-request';\n\n// ---------------------------------------------------------------------------\n// Tool base helper\n// ---------------------------------------------------------------------------\n\n/** Fields shared by all DynamicToolUIPart state variants. */\ninterface ToolBaseFields {\n type: 'dynamic-tool';\n toolName: string;\n toolCallId: string;\n title?: string;\n providerExecuted?: boolean;\n}\n\n/**\n * Extract the state-independent base fields for a DynamicToolUIPart.\n * Works with both chunks (tool-input-start, etc.) and existing parts.\n * @param source - Any object containing the required tool identity fields.\n * @param source.toolCallId - The tool call identifier.\n * @param source.toolName - The tool name.\n * @param source.title - Optional display title.\n * @param source.providerExecuted - Whether the provider executed the tool.\n * @returns Base fields shared across all DynamicToolUIPart state variants.\n */\nexport const toolBase = (source: {\n toolCallId: string;\n toolName: string;\n title?: string;\n providerExecuted?: boolean;\n}): ToolBaseFields =>\n stripUndefined({\n type: 'dynamic-tool' as const,\n toolCallId: source.toolCallId,\n toolName: source.toolName,\n title: source.title,\n providerExecuted: source.providerExecuted,\n });\n\n// ---------------------------------------------------------------------------\n// Tool part transition\n// ---------------------------------------------------------------------------\n\n/**\n * Transition a DynamicToolUIPart to a new state based on a tool output chunk.\n * Pure function — does not mutate the input part.\n * @param part - The existing tool part to transition.\n * @param chunk - The tool output chunk describing the transition.\n * @returns A new DynamicToolUIPart in the target state.\n */\nexport const transitionToolPart = (part: AI.DynamicToolUIPart, chunk: ToolOutputChunk): AI.DynamicToolUIPart => {\n const base = toolBase(part);\n\n switch (chunk.type) {\n case 'tool-output-available': {\n return stripUndefined({\n ...base,\n state: 'output-available' as const,\n input: part.input,\n output: chunk.output,\n preliminary: chunk.preliminary,\n });\n }\n\n case 'tool-output-error': {\n return {\n ...base,\n state: 'output-error',\n input: part.input,\n errorText: chunk.errorText,\n };\n }\n\n case 'tool-output-denied': {\n return {\n ...base,\n state: 'output-denied',\n input: part.input,\n approval: { id: '', approved: false },\n };\n }\n\n case 'tool-approval-request': {\n return {\n ...base,\n state: 'approval-requested',\n input: part.input,\n approval: { id: chunk.approvalId },\n };\n }\n }\n};\n","/**\n * Vercel AI SDK reducer.\n *\n * Pure `(init, fold)` over the `VercelInput | VercelOutput` union. Folds\n * input variants (user-message, tool-result, tool-result-error,\n * tool-approval-response) and `UIMessageChunk` outputs into a\n * VercelProjection holding `UIMessage[]` plus internal stream-tracker\n * state.\n *\n * The reducer is stateless: every fold is `(state, event, meta) → state'`,\n * with no instance state. Mutation in place is allowed — the projection\n * is single-owner.\n *\n * Idempotency is **per conflict key**, not stream-wide: when two events\n * compete for the same logical state (e.g. two `tool-output-available`\n * for the same `toolCallId`), the higher-serial one wins and the other\n * is dropped. Unrelated events arrive freely in any order. See\n * `_conflictKeyOf` for the per-variant key derivation.\n *\n * Client-published tool resolutions (`ToolResult`, `ToolResultError`,\n * `ToolApprovalResponse`) carry `codecMessageId` targeting the assistant\n * they amend; the reducer applies the resolution onto that assistant's\n * `dynamic-tool` part directly. If the assistant has not yet arrived in\n * the projection (out-of-order delivery), the resolution is buffered in\n * `pendingToolResolutions` and re-evaluated on each subsequent fold.\n */\n\nimport type * as AI from 'ai';\n\nimport type {\n CodecMessage,\n ReducerMeta,\n ToolApprovalResponse,\n ToolResult,\n ToolResultError,\n} from '../../core/codec/types.js';\nimport { stripUndefined } from '../../utils.js';\nimport type {\n VercelInput,\n VercelOutput,\n VercelToolApprovalResponsePayload,\n VercelToolResultErrorPayload,\n VercelToolResultPayload,\n} from './events.js';\nimport { toolBase, transitionToolPart } from './tool-transitions.js';\n\n// ---------------------------------------------------------------------------\n// Internal tracker state\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks an in-progress tool part within a UIMessage. Text and reasoning\n * parts don't need this — we write to them directly via partIndex. Tool\n * parts need an extra `inputText` buffer because deltas arrive as raw\n * JSON fragments that must be accumulated before parsing.\n */\ninterface ToolPartTracker {\n /** Index in the message's parts array. */\n partIndex: number;\n /** Accumulated streaming input text (for JSON parsing on completion). */\n inputText: string;\n}\n\n/** Per-codecMessageId tracking state for in-progress streams within a UIMessage. */\ninterface MessageTrackers {\n /** Text stream id → partIndex. */\n text: Map<string, number>;\n /** Reasoning stream id → partIndex. */\n reasoning: Map<string, number>;\n /** Tool call id → tracker. */\n tools: Map<string, ToolPartTracker>;\n}\n\n// ---------------------------------------------------------------------------\n// Projection\n// ---------------------------------------------------------------------------\n\n/**\n * The per-Run state produced by the Vercel codec's reducer.\n *\n * The SDK reads only `messages` (via `Codec.getMessages`). The remaining\n * fields are internal to the reducer; they happen to live on the\n * projection because the projection is the only thing the reducer can\n * carry from fold to fold (it has no instance state).\n */\nexport interface VercelProjection {\n /**\n * UIMessages produced or modified in this Run, in publication order,\n * each paired with its codec-message-id. The reducer correlates strictly\n * on `codecMessageId`; `message.id` is preserved verbatim from the source\n * (the AI SDK stream's `start.messageId` for assistants, the caller's id\n * for user messages) and is never used as an identity key.\n */\n messages: CodecMessage<AI.UIMessage>[];\n /**\n * Per-conflict-key high-water-marks. Maps a codec-derived conflict key\n * (see `_conflictKeyOf`) to the highest `meta.serial` already folded for\n * that key. Events whose serial is `<=` the stored value are dropped as\n * duplicates of an already-incorporated operation. Events that have no\n * conflict key (additive content, lifecycle markers) are folded\n * unconditionally.\n */\n conflictSerials: Map<string, string>;\n /** Per-codecMessageId tracker state for streamed parts. Internal — do not access. */\n trackers: Map<string, MessageTrackers>;\n /**\n * Tool-resolution events that arrived before any assistant in this\n * projection had a matching `toolCallId`. Re-evaluated on every\n * subsequent fold so that an out-of-order tool output is folded as\n * soon as the corresponding assistant lands.\n */\n pendingToolResolutions: PendingToolResolution[];\n}\n\n/**\n * A buffered tool resolution waiting for its assistant message to arrive.\n * The reducer scans pending entries after every successful fold so an\n * out-of-order tool output is promoted as soon as the matching assistant\n * is added to the projection.\n */\ninterface PendingToolResolution {\n /** The codec-message-id of the assistant the resolution targets. */\n targetCodecMessageId: string;\n /** Tool call this resolution targets. */\n toolCallId: string;\n /** Serial of the wire message — used by the conflict-key check on promotion. */\n serial: string;\n /** Variant of the tool-resolution used to transition the assistant's tool part. */\n resolution:\n | { kind: 'tool-result'; output: unknown }\n | { kind: 'tool-result-error'; message: string }\n | { kind: 'tool-approval-response'; approved: boolean; reason?: string };\n}\n\n// ---------------------------------------------------------------------------\n// init\n// ---------------------------------------------------------------------------\n\n/**\n * Build an empty initial projection.\n * @returns A fresh VercelProjection with no messages and no tracker state.\n */\nexport const init = (): VercelProjection => ({\n messages: [],\n conflictSerials: new Map(),\n trackers: new Map(),\n pendingToolResolutions: [],\n});\n\n// ---------------------------------------------------------------------------\n// fold\n// ---------------------------------------------------------------------------\n\n/**\n * Fold one input or output event into the projection. Mutates and returns\n * `state`.\n *\n * Idempotency is per conflict key (see `_conflictKeyOf`): if the event has\n * a conflict key and the projection has already folded an event for that\n * key at a higher-or-equal serial, this call is a no-op. Events without a\n * conflict key (additive content, lifecycle markers) are folded\n * unconditionally. Orphan events (e.g. tool-output for an unknown\n * toolCallId) are dropped silently inside the per-variant fold helpers.\n * @param state - Projection to fold into (may be mutated in place).\n * @param event - Input or output event to fold.\n * @param meta - Transport-derived metadata (serial, optional messageId).\n * @returns The same projection reference, possibly mutated.\n */\nexport const fold = (\n state: VercelProjection,\n event: VercelInput | VercelOutput,\n meta: ReducerMeta,\n): VercelProjection => {\n if (meta.serial) {\n const key = _conflictKeyOf(event, meta);\n if (key !== undefined) {\n const seen = state.conflictSerials.get(key);\n if (seen !== undefined && meta.serial <= seen) {\n return state;\n }\n state.conflictSerials.set(key, meta.serial);\n }\n }\n\n if (_isInput(event)) {\n switch (event.kind) {\n case 'user-message': {\n _foldUserMessage(state, event.message, meta);\n break;\n }\n case 'regenerate': {\n // Regenerate input — wire-only signal. Carries no projection state;\n // the agent reads `target` / `parent` from the wire headers via\n // the input-event lookup path. No fold work to do here.\n break;\n }\n case 'tool-result': {\n _foldClientToolResult(state, event, meta);\n break;\n }\n case 'tool-result-error': {\n _foldClientToolResultError(state, event, meta);\n break;\n }\n case 'tool-approval-response': {\n _foldToolApprovalResponse(state, event, meta);\n break;\n }\n }\n } else {\n _foldChunk(state, event, meta);\n }\n\n // Re-evaluate pending tool resolutions in case the just-folded event\n // produced the assistant they were waiting on. Cheap when the list is\n // empty (the common case).\n if (state.pendingToolResolutions.length > 0) {\n _retryPendingResolutions(state);\n }\n\n return state;\n};\n\n/**\n * Narrow the union to TInput vs TOutput by the discriminator field name.\n * VercelInput variants carry `kind`; VercelOutput variants carry `type`.\n * @param event - The event to narrow.\n * @returns True when the event is a VercelInput, false for VercelOutput.\n */\nconst _isInput = (event: VercelInput | VercelOutput): event is VercelInput => 'kind' in event;\n\n// ---------------------------------------------------------------------------\n// Conflict-key derivation\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a per-event conflict key, or `undefined` if the event doesn't\n * compete with any other event for shared state. Used by `fold` to scope\n * the high-water-mark check to genuine conflicts (e.g. two\n * `tool-output-available` for the same `toolCallId`) rather than to every\n * event in the stream.\n * @param event - The event being folded.\n * @param meta - Transport-derived metadata (used for events keyed by codec-message-id).\n * @returns The conflict key, or `undefined` if the event is additive / independent.\n */\nconst _conflictKeyOf = (event: VercelInput | VercelOutput, meta: ReducerMeta): string | undefined => {\n if (_isInput(event)) {\n switch (event.kind) {\n case 'user-message': {\n // Dedup re-publishes of the same user message by its wire\n // codec-message-id, never by the domain `message.id`. Without a\n // codec-message-id there is nothing to correlate on, so the fold\n // is left unconditional.\n return meta.messageId === undefined ? undefined : `user-msg:${meta.messageId}`;\n }\n case 'tool-approval-response': {\n return `tool-approval:${event.payload.toolCallId}`;\n }\n // Client tool results compete for the same final state of the tool\n // call (against agent-side `tool-output-available`/`tool-output-error`\n // chunks and against `tool-output-denied`/`tool-approval-request`).\n // Highest serial wins. Shares the `tool-output:` namespace with the\n // agent-side chunks below.\n case 'tool-result':\n case 'tool-result-error': {\n return `tool-output:${event.payload.toolCallId}`;\n }\n case 'regenerate': {\n return undefined;\n }\n }\n }\n\n switch (event.type) {\n // Tool-input state machine, keyed by toolCallId.\n case 'tool-input-start':\n case 'tool-input-available':\n case 'tool-input-error': {\n return `${event.type}:${event.toolCallId}`;\n }\n\n // All \"tool-output-ish\" output variants compete for the same final\n // state of the tool call. Shares the `tool-output:` namespace with\n // the client-published input variants above.\n case 'tool-output-available':\n case 'tool-output-error':\n case 'tool-output-denied':\n case 'tool-approval-request': {\n return `tool-output:${event.toolCallId}`;\n }\n\n // Per-stream start/end markers: duplicates would create phantom parts\n // or wipe accumulated text. Keyed by (codec-message-id, stream-id).\n case 'text-start':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-end': {\n return `${event.type}:${meta.messageId ?? ''}:${event.id}`;\n }\n\n // Message-level markers, keyed by codec-message-id.\n case 'finish':\n case 'message-metadata': {\n return `${event.type}:${meta.messageId ?? ''}`;\n }\n\n // Purely additive or independent — never dedup:\n // text-delta / reasoning-delta / tool-input-delta (additive content)\n // start / start-step / finish-step / abort / error (lifecycle)\n // file / source-url / source-document (independent attachments)\n // data-* (opaque to the reducer)\n default: {\n return undefined;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Input folds\n// ---------------------------------------------------------------------------\n\nconst _foldUserMessage = (state: VercelProjection, message: AI.UIMessage, meta: ReducerMeta): VercelProjection => {\n // Correlate the projection entry on the wire codec-message-id; the\n // caller-supplied `message.id` is preserved verbatim and surfaced to the\n // application unchanged. Without a codec-message-id the message has no\n // identity to key on, so it is appended as a fresh entry.\n const codecMessageId = meta.messageId;\n if (codecMessageId === undefined) {\n state.messages.push({ codecMessageId: message.id, message });\n return state;\n }\n const existingIdx = state.messages.findIndex((e) => e.codecMessageId === codecMessageId);\n if (existingIdx === -1) {\n state.messages.push({ codecMessageId, message });\n } else {\n state.messages[existingIdx] = { codecMessageId, message };\n }\n return state;\n};\n\n/**\n * Fold a client-published `ToolResult`. The input carries\n * `codecMessageId` pointing at the assistant whose `dynamic-tool` part\n * holds the matching `toolCallId`. If the assistant and its matching\n * `dynamic-tool` part are both present, fold directly; otherwise pend\n * until that tool part arrives.\n * @param state - Projection to fold into.\n * @param event - The tool-result input (codecMessageId + domain payload).\n * @param meta - Transport-derived metadata.\n * @returns The same projection reference.\n */\nconst _foldClientToolResult = (\n state: VercelProjection,\n event: ToolResult<VercelToolResultPayload>,\n meta: ReducerMeta,\n): VercelProjection => {\n const { toolCallId, output } = event.payload;\n const owner = _findOwner(state, event.codecMessageId, toolCallId);\n if (owner) {\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, {\n type: 'tool-output-available',\n toolCallId,\n output,\n });\n return state;\n }\n\n state.pendingToolResolutions.push({\n targetCodecMessageId: event.codecMessageId,\n toolCallId,\n serial: meta.serial,\n resolution: { kind: 'tool-result', output },\n });\n return state;\n};\n\n/**\n * Fold a client-published `ToolResultError`. Mirrors\n * {@link _foldClientToolResult} but with the error transition.\n * @param state - Projection to fold into.\n * @param event - The tool-result-error input (codecMessageId + domain payload).\n * @param meta - Transport-derived metadata.\n * @returns The same projection reference.\n */\nconst _foldClientToolResultError = (\n state: VercelProjection,\n event: ToolResultError<VercelToolResultErrorPayload>,\n meta: ReducerMeta,\n): VercelProjection => {\n const { toolCallId, message } = event.payload;\n const owner = _findOwner(state, event.codecMessageId, toolCallId);\n if (owner) {\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, {\n type: 'tool-output-error',\n toolCallId,\n errorText: message,\n });\n return state;\n }\n\n state.pendingToolResolutions.push({\n targetCodecMessageId: event.codecMessageId,\n toolCallId,\n serial: meta.serial,\n resolution: { kind: 'tool-result-error', message },\n });\n return state;\n};\n\n/**\n * Fold a client-published `ToolApprovalResponse`. The input carries\n * `codecMessageId` pointing at the assistant whose `dynamic-tool` part\n * holds the matching `toolCallId`. Approval → `approval-responded`;\n * denial → `output-denied` via {@link transitionToolPart}.\n * @param state - Projection to fold into.\n * @param event - The approval-response input.\n * @param meta - Transport-derived metadata.\n * @returns The same projection reference.\n */\nconst _foldToolApprovalResponse = (\n state: VercelProjection,\n event: ToolApprovalResponse<VercelToolApprovalResponsePayload>,\n meta: ReducerMeta,\n): VercelProjection => {\n const { toolCallId, approved, reason } = event.payload;\n const owner = _findOwner(state, event.codecMessageId, toolCallId);\n if (owner) {\n owner.message.parts[owner.tracker.partIndex] = _approvalTransition(owner.part, approved, reason);\n return state;\n }\n\n state.pendingToolResolutions.push({\n targetCodecMessageId: event.codecMessageId,\n toolCallId,\n serial: meta.serial,\n resolution: {\n kind: 'tool-approval-response',\n approved,\n ...(reason === undefined ? {} : { reason }),\n },\n });\n return state;\n};\n\ninterface OwnerLookup {\n message: AI.UIMessage;\n tracker: ToolPartTracker;\n part: AI.DynamicToolUIPart;\n}\n\nconst _findOwner = (state: VercelProjection, codecMessageId: string, toolCallId: string): OwnerLookup | undefined => {\n const entry = state.messages.find((e) => e.codecMessageId === codecMessageId);\n if (!entry) return undefined;\n const trackers = _ensureTrackers(state, codecMessageId);\n const found = _getToolPart(entry.message, trackers, toolCallId);\n if (!found) return undefined;\n return { message: entry.message, tracker: found.tracker, part: found.part };\n};\n\n/**\n * Locate the `dynamic-tool` part for a `toolCallId` anywhere in the projection.\n * Agent-emitted second-pass tool outputs (after an approved tool runs) are\n * stamped with a fresh codec-message-id that differs from the assistant holding\n * the tool call, so they can't be found via `meta.messageId` — they fold onto\n * whichever message holds the matching tool call (created in the first pass or\n * by an approval response).\n * @param state - Projection to scan.\n * @param toolCallId - The tool call to locate.\n * @returns The owning message, tracker, and part, or `undefined` if absent.\n */\nconst _findToolPartOwner = (state: VercelProjection, toolCallId: string): OwnerLookup | undefined => {\n for (const entry of state.messages) {\n const trackers = state.trackers.get(entry.codecMessageId);\n if (!trackers) continue;\n const found = _getToolPart(entry.message, trackers, toolCallId);\n if (found) return { message: entry.message, tracker: found.tracker, part: found.part };\n }\n return undefined;\n};\n\n/**\n * Build the next `dynamic-tool` part shape for an approval response.\n *\n * For `approved=true`, transition to `approval-responded` so the AI SDK's\n * multi-step loop will auto-run the tool on the next step.\n * `transitionToolPart` has no shape for this transition, so we synthesize\n * the part directly.\n *\n * For `approved=false`, delegate to `transitionToolPart` with a synthetic\n * `tool-output-denied` chunk so denial mirrors the chunk-driven path.\n * @param part - The existing `dynamic-tool` part being transitioned.\n * @param approved - Whether the user approved the tool execution.\n * @param reason - Optional human-readable reason.\n * @returns The replacement `dynamic-tool` part.\n */\nconst _approvalTransition = (\n part: AI.DynamicToolUIPart,\n approved: boolean,\n reason: string | undefined,\n): AI.DynamicToolUIPart => {\n if (approved) {\n return {\n ...toolBase(part),\n state: 'approval-responded',\n input: 'input' in part ? part.input : undefined,\n approval: {\n id: 'approval' in part && part.approval ? part.approval.id : '',\n approved: true,\n ...(reason === undefined ? {} : { reason }),\n },\n };\n }\n return transitionToolPart(part, {\n type: 'tool-output-denied',\n toolCallId: part.toolCallId,\n ...(reason === undefined ? {} : { reason }),\n });\n};\n\n/**\n * Re-attempt every pending tool resolution against the current projection.\n * Successfully promoted entries are removed from the pending list. Cheap:\n * bounded by the number of pending entries.\n * @param state - Projection to walk and mutate.\n */\nconst _retryPendingResolutions = (state: VercelProjection): void => {\n const next: PendingToolResolution[] = [];\n for (const pending of state.pendingToolResolutions) {\n const owner = _findOwner(state, pending.targetCodecMessageId, pending.toolCallId);\n if (!owner) {\n next.push(pending);\n continue;\n }\n switch (pending.resolution.kind) {\n case 'tool-result': {\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, {\n type: 'tool-output-available',\n toolCallId: pending.toolCallId,\n output: pending.resolution.output,\n });\n break;\n }\n case 'tool-result-error': {\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, {\n type: 'tool-output-error',\n toolCallId: pending.toolCallId,\n errorText: pending.resolution.message,\n });\n break;\n }\n case 'tool-approval-response': {\n owner.message.parts[owner.tracker.partIndex] = _approvalTransition(\n owner.part,\n pending.resolution.approved,\n pending.resolution.reason,\n );\n break;\n }\n }\n }\n state.pendingToolResolutions = next;\n};\n\n// ---------------------------------------------------------------------------\n// UIMessageChunk fold\n// ---------------------------------------------------------------------------\n\nconst _foldChunk = (state: VercelProjection, chunk: VercelOutput, meta: ReducerMeta): VercelProjection => {\n const messageId = meta.messageId;\n if (messageId === undefined) {\n // Without a target codec-message-id, a chunk has nowhere to land. Drop.\n return state;\n }\n\n switch (chunk.type) {\n case 'start':\n case 'start-step':\n case 'finish-step':\n case 'finish':\n case 'abort':\n case 'error':\n case 'message-metadata': {\n return _foldLifecycle(state, chunk, messageId);\n }\n\n case 'text-start':\n case 'text-delta':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-delta':\n case 'reasoning-end': {\n return _foldTextOrReasoning(state, chunk, messageId);\n }\n\n case 'tool-input-start':\n case 'tool-input-delta':\n case 'tool-input-available':\n case 'tool-input-error': {\n return _foldToolInput(state, chunk, messageId);\n }\n\n case 'tool-output-available':\n case 'tool-output-error':\n case 'tool-output-denied':\n case 'tool-approval-request': {\n return _foldToolOutput(state, chunk, messageId);\n }\n\n case 'file':\n case 'source-url':\n case 'source-document': {\n return _foldContentPart(state, chunk, messageId);\n }\n\n default: {\n if (chunk.type.startsWith('data-')) {\n return _foldDataPart(state, chunk, messageId);\n }\n return state;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Message + tracker helpers\n// ---------------------------------------------------------------------------\n\nconst _ensureMessage = (state: VercelProjection, codecMessageId: string): AI.UIMessage => {\n let entry = state.messages.find((e) => e.codecMessageId === codecMessageId);\n if (!entry) {\n // No source id seen yet — seed the domain `message.id` with the\n // codec-message-id as a fallback. The `start` chunk overwrites it with\n // the stream's `messageId` when the stream provides one.\n entry = { codecMessageId, message: { id: codecMessageId, role: 'assistant', parts: [] } };\n state.messages.push(entry);\n }\n return entry.message;\n};\n\nconst _ensureTrackers = (state: VercelProjection, messageId: string): MessageTrackers => {\n let trackers = state.trackers.get(messageId);\n if (!trackers) {\n trackers = { text: new Map(), reasoning: new Map(), tools: new Map() };\n state.trackers.set(messageId, trackers);\n }\n return trackers;\n};\n\nconst _getToolPart = (\n message: AI.UIMessage,\n trackers: MessageTrackers,\n toolCallId: string,\n): { tracker: ToolPartTracker; part: AI.DynamicToolUIPart } | undefined => {\n const tracker = trackers.tools.get(toolCallId);\n if (!tracker) return undefined;\n const part = message.parts[tracker.partIndex];\n if (part?.type !== 'dynamic-tool') return undefined;\n return { tracker, part };\n};\n\n// ---------------------------------------------------------------------------\n// Lifecycle events\n// ---------------------------------------------------------------------------\n\nconst _foldLifecycle = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'start' | 'start-step' | 'finish-step' | 'finish' | 'abort' | 'error' | 'message-metadata' }\n >,\n messageId: string,\n): VercelProjection => {\n switch (chunk.type) {\n case 'start': {\n // The projection entry is keyed on the wire codec-message-id\n // (`messageId`); every subsequent chunk for this message correlates on\n // that, independent of `message.id`. So we faithfully reproduce the\n // stream's own `messageId` on the reconstructed `UIMessage.id` (the\n // value surfaced to the application) without risk of orphaning later\n // chunks. When the stream omits it, the codec-message-id seeded by\n // `_ensureMessage` stands as the fallback id.\n const message = _ensureMessage(state, messageId);\n if (chunk.messageId !== undefined) message.id = chunk.messageId;\n if (chunk.messageMetadata !== undefined) message.metadata = chunk.messageMetadata;\n return state;\n }\n case 'start-step': {\n const message = _ensureMessage(state, messageId);\n message.parts.push({ type: 'step-start' });\n return state;\n }\n case 'finish-step': {\n // Reset text/reasoning stream trackers so a follow-up step can start\n // new parts with potentially-reused stream ids.\n const trackers = state.trackers.get(messageId);\n if (trackers) {\n trackers.text.clear();\n trackers.reasoning.clear();\n }\n return state;\n }\n case 'finish': {\n const message = state.messages.find((e) => e.codecMessageId === messageId)?.message;\n if (message && chunk.messageMetadata !== undefined) {\n message.metadata = chunk.messageMetadata;\n }\n // Tracker state retained — late events still resolvable; cleanup happens at Run end.\n return state;\n }\n case 'abort':\n case 'error': {\n // No state mutation — run termination is observed via the wire run-end\n // event, not the projection.\n return state;\n }\n case 'message-metadata': {\n const message = state.messages.find((e) => e.codecMessageId === messageId)?.message;\n if (message && chunk.messageMetadata !== undefined) {\n message.metadata = chunk.messageMetadata;\n }\n return state;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Text and reasoning streaming\n// ---------------------------------------------------------------------------\n\nconst _foldTextOrReasoning = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'text-start' | 'text-delta' | 'text-end' | 'reasoning-start' | 'reasoning-delta' | 'reasoning-end' }\n >,\n messageId: string,\n): VercelProjection => {\n const message = _ensureMessage(state, messageId);\n const trackers = _ensureTrackers(state, messageId);\n\n const isText = chunk.type.startsWith('text-');\n const partType = isText ? 'text' : 'reasoning';\n const activeMap = isText ? trackers.text : trackers.reasoning;\n\n switch (chunk.type) {\n case 'text-start':\n case 'reasoning-start': {\n activeMap.set(chunk.id, message.parts.length);\n message.parts.push({ type: partType, text: '' });\n return state;\n }\n case 'text-delta':\n case 'reasoning-delta': {\n const idx = activeMap.get(chunk.id);\n if (idx === undefined) return state;\n const part = message.parts[idx];\n if (part?.type === partType) {\n part.text += chunk.delta;\n }\n return state;\n }\n case 'text-end':\n case 'reasoning-end': {\n activeMap.delete(chunk.id);\n return state;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Tool input streaming\n// ---------------------------------------------------------------------------\n\nconst _foldToolInput = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'tool-input-start' | 'tool-input-delta' | 'tool-input-available' | 'tool-input-error' }\n >,\n messageId: string,\n): VercelProjection => {\n const message = _ensureMessage(state, messageId);\n const trackers = _ensureTrackers(state, messageId);\n\n switch (chunk.type) {\n case 'tool-input-start': {\n const partIndex = message.parts.length;\n message.parts.push({ ...toolBase(chunk), state: 'input-streaming', input: undefined });\n trackers.tools.set(chunk.toolCallId, { partIndex, inputText: '' });\n return state;\n }\n case 'tool-input-delta': {\n const tracker = trackers.tools.get(chunk.toolCallId);\n if (!tracker) return state;\n tracker.inputText += chunk.inputTextDelta;\n\n let parsedInput: unknown;\n try {\n // CAST: JSON.parse returns any; unknown is the safe trust-boundary type.\n parsedInput = JSON.parse(tracker.inputText) as unknown;\n } catch {\n parsedInput = undefined;\n }\n\n const found = _getToolPart(message, trackers, chunk.toolCallId);\n if (!found) return state;\n message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'input-streaming',\n input: parsedInput,\n };\n return state;\n }\n case 'tool-input-available': {\n const found = _getToolPart(message, trackers, chunk.toolCallId);\n if (!found) return state;\n message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'input-available',\n input: chunk.input,\n };\n return state;\n }\n case 'tool-input-error': {\n const found = _getToolPart(message, trackers, chunk.toolCallId);\n if (found) {\n message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'output-error',\n input: chunk.input,\n errorText: chunk.errorText,\n };\n } else {\n const partIndex = message.parts.length;\n message.parts.push({\n ...toolBase(chunk),\n state: 'output-error',\n input: chunk.input,\n errorText: chunk.errorText,\n });\n trackers.tools.set(chunk.toolCallId, { partIndex, inputText: '' });\n }\n return state;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Tool output transitions (agent-published chunks)\n// ---------------------------------------------------------------------------\n\nconst _foldToolOutput = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'tool-output-available' | 'tool-output-error' | 'tool-output-denied' | 'tool-approval-request' }\n >,\n messageId: string,\n): VercelProjection => {\n // `tool-output-available` / `tool-output-error` after an approved tool runs\n // are emitted by streamText's continuation pass under a fresh\n // codec-message-id that differs from the assistant holding the tool call.\n // Resolve the owning part by toolCallId across the whole projection so the\n // output folds onto the original message. Deliberately do NOT materialise\n // `messageId` first — that would leave a phantom empty message behind the\n // fresh id. Drop on miss: a tool output with no matching tool call has no\n // anchor to attach to.\n if (chunk.type === 'tool-output-available' || chunk.type === 'tool-output-error') {\n const owner = _findToolPartOwner(state, chunk.toolCallId);\n if (!owner) return state;\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, chunk);\n return state;\n }\n\n // `tool-approval-request` (first pass) creates the part on the run's own\n // message; `tool-output-denied` transitions that same part. Both key on the\n // stamped messageId.\n const message = _ensureMessage(state, messageId);\n const trackers = _ensureTrackers(state, messageId);\n\n const found = _getToolPart(message, trackers, chunk.toolCallId);\n if (!found) return state;\n\n message.parts[found.tracker.partIndex] = transitionToolPart(found.part, chunk);\n return state;\n};\n\n// ---------------------------------------------------------------------------\n// File / source content parts\n// ---------------------------------------------------------------------------\n\nconst _foldContentPart = (\n state: VercelProjection,\n chunk: Extract<AI.UIMessageChunk, { type: 'file' | 'source-url' | 'source-document' }>,\n messageId: string,\n): VercelProjection => {\n const message = _ensureMessage(state, messageId);\n\n switch (chunk.type) {\n case 'file': {\n message.parts.push({ type: 'file', mediaType: chunk.mediaType, url: chunk.url });\n return state;\n }\n case 'source-url': {\n message.parts.push(\n stripUndefined({\n type: 'source-url' as const,\n sourceId: chunk.sourceId,\n url: chunk.url,\n title: chunk.title,\n }),\n );\n return state;\n }\n case 'source-document': {\n message.parts.push(\n stripUndefined({\n type: 'source-document' as const,\n sourceId: chunk.sourceId,\n mediaType: chunk.mediaType,\n title: chunk.title,\n filename: chunk.filename,\n }),\n );\n return state;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// data-* parts\n// ---------------------------------------------------------------------------\n\nconst _foldDataPart = (\n state: VercelProjection,\n chunk: Extract<AI.UIMessageChunk, { type: `data-${string}` }>,\n messageId: string,\n): VercelProjection => {\n if (chunk.transient) return state;\n\n const message = _ensureMessage(state, messageId);\n\n // CAST: chunk.type is `data-${string}` which satisfies DataUIPart, but\n // TypeScript cannot verify the template literal matches a specific\n // UIMessagePart variant at the type level.\n const dataPart = stripUndefined({\n type: chunk.type,\n id: chunk.id,\n data: chunk.data,\n }) as AI.UIMessage['parts'][number];\n\n if (chunk.id !== undefined) {\n const idx = message.parts.findIndex((p) => p.type === chunk.type && 'id' in p && p.id === chunk.id);\n if (idx !== -1) {\n message.parts[idx] = dataPart;\n return state;\n }\n }\n\n message.parts.push(dataPart);\n return state;\n};\n\n// ---------------------------------------------------------------------------\n// getMessages\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the UIMessage list from a projection, each paired with its\n * codec-message-id. Client-published tool resolutions amend existing\n * assistants in place via `kind: 'tool-result'` etc. — they never\n * materialise as their own UIMessage in the projection, so no filtering is\n * needed here.\n * @param projection - Projection produced by `init` + repeated `fold` calls.\n * @returns The visible messages with their codec-message-ids, in publication order.\n */\nexport const getMessages = (projection: VercelProjection): CodecMessage<AI.UIMessage>[] => projection.messages;\n","/**\n * Vercel AI SDK codec — implements\n * `Codec<VercelInput, VercelOutput, VercelProjection, UIMessage>`.\n *\n * The codec is the reducer (extends `Reducer<VercelInput | VercelOutput,\n * VercelProjection>`) plus encoder/decoder factories and `getMessages`\n * for Tree population.\n *\n * ```ts\n * import { UIMessageCodec } from '@ably/ai-transport/vercel';\n *\n * const encoder = UIMessageCodec.createEncoder(writer, options);\n * const decoder = UIMessageCodec.createDecoder();\n * const projection = UIMessageCodec.init();\n * ```\n */\n\nimport type * as AI from 'ai';\n\nimport type { Codec } from '../../core/codec/types.js';\nimport { createDecoder } from './decoder.js';\nimport { createEncoder } from './encoder.js';\nimport type {\n VercelInput,\n VercelOutput,\n VercelToolApprovalResponsePayload,\n VercelToolResultErrorPayload,\n VercelToolResultPayload,\n} from './events.js';\nimport { fold, getMessages, init, type VercelProjection } from './reducer.js';\n\n/**\n * Vercel AI SDK codec implementing\n * `Codec<VercelInput, VercelOutput, VercelProjection, UIMessage>`.\n *\n * Folds `VercelInput`s and `VercelOutput`s into a `VercelProjection`\n * carrying `UIMessage[]`. Encoder and decoder factories handle the wire\n * mapping for both directions.\n */\nconst uiMessageCodecImpl = {\n // Internal field - picked up by registerAgent via AdapterTagHolder cast. Spec: AIT-CT1a3, AIT-ST1a3.\n adapterTag: 'vercel-ai-sdk-ui-message' as const,\n init,\n fold,\n createEncoder,\n createDecoder,\n getMessages,\n createUserMessage: (message: AI.UIMessage): VercelInput => ({ kind: 'user-message', message }),\n createRegenerate: (target: string, parent: string): VercelInput => ({\n kind: 'regenerate',\n target,\n parent,\n }),\n createToolResult: (codecMessageId: string, payload: VercelToolResultPayload): VercelInput => ({\n kind: 'tool-result',\n codecMessageId,\n payload,\n }),\n createToolResultError: (codecMessageId: string, payload: VercelToolResultErrorPayload): VercelInput => ({\n kind: 'tool-result-error',\n codecMessageId,\n payload,\n }),\n createToolApprovalResponse: (codecMessageId: string, payload: VercelToolApprovalResponsePayload): VercelInput => ({\n kind: 'tool-approval-response',\n codecMessageId,\n payload,\n }),\n};\n\n// Validate Codec conformance via `satisfies` on the variable (no excess-property\n// check, so the internal `adapterTag` is permitted) while keeping the concrete\n// type so the codec-specific factories (createToolResult, etc.) stay callable.\nexport const UIMessageCodec = uiMessageCodecImpl satisfies Codec<\n VercelInput,\n VercelOutput,\n VercelProjection,\n AI.UIMessage\n>;\n\nexport type { VercelInput, VercelOutput } from './events.js';\nexport { type VercelProjection } from './reducer.js';\n","/**\n * Type-safe EventEmitter wrapping Ably's internal EventEmitter.\n *\n * Takes a single `EventsMap` type parameter — an interface mapping event names\n * to payload types — rather than Ably's three type parameters. Adapted from\n * the ably-chat-js SDK.\n *\n * ```ts\n * interface MyEvents {\n * reaction: { emoji: string };\n * status: { online: boolean };\n * }\n *\n * const emitter = new EventEmitter<MyEvents>(logger);\n * emitter.on('reaction', (event) => console.log(event.emoji));\n * emitter.emit('reaction', { emoji: '👍' });\n * ```\n */\n\nimport * as Ably from 'ably';\n\nimport type { Logger } from './logger.js';\n\n/** Callback receiving a union of all possible event payloads. */\ntype Callback<EventsMap> = (arg: EventsMap[keyof EventsMap]) => void;\n\n/** Callback receiving the payload for a single event type. */\ntype CallbackSingle<K> = (arg: K) => void;\n\n/**\n * Type-safe interface for the Ably EventEmitter, parameterized by an EventsMap\n * that maps event names to their payload types.\n */\ninterface InterfaceEventEmitter<EventsMap> extends Ably.EventEmitter<Callback<EventsMap>, void, keyof EventsMap> {\n /** Emit an event with a type-safe payload. Payload is optional for `undefined`-typed events. */\n emit<K extends keyof EventsMap>(\n event: K,\n ...args: EventsMap[K] extends undefined ? [EventsMap[K]?] : [EventsMap[K]]\n ): void;\n\n /** Subscribe to a single event with a typed callback. */\n on<K extends keyof EventsMap>(event: K, callback: CallbackSingle<EventsMap[K]>): void;\n /** Subscribe to two events with a union-typed callback. */\n on<K1 extends keyof EventsMap, K2 extends keyof EventsMap>(\n events: [K1, K2],\n callback: CallbackSingle<EventsMap[K1] | EventsMap[K2]>,\n ): void;\n /** Subscribe to three events with a union-typed callback. */\n on<K1 extends keyof EventsMap, K2 extends keyof EventsMap, K3 extends keyof EventsMap>(\n events: [K1, K2, K3],\n callback: CallbackSingle<EventsMap[K1] | EventsMap[K2] | EventsMap[K3]>,\n ): void;\n /** Subscribe to an array of events. */\n on(events: (keyof EventsMap)[], callback: Callback<EventsMap>): void;\n /** Subscribe to all events. */\n on(callback: Callback<EventsMap>): void;\n\n /** Unsubscribe a callback from a specific event. */\n off<K extends keyof EventsMap>(event: K, listener: CallbackSingle<EventsMap[K]>): void;\n /** Unsubscribe a callback from all events, or remove all listeners if no callback provided. */\n off(listener?: Callback<EventsMap>): void;\n}\n\n/**\n * Bridge from our {@link Logger} to the Ably EventEmitter's internal logger\n * contract. Ably's EventEmitter calls `logger.logAction(level, action, message)`\n * when a listener throws — we route that to our Logger's `error` method.\n * @param logger - The application logger to delegate to.\n * @returns An object satisfying the Ably EventEmitter's logger interface.\n */\nconst toAblyLogger = (logger: Logger): unknown => ({\n logAction: (_level: number, action: string, message?: string) => {\n logger.error(action, { detail: message });\n },\n shouldLog: () => true,\n});\n\n// CAST: Access Ably's internal EventEmitter constructor. Not publicly exported\n// but available to other Ably SDKs. Ably always catches listener exceptions\n// internally; the logger parameter ensures those caught exceptions are logged\n// rather than silently swallowed.\nconst InternalEventEmitter: new <EventsMap>(logger: unknown) => InterfaceEventEmitter<EventsMap> = (\n Ably.Realtime as unknown as { EventEmitter: new <EventsMap>(logger: unknown) => InterfaceEventEmitter<EventsMap> }\n).EventEmitter;\n\n/**\n * Type-safe EventEmitter based on Ably's internal EventEmitter.\n *\n * Provides the same semantics as {@link Ably.EventEmitter} (error isolation\n * between listeners, synchronous dispatch) but with a single `EventsMap` type\n * parameter for ergonomic type safety.\n *\n * Requires a {@link Logger} so that listener exceptions are routed through\n * the application's logging infrastructure rather than silently swallowed.\n */\nexport class EventEmitter<EventsMap> extends InternalEventEmitter<EventsMap> {\n /**\n * Create a new EventEmitter.\n * @param logger - Application logger. Listener exceptions are logged at error level.\n */\n constructor(logger: Logger) {\n super(toAblyLogger(logger));\n }\n}\n","import * as Ably from 'ably';\n\nimport { ErrorCode } from './errors.js';\n\n/**\n * Structured logger with leveled output and hierarchical context.\n * Implementations filter messages by level and delegate to a {@link LogHandler}.\n */\nexport interface Logger {\n /**\n * Log a message at the trace level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n trace(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the debug level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n debug(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the info level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n info(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the warn level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n warn(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the error level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n error(message: string, context?: LogContext): void;\n\n /**\n * Creates a new logger with a context that will be merged with any context provided to individual log calls.\n * The context will be overridden by any matching keys in the individual log call's context.\n * @param context The context to use for all log calls.\n * @returns A new logger instance with the context.\n */\n withContext(context: LogContext): Logger;\n}\n\n/**\n * Represents the different levels of logging that can be used.\n */\nexport enum LogLevel {\n /**\n * Something routine and expected has occurred. This level will provide logs for the vast majority of operations\n * and function calls.\n */\n Trace = 'trace',\n\n /**\n * Development information, messages that are useful when trying to debug library behavior,\n * but superfluous to normal operation.\n */\n Debug = 'debug',\n\n /**\n * Informational messages. Operationally significant to the library but not out of the ordinary.\n */\n Info = 'info',\n\n /**\n * Anything that is not immediately an error, but could cause unexpected behavior in the future. For example,\n * passing an invalid value to an option. Indicates that some action should be taken to prevent future errors.\n */\n Warn = 'warn',\n\n /**\n * A given operation has failed and cannot be automatically recovered. The error may threaten the continuity\n * of operation.\n */\n Error = 'error',\n\n /**\n * No logging will be performed.\n */\n Silent = 'silent',\n}\n\n/**\n * Represents the context of a log message.\n * It is an object of key-value pairs that can be used to provide additional context to a log message.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type LogContext = Record<string, any>;\n\n/**\n * A function that can be used to handle log messages.\n * @param message The message to log.\n * @param level The log level of the message.\n * @param context The context of the log message as key-value pairs.\n */\nexport type LogHandler = (message: string, level: LogLevel, context?: LogContext) => void;\n\n/**\n * A simple console logger that logs messages to the console.\n * @param message The message to log.\n * @param level The log level of the message.\n * @param context - The context of the log message as key-value pairs.\n */\nexport const consoleLogger = (message: string, level: LogLevel, context?: LogContext) => {\n const contextString = context ? `, context: ${JSON.stringify(context)}` : '';\n const formattedMessage = `[${new Date().toISOString()}] ${level.valueOf().toUpperCase()} ably-ai-transport: ${message}${contextString}`;\n\n switch (level) {\n case LogLevel.Trace:\n case LogLevel.Debug: {\n console.log(formattedMessage);\n break;\n }\n case LogLevel.Info: {\n console.info(formattedMessage);\n break;\n }\n case LogLevel.Warn: {\n console.warn(formattedMessage);\n break;\n }\n case LogLevel.Error: {\n console.error(formattedMessage);\n break;\n }\n case LogLevel.Silent: {\n break;\n }\n }\n};\n\n/**\n * Options for creating a logger.\n */\nexport interface LoggerOptions {\n /**\n * The handler that receives formatted log messages. Defaults to {@link consoleLogger} when omitted.\n */\n logHandler?: LogHandler;\n /**\n * The minimum level to emit; messages below this level are suppressed. Must be a valid {@link LogLevel}, otherwise logger creation throws.\n */\n logLevel: LogLevel;\n}\n\n/**\n * Creates a {@link Logger} from the given options.\n * @param options The handler and minimum level for the logger.\n * @returns A logger that filters by level and delegates to the handler.\n * @throws {@link Ably.ErrorInfo} with {@link ErrorCode.InvalidArgument} if `options.logLevel` is not a recognised {@link LogLevel}.\n */\nexport const makeLogger = (options: LoggerOptions): Logger => {\n const logHandler = options.logHandler ?? consoleLogger;\n\n return new DefaultLogger(logHandler, options.logLevel);\n};\n\n/**\n * A convenient list of log levels as numbers that can be used for easier comparison.\n */\nenum LogLevelNumber {\n Trace = 0,\n Debug = 1,\n Info = 2,\n Warn = 3,\n Error = 4,\n Silent = 5,\n}\n\n/**\n * A mapping of log levels to their numeric equivalents.\n */\nconst logLevelNumberMap = new Map<LogLevel, LogLevelNumber>([\n [LogLevel.Trace, LogLevelNumber.Trace],\n [LogLevel.Debug, LogLevelNumber.Debug],\n [LogLevel.Info, LogLevelNumber.Info],\n [LogLevel.Warn, LogLevelNumber.Warn],\n [LogLevel.Error, LogLevelNumber.Error],\n [LogLevel.Silent, LogLevelNumber.Silent],\n]);\n\n/**\n * A default logger implementation.\n */\nclass DefaultLogger implements Logger {\n private readonly _handler: LogHandler;\n private readonly _levelNumber: LogLevelNumber;\n private readonly _context?: LogContext;\n\n constructor(handler: LogHandler, level: LogLevel, context?: LogContext) {\n this._handler = handler;\n this._context = context;\n\n const levelNumber = logLevelNumberMap.get(level);\n if (levelNumber === undefined) {\n throw new Ably.ErrorInfo(`unable to create logger; invalid log level: ${level}`, ErrorCode.InvalidArgument, 400);\n }\n\n this._levelNumber = levelNumber;\n }\n\n trace(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Trace, LogLevelNumber.Trace, context);\n }\n\n debug(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Debug, LogLevelNumber.Debug, context);\n }\n\n info(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Info, LogLevelNumber.Info, context);\n }\n\n warn(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Warn, LogLevelNumber.Warn, context);\n }\n\n error(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Error, LogLevelNumber.Error, context);\n }\n\n withContext(context: LogContext): Logger {\n // Get the original log level by finding the key in logLevelNumberMap that matches this._levelNumber.\n // The Error fallback is defensive and unreachable in practice: _levelNumber always originates from the map.\n const originalLevel =\n [...logLevelNumberMap.entries()].find(([, value]) => value === this._levelNumber)?.[0] ?? LogLevel.Error;\n\n return new DefaultLogger(this._handler, originalLevel, this._mergeContext(context));\n }\n\n private _write(message: string, level: LogLevel, levelNumber: LogLevelNumber, context?: LogContext): void {\n if (levelNumber >= this._levelNumber) {\n this._handler(message, level, this._mergeContext(context));\n }\n }\n\n private _mergeContext(context?: LogContext): LogContext | undefined {\n if (!this._context) {\n return context ?? undefined;\n }\n\n return context ? { ...this._context, ...context } : this._context;\n }\n}\n","/**\n * Vercel-owned per-run output stream.\n *\n * Builds the `ReadableStream<UIMessageChunk>` that `useChat` consumes by\n * subscribing to the session Tree's `output` and `run` events for a single\n * run. Streaming is a useChat-integration concern, so it lives in the Vercel\n * layer rather than the generic core: the core Tree is the fan-out point, and\n * this projects its events into the shape `useChat` expects.\n *\n * Close semantics — the stream the consumer reads ends when:\n * - a **terminal chunk** (`finish` / `error` / `abort`) is folded for the run.\n * This is the signal `useChat`'s `sendAutomaticallyWhen` waits for, and it\n * fires even when the run merely *suspends* for a tool call (a tool-calls\n * `finish` ends the consumer stream while the core run stays alive in the\n * Tree for the continuation); or\n * - the run reaches `run-end`, which is always terminal (safety net for a run\n * that ends without emitting a terminal chunk). A `run-suspend` keeps the\n * core run alive and does not close the consumer stream.\n *\n * It errors when the session emits a non-fatal `error` (e.g. channel\n * continuity loss, or an agent-reported mid-run error), so the consumer's\n * reader rejects rather than hanging.\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport type { ClientSession } from '../../core/transport/types.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';\n\ntype VercelSession = ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;\n\n/**\n * Whether a Vercel output chunk ends the consumer-facing stream. The terminal\n * variants are `finish` (end of an LLM turn, including tool-calls), `error`,\n * and `abort`.\n * @param output - The decoded output chunk.\n * @returns True when the chunk should close the consumer stream.\n */\nconst isTerminalChunk = (output: VercelOutput): boolean =>\n output.type === 'finish' || output.type === 'error' || output.type === 'abort';\n\n/** A consumer-facing run output stream plus the handles to settle it externally. */\nexport interface RunOutputStream {\n /** The stream of decoded outputs for the run, as `useChat` consumes it. */\n stream: ReadableStream<VercelOutput>;\n /** Close the stream now (e.g. on local cancel). Idempotent. */\n close: () => void;\n /** Error the stream now (e.g. on a failed agent-invocation POST). Idempotent. */\n error: (reason: Ably.ErrorInfo) => void;\n}\n\n/**\n * Create a consumer-facing output stream for a send, sourced from the session\n * Tree's events. See the module docs for close/error semantics. The returned\n * `close`/`error` let the caller settle the stream for conditions the Tree\n * doesn't surface (local cancel, POST failure).\n *\n * Outputs route PURELY by the triggering input's codec-message-id — the key the\n * client owns from send time, before the agent mints the runId. The agent's\n * minted runId is supplied as a promise so the run-end safety-net can still\n * close the stream once it resolves.\n * @param session - The Vercel client session whose Tree to observe.\n * @param runId - The agent-minted runId, resolved when run-start is observed.\n * Used only by the run-end safety-net; routing keys on `inputCodecMessageId`.\n * @param inputCodecMessageId - The triggering input's codec-message-id. An\n * output routes to this stream when it carries this id.\n * @returns The stream and its external settle handles.\n */\nexport const createRunOutputStream = (\n session: VercelSession,\n runId: Promise<string>,\n inputCodecMessageId: string,\n): RunOutputStream => {\n const holder: { controller?: ReadableStreamDefaultController<VercelOutput> } = {};\n // ReadableStream's start() runs synchronously, so the controller is captured\n // before the constructor returns.\n const unsubscribe: (() => void)[] = [];\n const stream = new ReadableStream<VercelOutput>({\n start: (controller) => {\n holder.controller = controller;\n },\n cancel: () => {\n teardown();\n },\n });\n const { controller } = holder;\n if (!controller) {\n throw new Ably.ErrorInfo(\n 'unable to create run stream; ReadableStream start() was not called synchronously',\n ErrorCode.SessionSubscriptionError,\n 500,\n );\n }\n\n // The agent mints the runId; learn it (for the run-end safety-net) when the\n // promise resolves. Fire-and-forget: the stream opens on the input key, so a\n // never-resolving runId only forgoes the safety-net, not normal close.\n let resolvedRunId: string | undefined;\n // Best-effort: failure only disables the run-end safety-net; normal close is\n // the terminal chunk. `void` discards the promise (no await needed here).\n void runId.then(\n (id) => {\n resolvedRunId = id;\n },\n () => {\n /* session closed before run-start; safety-net stays disarmed */\n },\n );\n\n let settled = false;\n const teardown = (): void => {\n for (const unsub of unsubscribe) unsub();\n unsubscribe.length = 0;\n };\n // Settle the stream at most once: run the controller action (close/error),\n // swallow the throw if the consumer already cancelled, then tear down.\n const settle = (action: () => void): void => {\n if (settled) return;\n settled = true;\n try {\n action();\n } catch {\n /* consumer already cancelled the stream */\n }\n teardown();\n };\n const close = (): void => {\n settle(() => {\n controller.close();\n });\n };\n const error = (reason: Ably.ErrorInfo): void => {\n settle(() => {\n controller.error(reason);\n });\n };\n\n unsubscribe.push(\n session.tree.on('output', (event) => {\n if (event.inputCodecMessageId !== inputCodecMessageId) return;\n for (const output of event.events) {\n try {\n controller.enqueue(output);\n } catch {\n close();\n return;\n }\n if (isTerminalChunk(output)) {\n close();\n return;\n }\n }\n }),\n session.tree.on('run', (event) => {\n // run-end is always terminal; a run-suspend (event.type === 'suspend')\n // keeps the core run alive and must not close the consumer stream. Match\n // against the resolved runId once the agent has minted it.\n if (event.type === 'end' && resolvedRunId !== undefined && event.runId === resolvedRunId) {\n close();\n }\n }),\n session.on('error', (reason) => {\n error(reason);\n }),\n );\n\n return { stream, close, error };\n};\n","/**\n * Vercel chat transport: wraps a core ClientSession to satisfy the\n * ChatTransport interface that useChat expects.\n *\n * This is a thin adapter — the real logic lives in the core client session.\n * The chat transport maps Vercel's sendMessages/reconnectToStream contract\n * to the core session's send/cancel methods.\n *\n * useChat manages message state before calling sendMessages:\n * - submit-message (new): appends the new user message, passes the full array\n * - submit-message (edit): truncates after the edited message, replaces it,\n * passes the truncated array with messageId set\n * - regenerate-message: truncates after the target, passes the truncated array\n *\n * The adapter uses `(trigger, last-message role)` to determine the\n * history/messages split:\n * - submit-message + last message is a user message: that last message is new\n * (publish to channel), rest is history. A new submit and an edit both take\n * this path — an edit just carries a messageId.\n * - submit-message + last message is an assistant already in the tree\n * (continuation): no new messages, entire array is history\n * - regenerate-message: no new messages, entire array is history\n *\n * For an edit (submit-message with messageId) and for forking off an\n * unresolved tool call, the adapter computes fork metadata (forkOf/parent)\n * from the conversation tree so the server can place the response on the\n * correct branch. Regeneration fork metadata is NOT computed here —\n * `View.regenerate` derives forkOf/parent from the tree itself.\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport type { CodecMessage } from '../../core/codec/types.js';\nimport type { ActiveRun, ClientSession, SendOptions } from '../../core/transport/types.js';\nimport { ErrorCode } from '../../errors.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport { LogLevel, makeLogger } from '../../logger.js';\nimport type { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';\nimport { UIMessageCodec } from '../codec/index.js';\nimport { createRunOutputStream } from './run-output-stream.js';\n\n// ---------------------------------------------------------------------------\n// ChatTransport options\n// ---------------------------------------------------------------------------\n\n/**\n * Context passed to {@link ChatTransportOptions.prepareSendMessagesRequest} for\n * customizing the HTTP POST body and headers.\n */\nexport interface SendMessagesRequestContext {\n /** Chat session ID (from useChat's id). */\n chatId?: string;\n /** What triggered the request: user sent a message, or requested regeneration. */\n trigger: 'submit-message' | 'regenerate-message';\n /**\n * The message ID for edit or regeneration requests. For regeneration,\n * identifies the assistant message to regenerate. For edits (submit-message\n * with messageId), identifies the user message being replaced. Undefined\n * when submitting a new message.\n */\n messageId?: string;\n /** Previous messages in the conversation (context for the LLM). */\n history: AI.UIMessage[];\n /** The new message(s) being sent (to publish to the channel). Empty for regeneration and for continuations (an auto-submit where the last message is an already-tracked assistant). */\n messages: AI.UIMessage[];\n /** The codec-message-id of the message being forked — the edited user message, or the preceding assistant when forking off an unresolved tool call. Undefined for regeneration (View.regenerate derives it) and fresh sends. */\n forkOf?: string;\n /** The codec-message-id of the predecessor in the conversation thread. */\n parent?: string;\n}\n\n/** Default agent endpoint the transport POSTs invocations to — mirrors Vercel's DefaultChatTransport. */\nexport const DEFAULT_VERCEL_API = '/api/chat';\n\n/** Options for customizing the ChatTransport behavior. */\nexport interface ChatTransportOptions {\n /**\n * Endpoint the transport POSTs the invocation pointer to, to wake the\n * agent. Mirrors useChat's request-driven contract. Default `/api/chat`.\n */\n api?: string;\n /** Fetch credentials mode for the invocation POST (e.g. `'include'`). */\n credentials?: RequestCredentials;\n /** Custom fetch implementation for the invocation POST. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /**\n * Customize the invocation POST before sending. Called by sendMessages()\n * with the conversation context; the returned `body` is merged into the\n * POST body (the run's invocation identifiers always take precedence) and\n * `headers` are added to the request. Use it for auth headers or extra\n * agent metadata.\n * @param context - The conversation context for the current request.\n * @returns The body and headers to merge into the invocation POST.\n */\n prepareSendMessagesRequest?: (context: SendMessagesRequestContext) => {\n body?: Record<string, unknown>;\n headers?: Record<string, string>;\n };\n}\n\n// ---------------------------------------------------------------------------\n// ChatTransport interface\n// ---------------------------------------------------------------------------\n\n/**\n * Additional options passed through from useChat alongside the core\n * sendMessages/reconnectToStream parameters.\n *\n * Mirrors the AI SDK's internal ChatRequestOptions type, which is not\n * exported from the `ai` package.\n */\ninterface ChatRequestOptions {\n /** Additional headers for the request. */\n headers?: Record<string, string> | Headers;\n /** Additional JSON body properties for the request. */\n body?: object;\n /** Custom metadata to attach to the request. */\n metadata?: unknown;\n}\n\n/**\n * Transport interface for Vercel AI SDK's useChat hook.\n *\n * Structurally compatible with the AI SDK's internal `ChatTransport<UIMessage>`\n * interface. Extended with `close()` for releasing the underlying Ably transport\n * resources and `streaming` / `onStreamingChange` for coordinating with\n * useMessageSync.\n */\nexport interface ChatTransport {\n /** Send messages and return a streaming response of UIMessageChunk events. */\n sendMessages: (\n options: {\n /** The type of message submission — new message or regeneration. */\n trigger: 'submit-message' | 'regenerate-message';\n /** Unique identifier for the chat session. */\n chatId: string;\n /** ID of the message to regenerate, or undefined for new messages. */\n messageId: string | undefined;\n /** Array of UI messages representing the conversation history. */\n messages: AI.UIMessage[];\n /** Signal to abort the request if needed. */\n abortSignal: AbortSignal | undefined;\n } & ChatRequestOptions,\n ) => Promise<ReadableStream<AI.UIMessageChunk>>;\n\n /**\n * Reconnect to an existing streaming response. Returns null if no active\n * stream exists for the specified chat session.\n */\n reconnectToStream: (\n options: {\n /** Unique identifier for the chat session to reconnect to. */\n chatId: string;\n } & ChatRequestOptions,\n ) => Promise<ReadableStream<AI.UIMessageChunk> | null>;\n\n /** Close the underlying transport, releasing all resources. */\n close(): Promise<void>;\n\n /** Whether an own-run stream is currently being consumed by useChat. */\n readonly streaming: boolean;\n\n /**\n * Subscribe to streaming state changes. The callback fires when the\n * ChatTransport transitions between streaming and idle. Used by\n * useMessageSync to gate setMessages calls during active streams.\n * @param callback - Called with `true` when a stream starts, `false` when it ends.\n * @returns Unsubscribe function.\n */\n onStreamingChange(callback: (streaming: boolean) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Stream wrapper — passthrough that signals completion via a promise\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a ReadableStream in a passthrough TransformStream that resolves a\n * promise when the stream completes or errors. The returned stream passes\n * all chunks through unchanged, and `fail(reason)` errors the readable side\n * useChat consumes without cancelling or otherwise disturbing the source run\n * stream (used when the agent-invocation POST fails).\n * @param source - The original stream to wrap.\n * @returns The wrapped stream, a `done` promise that resolves when the stream\n * closes, and a `fail` callback that errors the wrapped stream.\n */\n\nconst wrapStreamWithDone = <T>(\n source: ReadableStream<T>,\n): { stream: ReadableStream<T>; done: Promise<void>; fail: (reason: Ably.ErrorInfo) => void } => {\n let resolveDone: () => void;\n const done = new Promise<void>((resolve) => {\n resolveDone = resolve;\n });\n\n const passthrough = new TransformStream<T, T>({\n flush: () => {\n resolveDone();\n },\n });\n\n // Aborting this signal errors the destination (the readable useChat reads)\n // with the abort reason. `preventCancel` keeps the source run stream intact\n // so the tree/observers are unaffected — only the useChat-facing view fails.\n const failController = new AbortController();\n\n // Pipe in the background. If the source errors/cancels, or `fail()` aborts,\n // resolve done so the serialization queue advances.\n // Fire-and-forget: the pipe runs independently; errors surface through\n // the readable side that useChat consumes.\n source.pipeTo(passthrough.writable, { signal: failController.signal, preventCancel: true }).catch(() => {\n resolveDone();\n });\n\n return {\n stream: passthrough.readable,\n done,\n fail: (reason: Ably.ErrorInfo) => {\n failController.abort(reason);\n },\n };\n};\n\n// ---------------------------------------------------------------------------\n// Unresolved tool call detection\n// ---------------------------------------------------------------------------\n\n/**\n * Whether a UIMessage part is a tool part — either the codec-normalised\n * `dynamic-tool` shape or the AI SDK's statically-declared `tool-${name}`\n * shape. Both carry `toolCallId` and `state`; the shape check at the end\n * is defensive against a future AI SDK release introducing a non-tool\n * variant under the `tool-` prefix (none exists today).\n * @param part - The UIMessage part to inspect.\n * @returns True when the part is a tool part of either representation.\n */\nconst _isToolPart = (part: AI.UIMessage['parts'][number]): part is AI.DynamicToolUIPart | AI.ToolUIPart =>\n (part.type === 'dynamic-tool' || part.type.startsWith('tool-')) && 'toolCallId' in part && 'state' in part;\n\n/**\n * Whether an assistant message has a `dynamic-tool` part that can't resolve\n * without further user action. Matches:\n * - `input-streaming` / `input-available` — tool call emitted, not yet run.\n * - `approval-requested` — waiting for the user.\n *\n * Excludes `approval-responded` (streamText will run the tool this run)\n * and all terminal `output-*` states.\n * @param msg - The UIMessage to inspect.\n * @returns True when a fork-on-send is warranted to avoid shipping a\n * dangling tool call to the LLM.\n */\nconst hasUnresolvedToolCall = (msg: AI.UIMessage): boolean =>\n msg.role === 'assistant' &&\n msg.parts.some(\n (p) =>\n _isToolPart(p) &&\n (p.state === 'input-streaming' || p.state === 'input-available' || p.state === 'approval-requested'),\n );\n\n/**\n * `dynamic-tool` part states that mean \"the LLM produced a tool call and\n * is waiting on it\". Used to detect new client-side resolutions in the\n * useChat overlay relative to the tree.\n */\nconst UNRESOLVED_TOOL_STATES = new Set(['input-streaming', 'input-available', 'approval-requested']);\n\n/**\n * Walk the useChat message overlay against the session tree and synthesize\n * the {@link VercelInput}s needed to resolve every `dynamic-tool` part the\n * user acted on (executed a tool, approved, denied) but the tree's reduced\n * state hasn't reflected yet.\n *\n * Each input carries the prior assistant's tree codec-message-id (the one\n * holding the original `dynamic-tool` part the resolution targets) in its\n * `codecMessageId` field, so the encoder stamps `codec-message-id`\n * and the reducer's direct-fold path lands the resolution on that assistant\n * in one step — no cross-message redirect-by-toolCallId fallback. Every\n * variant rides the `ai-input` wire, matching its publisher (client → input).\n *\n * The resulting inputs are passed alongside the continuation `view.send`\n * so the channel publish and the continuation POST land as ONE atomic\n * operation — the agent's `loadProjection()` history fetch is guaranteed\n * to see them because the channel publish happens before the POST inside\n * `_internalSend`.\n *\n * Three resolutions are produced:\n *\n * - `approval-responded` overlay vs `approval-requested` tree →\n * `tool-approval-response` carrying the user's decision\n * (`approved` = `overlayPart.approval.approved`, i.e. approve or deny)\n * - `output-available` overlay vs unresolved tree → `tool-result`\n * - `output-error` overlay vs unresolved tree → `tool-result-error`\n * @param codecMessages - The visible tree messages paired with their codec-message-ids.\n * @param messages - useChat's local overlay messages.\n * @returns The continuation inputs to publish, in tree order. Each input\n * carries its own `codecMessageId` targeting the prior assistant it folds\n * onto.\n */\nconst deriveContinuationInputs = (\n codecMessages: CodecMessage<AI.UIMessage>[],\n messages: AI.UIMessage[],\n): VercelInput[] => {\n const inputs: VercelInput[] = [];\n for (const overlay of messages) {\n if (overlay.role !== 'assistant') continue;\n // Match the overlay to its tree message by domain id (both sides\n // reconstruct the same stream id), but address the emitted inputs by\n // the tree message's codec-message-id — the agent folds tool\n // resolutions onto the assistant by codec-message-id, never by the\n // domain `message.id`.\n const treeEntry = codecMessages.find((p) => p.message.id === overlay.id);\n if (!treeEntry) continue;\n const { codecMessageId, message: treeMessage } = treeEntry;\n\n for (const overlayPart of overlay.parts) {\n if (!_isToolPart(overlayPart)) continue;\n // The codec normalises every tool part to `dynamic-tool`, but the\n // AI SDK's useChat overlay emits `tool-${name}` parts for statically\n // declared tools. Match by toolCallId rather than the type prefix\n // so the cross-representation comparison works regardless of which\n // side the tool was declared on.\n const treePart = treeMessage.parts.find(\n (p: AI.UIMessage['parts'][number]): p is AI.DynamicToolUIPart | AI.ToolUIPart =>\n _isToolPart(p) && p.toolCallId === overlayPart.toolCallId,\n );\n\n // Approval response: useChat's `addToolApprovalResponse` flipped the\n // overlay part to `approval-responded` while the tree still sits on\n // `approval-requested`. Publish a `tool-approval-response` TInput so the\n // agent's projection sees the decision.\n if (overlayPart.state === 'approval-responded' && (!treePart || treePart.state === 'approval-requested')) {\n inputs.push(\n UIMessageCodec.createToolApprovalResponse(codecMessageId, {\n toolCallId: overlayPart.toolCallId,\n approved: overlayPart.approval.approved,\n ...(overlayPart.approval.reason === undefined ? {} : { reason: overlayPart.approval.reason }),\n }),\n );\n continue;\n }\n\n // Client-tool resolution: overlay has `output-available` / `output-error`\n // while the tree's part is still unresolved. Construct a TInput\n // variant (not a UIMessageChunk) so the encoder publishes on the\n // `ai-input` wire — client tool results belong on `ai-input`, matching\n // their client publisher, not on `ai-output`.\n if (overlayPart.state !== 'output-available' && overlayPart.state !== 'output-error') continue;\n // Tree already resolved (echo arrived back) — nothing to do.\n if (treePart && !UNRESOLVED_TOOL_STATES.has(treePart.state)) continue;\n\n if (overlayPart.state === 'output-available') {\n inputs.push(\n UIMessageCodec.createToolResult(codecMessageId, {\n toolCallId: overlayPart.toolCallId,\n output: overlayPart.output,\n }),\n );\n } else {\n inputs.push(\n UIMessageCodec.createToolResultError(codecMessageId, {\n toolCallId: overlayPart.toolCallId,\n message: overlayPart.errorText,\n }),\n );\n }\n }\n }\n return inputs;\n};\n\n/**\n * Find the codec-message-id immediately preceding the message identified by\n * domain id `domainId` in the flat visible conversation. The target is\n * located by its domain `message.id` (the id useChat references), but the\n * returned value is the predecessor's codec-message-id — never a domain id.\n * Returns undefined if the target is the first message or not found.\n * @param codecMessages - Visible messages paired with their codec-message-ids.\n * @param domainId - The domain id of the target message.\n * @returns The predecessor's codec-message-id, or undefined.\n */\nconst findPredecessorCodecId = (codecMessages: CodecMessage<AI.UIMessage>[], domainId: string): string | undefined => {\n const idx = codecMessages.findIndex((p) => p.message.id === domainId);\n if (idx <= 0) return undefined;\n return codecMessages[idx - 1]?.codecMessageId;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/** Internal EventEmitter events map backing the transport's streaming state. */\ninterface ChatTransportEventsMap {\n /** Fired on every streaming-state transition with the new value. */\n streaming: boolean;\n}\n\n/**\n * Create a Vercel ChatTransport from a core ClientSession.\n *\n * Exposes a `streaming` flag and `onStreamingChange` callback so that\n * `useMessageSync` can gate `setMessages` calls during active own-run\n * streams, preventing the push/replace ID mismatch in useChat's `write()`.\n *\n * Note: concurrent `sendMessage` calls from the same user are a useChat\n * limitation that cannot be fixed from the transport layer. The\n * developer must respect useChat's `status` and only call `sendMessage`\n * when status is `'ready'`.\n * @param session - The core client session to wrap.\n * @param chatOptions - Optional hooks for customizing request construction.\n * @returns A {@link ChatTransport} compatible with Vercel's useChat hook.\n */\nexport const createChatTransport = (\n session: ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>,\n chatOptions?: ChatTransportOptions,\n): ChatTransport => {\n // -- Invocation POST config (the transport owns waking the agent) ----------\n const api = chatOptions?.api ?? DEFAULT_VERCEL_API;\n const fetchFn = chatOptions?.fetch ?? globalThis.fetch.bind(globalThis);\n const credentials = chatOptions?.credentials;\n\n // -- Streaming state -------------------------------------------------------\n // Backed by the shared EventEmitter for listener error isolation (one bad\n // onStreamingChange handler can't prevent others from firing or block the\n // state transition) and uniform emitter behaviour across the SDK. The\n // factory takes no logger, so a silent one is used — listener exceptions are\n // swallowed by the emitter rather than surfaced.\n let _streaming = false;\n const emitter = new EventEmitter<ChatTransportEventsMap>(makeLogger({ logLevel: LogLevel.Silent }));\n\n const setStreaming = (value: boolean): void => {\n _streaming = value;\n emitter.emit('streaming', value);\n };\n\n // -- sendMessages implementation -------------------------------------------\n\n const sendMessages: ChatTransport['sendMessages'] = async (opts) => {\n const { messages, abortSignal, trigger, messageId } = opts;\n\n // The visible messages paired with their codec-message-ids. useChat\n // references messages by their domain `message.id`; we match on that to\n // locate a message in the tree, then route every transport operation by\n // the message's codec-message-id (the SDK never correlates on the domain\n // id, which may differ from the codec-message-id).\n const codecMessages = session.view.getMessages();\n const codecIdByDomainId = new Map(codecMessages.map((m) => [m.message.id, m.codecMessageId]));\n const codecIdOf = (domainId: string): string | undefined => codecIdByDomainId.get(domainId);\n\n // useChat calls sendMessages in three distinct modes. We disambiguate\n // by (trigger, last-message role) so each mode dispatches correctly:\n //\n // - 'regenerate-message' → fork an assistant\n // - 'submit-message' + last message is assistant → continuation\n // (auto-submit after\n // addToolResult, or\n // multi-step tool use)\n // - 'submit-message' + last message is user → new user message\n // (or edit if\n // messageId is set)\n //\n // Continuation mode must NOT publish the assistant as a new message or\n // treat messageId as a fork target — useChat v6's sendAutomaticallyWhen\n // path always sets messageId to the last message id regardless.\n const lastMessage = messages.at(-1);\n const lastMessageInTree = !!lastMessage && codecIdByDomainId.has(lastMessage.id);\n const isContinuation = trigger === 'submit-message' && lastMessage?.role === 'assistant' && lastMessageInTree;\n\n // Fork-on-unresolved-tool: user sent a new message while the preceding\n // assistant has an unresolved tool call (approval-requested, input-*).\n // Fork the new message off the preceding assistant so the unresolved\n // tool call stays dormant on a sibling branch. Inference for this run runs\n // on the clean fork — the LLM never sees the dangling tool_use.\n //\n // Only applies to fresh user-message submits (not continuations, not\n // regenerates, not edits-with-messageId).\n //\n // `messages.at(-1)` is the fresh user-prompt being submitted right now;\n // `messages.at(-2)` is therefore the prior assistant whose tool state\n // we need to inspect for the unresolved-tool gate below.\n const precedingMessage =\n trigger === 'submit-message' && !messageId && lastMessage?.role === 'user' ? messages.at(-2) : undefined;\n // The domain id of the preceding assistant when it carries an unresolved\n // tool call and is present in the tree — the new user message forks off it.\n const forkSourceDomainId =\n precedingMessage && hasUnresolvedToolCall(precedingMessage) && codecIdByDomainId.has(precedingMessage.id)\n ? precedingMessage.id\n : undefined;\n\n // Determine the history/messages split based on mode.\n let newMessages: AI.UIMessage[];\n let history: AI.UIMessage[];\n\n if (trigger === 'regenerate-message' || isContinuation) {\n newMessages = [];\n history = messages;\n } else {\n if (messages.length === 0) {\n throw new Ably.ErrorInfo(\n 'unable to send messages; messages array is empty for submit-message trigger',\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n // CAST: length check above guarantees at least one element; .at(-1) cannot be undefined.\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style -- prefer `as` over `!` per TYPES.md\n newMessages = [messages.at(-1) as AI.UIMessage];\n // When forking off an unresolved tool call, drop the unresolved\n // assistant from history too — it belongs on the sibling branch, not\n // the ancestor chain of the new message.\n history = forkSourceDomainId ? messages.slice(0, -2) : messages.slice(0, -1);\n }\n\n // Compute fork metadata for edit (submit-message with messageId) and\n // fork-on-unresolved-tool. Regenerate is NOT precomputed here —\n // `View.regenerate` derives forkOf/parent from the tree itself and\n // overrides anything we'd set.\n let forkOf: string | undefined;\n let parent: string | undefined;\n\n if (trigger === 'submit-message' && messageId && !isContinuation) {\n // Edit: messageId is the domain id of the user message being replaced.\n // forkOf = its codec-message-id, parent = the immediately-preceding\n // codec-message-id in the flat conversation.\n forkOf = codecIdOf(messageId);\n parent = findPredecessorCodecId(codecMessages, messageId);\n } else if (forkSourceDomainId) {\n // Fork off the preceding assistant — the new user message becomes a\n // sibling of the unresolved tool call assistant, rooted at its parent.\n forkOf = codecIdOf(forkSourceDomainId);\n parent = findPredecessorCodecId(codecMessages, forkSourceDomainId);\n }\n\n let sendBody: Record<string, unknown>;\n let sendHeaders: Record<string, string> | undefined;\n\n if (chatOptions?.prepareSendMessagesRequest) {\n const prepared = chatOptions.prepareSendMessagesRequest({\n chatId: opts.chatId,\n trigger,\n messageId,\n history,\n messages: newMessages,\n forkOf,\n parent,\n });\n sendBody = prepared.body ?? {};\n sendHeaders = prepared.headers;\n } else {\n sendBody = {};\n sendHeaders = undefined;\n }\n\n const sendOpts: SendOptions = {};\n if (forkOf !== undefined) sendOpts.forkOf = forkOf;\n if (parent !== undefined) sendOpts.parent = parent;\n // Continuations reuse the suspended assistant's runId so the agent's\n // existing run resumes under a fresh invocation rather than spinning\n // up a brand-new run. `isContinuation` implies `lastMessage` is defined.\n if (isContinuation) {\n // `isContinuation` implies `lastMessage` is defined (it gates on\n // `lastMessage?.role`). Route the runId lookup by codec-message-id.\n const codecId = codecIdOf(lastMessage.id);\n const run = codecId === undefined ? undefined : session.view.runOf(codecId);\n if (run) sendOpts.runId = run.runId;\n }\n\n // Dispatch by mode:\n //\n // - Continuation: derive tool-resolution events from useChat's overlay\n // vs the tree and pair each with the prior assistant's tree codec-message-id —\n // the SDK stamps the wire's `codec-message-id` to that id so the\n // reducer's direct fold path runs (no redirect, no consume).\n // - Regenerate: route through `view.regenerate`. The View mints a\n // wire-only regenerate event (`ait-regenerate`) carrying\n // `forkOf=A1` / `parent=U1` on transport headers. U1 is NOT\n // republished — A1 and A2 group as tree siblings under U1 via the\n // existing forkOf machinery. The LLM receives the truncated history\n // through U1 inclusive via the body.\n // - Fresh send / edit: publish the new user-message input(s) via\n // `view.send`.\n let run: ActiveRun;\n if (isContinuation) {\n const inputs = deriveContinuationInputs(codecMessages, messages);\n run = await session.view.send(inputs, sendOpts);\n } else if (trigger === 'regenerate-message') {\n if (messageId === undefined) {\n throw new Ably.ErrorInfo(\n 'unable to regenerate; regenerate-message trigger fired without messageId',\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n // useChat passes the assistant's domain id; route by its codec-message-id.\n const regenCodecId = codecIdOf(messageId);\n if (regenCodecId === undefined) {\n throw new Ably.ErrorInfo(\n `unable to regenerate; message not visible: ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n run = await session.view.regenerate(regenCodecId, sendOpts);\n } else {\n const inputs = newMessages.map((m) => UIMessageCodec.createUserMessage(m));\n run = await session.view.send(inputs, sendOpts);\n }\n\n // Build the consumer-facing stream from the Tree's events for this run.\n // Streaming is a useChat concern owned by the Vercel layer; the core\n // session exposes no per-run stream. Key it on\n // `run.inputCodecMessageId` — the triggering input's codec-message-id, which\n // the client owns from send time and the agent echoes as\n // `input-codec-message-id`. The agent mints the runId, supplied as\n // `run.runId` (a promise) for the run-end safety-net.\n const runStream = createRunOutputStream(session, run.runId, run.inputCodecMessageId);\n\n if (abortSignal) {\n const onAbort = (): void => {\n // Best-effort cancel via the run handle (knows its own key / runId);\n // the core resolves the runId once the agent mints it.\n void run.cancel();\n // Close the consumer stream immediately so useChat's reader ends\n // without waiting for the agent's run-end round-trip.\n runStream.close();\n };\n // useChat sets `status: 'submitted'` synchronously inside `makeRequest`\n // BEFORE awaiting `transport.sendMessages`. That immediately enables\n // the Stop button in the UI. If the user clicks Stop while\n // `session.view.send` is still awaiting the run-start ack (which\n // can take seconds for a real LLM), useChat aborts the signal before\n // we ever get here. `addEventListener('abort', ...)` does not fire\n // for an already-aborted signal, so we'd silently lose the cancel\n // and the agent would keep streaming.\n if (abortSignal.aborted) {\n onAbort();\n } else {\n abortSignal.addEventListener('abort', onAbort, { once: true });\n }\n }\n\n // Wrap the stream to detect completion. The streaming flag gates\n // useMessageSync so that setMessages doesn't interfere with\n // useChat's internal write() during active streams.\n const { stream, done, fail } = wrapStreamWithDone(runStream.stream);\n setStreaming(true);\n\n // Fire-and-forget: clear the streaming flag when the stream ends.\n void done.then(() => {\n setStreaming(false);\n });\n\n // Wake the agent: POST the invocation pointer to the configured endpoint.\n // useChat's transport contract is request-driven, so the transport owns\n // this POST (the core session is HTTP-free). Fire-and-forget — `await`\n // would delay the stream return, and the agent's response arrives over\n // the Ably channel, not the HTTP response. The run's invocation\n // identifiers always win over any custom body so the agent can parse it\n // via Invocation.fromJSON. A failed POST means the agent never woke, so\n // error the useChat-facing stream; the core run and observers are\n // untouched.\n const postBody = { ...sendBody, ...run.toInvocation().toJSON() };\n fetchFn(api, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...sendHeaders },\n body: JSON.stringify(postBody),\n ...(credentials ? { credentials } : {}),\n })\n .then((response) => {\n if (!response.ok) {\n fail(\n new Ably.ErrorInfo(\n `unable to send; HTTP POST to ${api} returned ${String(response.status)} ${response.statusText}`,\n ErrorCode.SessionSendFailed,\n response.status,\n ),\n );\n }\n })\n .catch((error: unknown) => {\n const cause = error instanceof Ably.ErrorInfo ? error : undefined;\n fail(\n new Ably.ErrorInfo(\n `unable to send; HTTP POST to ${api} failed: ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.SessionSendFailed,\n 500,\n cause,\n ),\n );\n });\n\n return stream;\n };\n\n return {\n sendMessages,\n\n // Observer mode handles in-progress streams automatically.\n // The transport subscribes before attach — on the next server append,\n // observer accumulation emits lifecycle events that useMessageSync\n // upserts into React state.\n // eslint-disable-next-line unicorn/no-null, @typescript-eslint/promise-function-async -- null is required by the AI SDK ChatTransport contract; no await needed\n reconnectToStream: () => Promise.resolve(null),\n\n close: async () => session.close(),\n\n get streaming(): boolean {\n return _streaming;\n },\n\n onStreamingChange: (callback: (streaming: boolean) => void): (() => void) => {\n emitter.on('streaming', callback);\n return () => {\n emitter.off('streaming', callback);\n };\n },\n };\n};\n","/** SDK version. Kept in sync with `package.json` by the `/release` workflow. */\nexport const VERSION = '0.2.0';\n","/**\n * Wraps the two paths chat-js uses (see ChatClient._addAgent): the\n * `options.agents` mutation (read by ably-js when opening the initial\n * WebSocket) and the `params.agent` channel option (sent on ATTACH so\n * an already-open connection still carries the identifier).\n *\n * `options.agents` is a private API on the Realtime client — no public\n * typed accessor exists in the `ably` package — so this module casts to a\n * `RealtimeWithOptions` shape to write it.\n */\n\nimport type * as Ably from 'ably';\n\nimport { VERSION } from '../version.js';\n\ninterface RealtimeWithOptions extends Ably.Realtime {\n options: { agents?: Record<string, string | undefined> };\n}\n\nconst SDK_NAME = 'ai-transport-js';\n\n/** Internal shape a codec may carry to opt into Ably-Agent header registration. */\ninterface AdapterTagHolder {\n readonly adapterTag?: string;\n}\n\n/**\n * Merge `agents` into `client.options.agents` and return the space-separated\n * `params.agent` string for channel ATTACH.\n * @param client - The Ably Realtime client to mutate.\n * @param agents - Map of agent-name to version strings to register.\n * @returns Channel options containing `params.agent` for `channels.get`.\n */\nconst injectAgents = (\n client: Ably.Realtime,\n // CAST: Ably.Realtime's public type omits `options.agents`, but the SDK\n // does carry it at runtime. ably-chat-js relies on the same shape — see\n // ChatClient._addAgent in https://github.com/ably/ably-chat-js.\n agents: Record<string, string>,\n): { params: { agent: string } } => {\n const realtime = client as RealtimeWithOptions;\n realtime.options.agents = { ...realtime.options.agents, ...agents };\n const agentString = Object.entries(agents)\n .map(([name, version]) => `${name}/${version}`)\n .join(' ');\n return { params: { agent: agentString } };\n};\n\n/**\n * Register this SDK (and optionally a codec) on the supplied Realtime client\n * and return the channel options the caller should pass to\n * `client.channels.get(...)` so the agent is also carried on channel ATTACH.\n * Sets `options.agents['ai-transport-js'] = VERSION`. When the codec carries\n * an internal `adapterTag` field (via {@link AdapterTagHolder}), also sets\n * `options.agents[adapterTag] = VERSION`.\n * Idempotent — repeated calls with the same client and codec produce the same keys/values.\n * Spec: AIT-CT1a, AIT-CT1a2, AIT-CT1a3, AIT-ST1a, AIT-ST1a2, AIT-ST1a3.\n * @param client - The Ably Realtime client to register on.\n * @param codec - The codec instance; cast to {@link AdapterTagHolder} to detect an optional identifier.\n * @returns Channel options containing `params.agent` for `channels.get`.\n */\nexport const registerAgent = (client: Ably.Realtime, codec?: unknown): { params: { agent: string } } => {\n // CAST: AdapterTagHolder is an internal opt-in shape — not part of the public Codec interface.\n const adapterTag = (codec as AdapterTagHolder | undefined)?.adapterTag;\n const agents: Record<string, string> = { [SDK_NAME]: VERSION };\n if (adapterTag) agents[adapterTag] = VERSION;\n return injectAgents(client, agents);\n};\n","/**\n * Transport header builder.\n *\n * Single source of truth for which transport headers every transport\n * message carries. Used by the agent session (pipe, addEvents) and by\n * the client session (optimistic message stamping).\n */\n\nimport {\n EVENT_RUN_END,\n EVENT_RUN_RESUME,\n EVENT_RUN_START,\n EVENT_RUN_SUSPEND,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_EVENT_ID,\n HEADER_FORK_OF,\n HEADER_INPUT_CLIENT_ID,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_INVOCATION_ID,\n HEADER_MSG_REGENERATE,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_RUN_CLIENT_ID,\n HEADER_RUN_ID,\n HEADER_RUN_REASON,\n} from '../../constants.js';\nimport type { RunEndReason, RunLifecycleEvent } from './types.js';\n\n/**\n * Build the standard transport header set for a message.\n * @param opts - The header values to include.\n * @param opts.role - Message role (e.g. \"user\", \"assistant\").\n * @param opts.runId - Run correlation ID, or `undefined` for a fresh client\n * input (the agent mints run-ids, so it is not known synchronously). Omitted\n * from the headers when undefined; a continuation still carries the known run-id.\n * @param opts.codecMessageId - Message identity — the wire `codec-message-id` for this message.\n * @param opts.runClientId - ClientId of the run initiator.\n * @param opts.parent - Preceding message's codec-message-id (for branching).\n * @param opts.forkOf - Forked user-prompt's codec-message-id (for edits — creates a Run-level fork sibling).\n * @param opts.regenerates - Assistant codec-message-id this run regenerates. Stamps\n * `msg-regenerate`. Distinct from `forkOf`: regenerate is a\n * continuation of the prior run (no Run-level fork), with the message\n * replacement resolved at projection extraction time.\n * @param opts.invocationId - Agent-minted invocation id. Stamped by the agent on every event it publishes for the invocation (run lifecycle + outputs) so the client can observe it; not set by the client on the input.\n * @param opts.inputClientId - ClientId of the input event (the `ai-input`) that\n * drove the current invocation. The agent reads it from the publisher's\n * Ably-level `clientId` on the matched input event and re-stamps it on its\n * own publishes (run lifecycle + outputs). Differs from `runClientId` on\n * continuation invocations driven by an input from a non-owner.\n * @param opts.inputEventId - Per-event identifier. Set on each client-published user-prompt message; the invocation body's `inputEventIds` lists the ids the agent should look up.\n * @param opts.inputCodecMessageId - The codec-message-id of the input event that\n * triggered the current invocation (the one whose `event-id` matched the\n * invocation's `inputEventId`). The agent re-stamps it on every event it\n * publishes for the invocation (run lifecycle + outputs), mirroring\n * `inputClientId`, so the client can correlate any of those events back to\n * the originating input by the id it owned at send time.\n * @returns A headers record with the transport headers set.\n */\nexport const buildTransportHeaders = (opts: {\n role: string;\n runId?: string;\n codecMessageId: string;\n runClientId?: string;\n parent?: string;\n forkOf?: string;\n regenerates?: string;\n invocationId?: string;\n inputClientId?: string;\n inputCodecMessageId?: string;\n inputEventId?: string;\n}): Record<string, string> => {\n const h: Record<string, string> = {\n [HEADER_ROLE]: opts.role,\n [HEADER_CODEC_MESSAGE_ID]: opts.codecMessageId,\n };\n if (opts.runId !== undefined) h[HEADER_RUN_ID] = opts.runId;\n if (opts.runClientId !== undefined) h[HEADER_RUN_CLIENT_ID] = opts.runClientId;\n if (opts.parent) h[HEADER_PARENT] = opts.parent;\n if (opts.forkOf) h[HEADER_FORK_OF] = opts.forkOf;\n if (opts.regenerates) h[HEADER_MSG_REGENERATE] = opts.regenerates;\n if (opts.invocationId) h[HEADER_INVOCATION_ID] = opts.invocationId;\n if (opts.inputClientId !== undefined) h[HEADER_INPUT_CLIENT_ID] = opts.inputClientId;\n if (opts.inputCodecMessageId !== undefined) h[HEADER_INPUT_CODEC_MESSAGE_ID] = opts.inputCodecMessageId;\n if (opts.inputEventId) h[HEADER_EVENT_ID] = opts.inputEventId;\n return h;\n};\n\n/**\n * Build the transport header set for a run-lifecycle event (run-start,\n * run-resume, run-suspend, run-end). Single source of truth for lifecycle\n * header stamping, mirroring {@link buildTransportHeaders} for the\n * message-carrier path. Every field except `runId`/`runClientId` is optional\n * and omitted when not provided.\n *\n * A resume suppresses the structural `parent` / `forkOf` / `regenerates`\n * headers — the caller passes them only for a fresh run-start. `reason` is\n * stamped only on run-end.\n * @param opts - The lifecycle header values to include.\n * @param opts.runId - The run's id.\n * @param opts.runClientId - ClientId of the run initiator (empty string when unknown).\n * @param opts.parent - Structural parent codec-message-id (fresh run-start only).\n * @param opts.forkOf - Forked user-prompt codec-message-id (fresh run-start only).\n * @param opts.regenerates - Regenerated assistant codec-message-id (fresh run-start only).\n * @param opts.invocationId - Agent-minted invocation id; carried on every lifecycle event.\n * @param opts.inputClientId - ClientId of the triggering input event.\n * @param opts.inputCodecMessageId - Codec-message-id of the triggering input event.\n * @param opts.reason - Terminal reason; stamped on run-end only.\n * @returns A headers record with the lifecycle headers set.\n */\nexport const buildLifecycleHeaders = (opts: {\n runId: string;\n runClientId: string;\n parent?: string;\n forkOf?: string;\n regenerates?: string;\n invocationId?: string;\n inputClientId?: string;\n inputCodecMessageId?: string;\n reason?: RunEndReason;\n}): Record<string, string> => {\n const h: Record<string, string> = {\n [HEADER_RUN_ID]: opts.runId,\n [HEADER_RUN_CLIENT_ID]: opts.runClientId,\n };\n if (opts.reason !== undefined) h[HEADER_RUN_REASON] = opts.reason;\n if (opts.parent !== undefined) h[HEADER_PARENT] = opts.parent;\n if (opts.forkOf !== undefined) h[HEADER_FORK_OF] = opts.forkOf;\n if (opts.regenerates !== undefined) h[HEADER_MSG_REGENERATE] = opts.regenerates;\n if (opts.invocationId !== undefined) h[HEADER_INVOCATION_ID] = opts.invocationId;\n if (opts.inputClientId !== undefined) h[HEADER_INPUT_CLIENT_ID] = opts.inputClientId;\n if (opts.inputCodecMessageId !== undefined) h[HEADER_INPUT_CODEC_MESSAGE_ID] = opts.inputCodecMessageId;\n return h;\n};\n\n/** The four run-lifecycle Ably message names. */\ntype RunLifecycleName =\n | typeof EVENT_RUN_START\n | typeof EVENT_RUN_SUSPEND\n | typeof EVENT_RUN_RESUME\n | typeof EVENT_RUN_END;\n\n/**\n * Whether an Ably message `name` is one of the run-lifecycle event names\n * (run-start / run-suspend / run-resume / run-end). Single source of truth for\n * the classification both decode loops and the agent's history scan use to\n * route lifecycle wires away from the codec decoder. Narrows `name` to a\n * lifecycle name so callers can pass it straight to {@link parseRunLifecycle}.\n * @param name - The inbound Ably message `name`, or undefined.\n * @returns True when `name` is a run-lifecycle event name.\n */\nexport const isRunLifecycleName = (name: string | undefined): name is RunLifecycleName =>\n name === EVENT_RUN_START || name === EVENT_RUN_SUSPEND || name === EVENT_RUN_RESUME || name === EVENT_RUN_END;\n\n/**\n * Parse an inbound run-lifecycle Ably message into a {@link RunLifecycleEvent}.\n *\n * Single source of truth for turning the wire run-lifecycle message `name`,\n * transport headers, and channel serial into the structured lifecycle event\n * the Tree consumes. Used by the client decode loop (live) and the View's\n * history replay so both build the event identically.\n * @param name - The inbound Ably message `name`.\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial of the message, or `undefined` for an\n * optimistic local event. Stamped onto the returned event.\n * @returns The lifecycle event, or `undefined` when `name` is not a\n * run-lifecycle event name or the message carries no `run-id`.\n */\nexport const parseRunLifecycle = (\n name: string,\n headers: Record<string, string>,\n serial: string | undefined,\n): RunLifecycleEvent | undefined => {\n const runId = headers[HEADER_RUN_ID];\n if (!runId) return undefined;\n\n const clientId = headers[HEADER_RUN_CLIENT_ID] ?? '';\n\n if (name === EVENT_RUN_START) {\n const parent = headers[HEADER_PARENT];\n const forkOf = headers[HEADER_FORK_OF];\n const regenerates = headers[HEADER_MSG_REGENERATE];\n return {\n type: 'start',\n runId,\n clientId,\n serial,\n invocationId: headers[HEADER_INVOCATION_ID] ?? '',\n ...(parent !== undefined && { parent }),\n ...(forkOf !== undefined && { forkOf }),\n ...(regenerates !== undefined && { regenerates }),\n };\n }\n\n if (name === EVENT_RUN_SUSPEND) {\n return { type: 'suspend', runId, clientId, serial, invocationId: headers[HEADER_INVOCATION_ID] ?? '' };\n }\n\n if (name === EVENT_RUN_RESUME) {\n return { type: 'resume', runId, clientId, serial, invocationId: headers[HEADER_INVOCATION_ID] ?? '' };\n }\n\n if (name === EVENT_RUN_END) {\n // CAST: agent always writes a valid RunEndReason; default to 'complete' for robustness.\n const reason = (headers[HEADER_RUN_REASON] ?? 'complete') as RunEndReason;\n return { type: 'end', runId, clientId, serial, invocationId: headers[HEADER_INVOCATION_ID] ?? '', reason };\n }\n\n return undefined;\n};\n","/**\n * Helpers for FIFO-bounded `Map`s — Maps used as capacity-limited buffers that\n * must evict their oldest entry when full.\n */\n\n/**\n * Make room for a new key in a FIFO-bounded map: if `map` is at (or over)\n * `limit` and does not already contain `key`, evict the oldest entry (insertion\n * order) and return its key so the caller can log the eviction. Returns\n * `undefined` when nothing was evicted (the key already exists, the map is\n * below the limit, or it is empty).\n *\n * The caller performs the actual set/append afterwards — this only frees a\n * slot — so it works for maps whose values are replaced and for maps whose\n * values are appended-to lists.\n * @param map - The bounded map to evict from.\n * @param key - The key about to be added; an existing key never evicts.\n * @param limit - The maximum number of entries the map may hold.\n * @returns The evicted key, or `undefined` if nothing was evicted.\n */\nexport const evictOldestIfFull = <K, V>(map: Map<K, V>, key: K, limit: number): K | undefined => {\n if (map.has(key) || map.size < limit) return undefined;\n const oldest = map.keys().next().value;\n if (oldest === undefined) return undefined;\n map.delete(oldest);\n return oldest;\n};\n","/**\n * buildBranchChain — order a single conversation branch by walking\n * codec-message-id parent links upward from an anchor node to the root.\n *\n * This is the shared ordering spine of the agent's conversation\n * reconstruction and of history decode: both need the same root→anchor\n * sequence of nodes before folding each node's projection. Keeping the walk\n * here — pure, with no codec, no I/O, no logger — lets it be proven in\n * isolation and reused by both engines without drift.\n *\n * Branch selection is implicit: a node reaches only its own ancestors via\n * `parentCodecMessageId`, so sibling branches (edits / regenerates that the\n * anchor did not descend from) are never visited. There is no separate\n * fork/regenerate filtering step — the un-taken sibling is simply unreachable.\n */\n\n/**\n * The single field {@link buildBranchChain} reads from a node. Richer node-meta\n * shapes (carrying run-id, fork-of, regenerates, …) satisfy this structurally,\n * so callers can pass their full index map directly.\n */\nexport interface BranchChainNode {\n /**\n * Codec-message-id of this node's structural parent — the node it hangs off\n * — or `undefined` for a root node. This is the only edge the walk follows.\n */\n parentCodecMessageId: string | undefined;\n}\n\n/**\n * Walk `parentCodecMessageId` links upward from `anchorCodecMessageId` and\n * return the branch it sits on, ordered root-first (oldest) to anchor (newest,\n * last). The anchor is always the final element.\n *\n * The walk stops at the root (a node with no parent), at a dangling parent\n * (a parent id absent from `nodeMeta` is still included as the chain head,\n * then the walk ends), or on revisiting a node (a cycle in malformed data is\n * broken best-effort rather than looping forever).\n * @param nodeMeta - Lookup from codec-message-id to its node meta. Need not\n * contain the anchor or every ancestor; missing entries simply end the walk.\n * @param anchorCodecMessageId - The codec-message-id to start the walk from\n * (the newest node on the branch; included in the result).\n * @returns The branch's codec-message-ids ordered root-first to anchor-last.\n */\nexport const buildBranchChain = (\n nodeMeta: ReadonlyMap<string, BranchChainNode>,\n anchorCodecMessageId: string,\n): string[] => {\n const chain: string[] = [];\n const seen = new Set<string>();\n let current: string | undefined = anchorCodecMessageId;\n while (current !== undefined && !seen.has(current)) {\n seen.add(current);\n chain.push(current);\n current = nodeMeta.get(current)?.parentCodecMessageId;\n }\n return chain.toReversed();\n};\n","/**\n * Shared wire decode-and-apply engine.\n *\n * The client's live decode loop and the View's history replay both reconstruct\n * the conversation Tree from the same raw Ably wire log. This module is the one\n * place that classifies a wire message (run-lifecycle vs codec-decoded), parses\n * or decodes it, and applies it to the Tree — so the two paths can never drift.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_RUN_ID } from '../../constants.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport type { Codec, CodecInputEvent, CodecOutputEvent, Decoder } from '../codec/types.js';\nimport { isRunLifecycleName, parseRunLifecycle } from './headers.js';\nimport type { TreeInternal } from './tree.js';\nimport type { RunLifecycleEvent } from './types.js';\n\n/**\n * Apply one inbound wire message to the tree.\n *\n * Run-lifecycle messages are turned into a {@link RunLifecycleEvent} via\n * {@link parseRunLifecycle} and applied with `applyRunLifecycle`; everything\n * else is decoded with `decoder` and applied with `applyMessage`, skipping\n * wire-only carriers that decode to no events and carry no run-id (the eventual\n * reply run is created later by its run-start).\n *\n * Does NOT emit the tree's `ably-message` event — the caller owns that, because\n * the live loop emits per message while history replay emits in a batch once\n * the whole page is applied. Returns the parsed lifecycle event so a live\n * caller can run its own side-effects (resolving a pending run-start,\n * surfacing an agent error); returns `undefined` for a codec-decoded message\n * or a lifecycle message that carried no run-id.\n * @param tree - The tree to apply the message to.\n * @param decoder - The codec decoder used for non-lifecycle messages.\n * @param rawMsg - The inbound Ably wire message.\n * @returns The parsed run-lifecycle event, or `undefined`.\n */\nexport const applyWireMessage = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(\n tree: TreeInternal<TInput, TOutput, TProjection>,\n decoder: Decoder<TInput, TOutput>,\n rawMsg: Ably.InboundMessage,\n): RunLifecycleEvent | undefined => {\n const headers = getTransportHeaders(rawMsg);\n const serial = rawMsg.serial;\n\n if (isRunLifecycleName(rawMsg.name)) {\n const event = parseRunLifecycle(rawMsg.name, headers, serial);\n if (event) tree.applyRunLifecycle(event);\n return event;\n }\n\n const { inputs, outputs } = decoder.decode(rawMsg);\n if (inputs.length > 0 || outputs.length > 0 || headers[HEADER_RUN_ID]) {\n tree.applyMessage({ inputs, outputs }, headers, serial);\n }\n return undefined;\n};\n\n/**\n * Decode one wire message with `decoder` and fold its events into `projection`,\n * returning the updated projection. Unlike {@link applyWireMessage} this builds\n * a standalone projection rather than applying to a tree — used by the agent's\n * conversation reconstruction. The caller owns the decoder so its streaming\n * state can span the messages of a run, and chooses the reducer routing key.\n * @param codec - The codec whose inherited Reducer `fold` method folds each decoded event into the projection.\n * @param decoder - The caller-owned codec decoder (reused across a run's wires).\n * @param projection - The projection to fold the message's events into.\n * @param rawMsg - The wire message to decode and fold.\n * @param messageId - The reducer routing key (codec-message-id) for this message.\n * @returns The projection after folding all of the message's decoded events.\n */\nexport const foldMessageInto = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n codec: Codec<TInput, TOutput, TProjection, TMessage>,\n decoder: Decoder<TInput, TOutput>,\n projection: TProjection,\n rawMsg: Ably.InboundMessage,\n messageId: string,\n): TProjection => {\n const { inputs, outputs } = decoder.decode(rawMsg);\n let next = projection;\n for (const event of [...inputs, ...outputs]) {\n next = codec.fold(next, event, { serial: rawMsg.serial ?? '', messageId });\n }\n return next;\n};\n","/**\n * Agent-side conversation reconstruction from the channel wire log.\n *\n * When an agent wakes (or resumes) it has no in-memory tree — it rebuilds the\n * state it needs by paging channel history and folding the wires through the\n * codec. Two entry points:\n *\n * - {@link loadRunProjection} — fold a single run's wires into one projection\n * (used to resume a suspended run with its client tool-output amends).\n * - {@link loadConversation} — walk the structural parent chain from the\n * current run's input node to the root and fold each node, producing the full\n * multi-turn message history along the taken branch.\n *\n * Both reuse {@link foldMessageInto} (the shared per-message fold primitive) and\n * {@link buildBranchChain} (the shared parent-chain walk), so the agent's\n * reconstruction can't drift from the client/View decode paths.\n */\n\nimport * as Ably from 'ably';\n\nimport { EVENT_RUN_START, HEADER_CODEC_MESSAGE_ID, HEADER_PARENT, HEADER_RUN_ID } from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport { compareBySerial, getTransportHeaders } from '../../utils.js';\nimport type { Codec, CodecInputEvent, CodecOutputEvent } from '../codec/types.js';\nimport type { BranchChainNode } from './branch-chain.js';\nimport { buildBranchChain } from './branch-chain.js';\nimport { foldMessageInto } from './decode-fold.js';\nimport { isRunLifecycleName } from './headers.js';\n\n// ---------------------------------------------------------------------------\n// History collection + dedup\n// ---------------------------------------------------------------------------\n\n/**\n * Merge messages observed live (e.g. by the input-event lookup) into a set of\n * collected history messages, dedup by serial, and sort chronologically.\n *\n * History messages take priority in deduplication (history serial wins if the\n * same message appears in both). Messages without a serial are dropped because\n * they cannot be reliably ordered.\n * @param collected - Raw messages from channel.history (any order).\n * @param live - Messages observed live (e.g. by the input-event lookup); may be undefined.\n * @returns Deduplicated, chronologically sorted messages.\n */\nexport const withLiveMessages = (\n collected: readonly Ably.InboundMessage[],\n live?: readonly Ably.InboundMessage[],\n): Ably.InboundMessage[] => {\n const seen = new Set<string>();\n const result: Ably.InboundMessage[] = [];\n for (const msg of collected) {\n if (msg.serial !== undefined && !seen.has(msg.serial)) {\n seen.add(msg.serial);\n result.push(msg);\n }\n }\n if (live !== undefined) {\n for (const msg of live) {\n if (msg.serial !== undefined && !seen.has(msg.serial)) {\n seen.add(msg.serial);\n result.push(msg);\n }\n }\n }\n return result.toSorted(compareBySerial);\n};\n\n/**\n * Page through a channel's history and collect raw messages, bounded so a\n * long-lived channel can't exhaust memory. No `untilAttach` — callers need\n * messages published after the channel first attached (e.g. client tool-output\n * amends on a suspended run).\n * @param channel - The Ably channel to read history from.\n * @param pageLimit - Messages requested per history page.\n * @param maxMessages - Stop paging once this many messages are collected.\n * @returns The collected messages in history order (newest first per Ably).\n */\nconst collectHistory = async (\n channel: Ably.RealtimeChannel,\n pageLimit: number,\n maxMessages: number,\n): Promise<Ably.InboundMessage[]> => {\n const collected: Ably.InboundMessage[] = [];\n let page = await channel.history({ limit: pageLimit });\n collected.push(...page.items);\n while (page.hasNext() && collected.length < maxMessages) {\n const nextPage: Ably.PaginatedResult<Ably.InboundMessage> | null = await page.next();\n if (!nextPage) break;\n collected.push(...nextPage.items);\n page = nextPage;\n }\n return collected;\n};\n\n// ---------------------------------------------------------------------------\n// Per-node folds\n// ---------------------------------------------------------------------------\n\n/**\n * Fold a pre-sorted array of wire messages for a single run into a projection.\n *\n * Skips lifecycle events (they carry no codec content) and stops before the\n * message whose `codec-message-id` equals `truncateAt` (exclusive — that\n * message is not folded). Used by both {@link loadRunProjection} (no\n * truncation) and {@link loadConversation} (per-ancestor folding).\n * @param codec - Codec used to decode and fold events.\n * @param sortedMessages - Chronologically ordered wire messages (all runs).\n * @param runId - Only messages stamped with this run-id are folded.\n * @param truncateAt - Stop before this codec-message-id; omit to fold all messages.\n * @returns The projection and the count of messages that were folded.\n */\nexport const foldRunMessages = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n codec: Codec<TInput, TOutput, TProjection, TMessage>,\n sortedMessages: readonly Ably.InboundMessage[],\n runId: string,\n truncateAt?: string,\n): { projection: TProjection; folded: number } => {\n const decoder = codec.createDecoder();\n let projection = codec.init();\n let folded = 0;\n for (const msg of sortedMessages) {\n const h = getTransportHeaders(msg);\n if (h[HEADER_RUN_ID] !== runId) continue;\n if (isRunLifecycleName(msg.name)) continue;\n const codecMsgId = h[HEADER_CODEC_MESSAGE_ID];\n if (truncateAt !== undefined && codecMsgId === truncateAt) break;\n projection = foldMessageInto(codec, decoder, projection, msg, codecMsgId ?? '');\n folded++;\n }\n return { projection, folded };\n};\n\n/**\n * Fold a single run-less INPUT node's events into a fresh projection: every\n * wire stamped with `codecMessageId` and NO run-id (the user prompt the client\n * published before the agent minted a run-id). The two-node analogue of\n * {@link foldRunMessages} for the user-input side of the conversation chain.\n * @param codec - Codec used to decode and fold events.\n * @param sortedMessages - Chronologically ordered wire messages (all runs).\n * @param codecMessageId - The input node's codec-message-id.\n * @returns The folded projection for that input node.\n */\nexport const foldInputMessages = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n codec: Codec<TInput, TOutput, TProjection, TMessage>,\n sortedMessages: readonly Ably.InboundMessage[],\n codecMessageId: string,\n): TProjection => {\n const decoder = codec.createDecoder();\n let projection = codec.init();\n for (const msg of sortedMessages) {\n const h = getTransportHeaders(msg);\n if (h[HEADER_RUN_ID] !== undefined) continue;\n if (h[HEADER_CODEC_MESSAGE_ID] !== codecMessageId) continue;\n projection = foldMessageInto(codec, decoder, projection, msg, codecMessageId);\n }\n return projection;\n};\n\n// ---------------------------------------------------------------------------\n// Run-state reconstruction\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch all messages on the channel that belong to `runId`, decode them\n * through the codec, and fold them into a single projection. Used by the agent\n * to reconstruct a run's full state — including client-published tool-output\n * amends — when resuming a suspended run in a fresh agent session.\n *\n * Doesn't require channel rewind: an explicit `channel.history()` call returns\n * the same data even if the channel is already attached from a prior session.\n * @param opts - Load parameters.\n * @param opts.channel - The Ably channel to read history from.\n * @param opts.codec - Codec used to decode and fold events.\n * @param opts.runId - Run identifier whose events should be folded.\n * @param opts.signal - AbortSignal checked once at entry: if already aborted the call throws immediately and no history is fetched. It does not interrupt an in-flight load.\n * @param opts.logger - Optional logger for diagnostic output.\n * @param opts.liveMessages - Raw Ably messages already observed live (e.g. by\n * the input-event lookup). Folded alongside the history fetch so just-published\n * client wires don't depend on Ably's history-indexing window.\n * @returns The projection produced by folding all run events in serial order.\n * @throws {Ably.ErrorInfo} with code ErrorCode.InvalidArgument if `signal` is already aborted at entry (the run was cancelled before loading began).\n */\nexport const loadRunProjection = async <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(opts: {\n channel: Ably.RealtimeChannel;\n codec: Codec<TInput, TOutput, TProjection, TMessage>;\n runId: string;\n signal: AbortSignal;\n logger: Logger | undefined;\n liveMessages?: readonly Ably.InboundMessage[];\n}): Promise<TProjection> => {\n const { channel, codec, runId, signal, logger, liveMessages } = opts;\n\n if (signal.aborted) {\n throw new Ably.ErrorInfo(\n `unable to load run projection; run ${runId} was cancelled`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n await channel.attach();\n\n // 2000 wire messages is generously more than any single run could produce.\n const collected = await collectHistory(channel, 200, 2000);\n\n const sorted = withLiveMessages(collected, liveMessages);\n const { projection, folded } = foldRunMessages(codec, sorted, runId);\n\n logger?.debug('loadRunProjection(); folded run events', { runId, folded });\n return projection;\n};\n\n/** A node in the reconstruction index — {@link BranchChainNode} plus its run-id. */\ninterface NodeMeta extends BranchChainNode {\n /** The run-id this node belongs to, or undefined for a run-less input node. */\n runId: string | undefined;\n}\n\n/**\n * Reconstruct the full multi-turn conversation history along the branch the\n * current run sits on.\n *\n * Pages channel history (merging live lookup messages), indexes each\n * codec-message-id's structural parent and run-id with sticky identity (the\n * first wire wins; later amends can't poison it), backfills a reply run's\n * parent from its `ai-run-start` when the output wire wasn't indexed, then\n * walks the structural parent chain from the current run's input node\n * (`assistantParentFallback`) to the root and folds each node in chain order.\n * The current run is folded once, wholesale, at the tail.\n * @param opts - Reconstruction parameters.\n * @param opts.channel - The Ably channel to read history from.\n * @param opts.codec - Codec used to decode and fold events.\n * @param opts.runId - The current run's id.\n * @param opts.signal - AbortSignal checked once at entry; if already aborted the call throws before any history is fetched. It does not interrupt an in-flight load.\n * @param opts.logger - Optional logger for diagnostic output.\n * @param opts.liveMessages - Wires already observed live, merged into history.\n * @param opts.assistantParentFallback - The current run's input node\n * (codec-message-id) — the anchor the parent-chain walk starts from. When\n * undefined, only the current run is folded.\n * @param opts.pageLimit - Messages requested per history page.\n * @param opts.maxMessages - Stop paging once this many messages are collected.\n * @returns The branch's messages (root-first) and the current run's projection.\n * @throws {Ably.ErrorInfo} with code ErrorCode.InvalidArgument when `signal` is already aborted at entry.\n */\nexport const loadConversation = async <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(opts: {\n channel: Ably.RealtimeChannel;\n codec: Codec<TInput, TOutput, TProjection, TMessage>;\n runId: string;\n signal: AbortSignal;\n logger: Logger | undefined;\n liveMessages: readonly Ably.InboundMessage[] | undefined;\n assistantParentFallback: string | undefined;\n pageLimit: number;\n maxMessages: number;\n}): Promise<{ messages: TMessage[]; projection: TProjection }> => {\n const { channel, codec, runId, signal, logger, liveMessages, assistantParentFallback, pageLimit, maxMessages } = opts;\n\n if (signal.aborted) {\n throw new Ably.ErrorInfo(`unable to load conversation; run ${runId} was cancelled`, ErrorCode.InvalidArgument, 400);\n }\n\n // Single channel.history() fetch for all runs. Live lookup messages are\n // merged in so the current run's just-published client wires don't depend on\n // Ably's history-indexing window. Deduped by serial (history wins), sorted.\n const collected = await collectHistory(channel, pageLimit, maxMessages);\n const sortedMessages = withLiveMessages(collected, liveMessages);\n\n // Index pass — node metadata per codec-message-id from the serial-sorted\n // history, with sticky identity (the first wire for a codec-message-id wins\n // for the structural parent; a later amend can't poison it). Run-bearing\n // wires record their runId; run-less user inputs are input nodes (runId\n // undefined).\n const nodeMeta = new Map<string, NodeMeta>();\n const runIdToCodecMessageId = new Map<string, string>();\n for (const msg of sortedMessages) {\n if (isRunLifecycleName(msg.name)) continue;\n const h = getTransportHeaders(msg);\n const cid = h[HEADER_CODEC_MESSAGE_ID];\n if (cid === undefined) continue;\n const msgRunId = h[HEADER_RUN_ID];\n if (msgRunId !== undefined) runIdToCodecMessageId.set(msgRunId, cid);\n if (!nodeMeta.has(cid)) {\n nodeMeta.set(cid, { runId: msgRunId, parentCodecMessageId: h[HEADER_PARENT] });\n }\n }\n // Backfill a reply run's structural parent from ai-run-start when its output\n // wire wasn't indexed (rare history lag). Keyed by runId → codec-message-id.\n for (const msg of sortedMessages) {\n if (msg.name !== EVENT_RUN_START) continue;\n const h = getTransportHeaders(msg);\n const msgRunId = h[HEADER_RUN_ID];\n if (msgRunId === undefined) continue;\n const cid = runIdToCodecMessageId.get(msgRunId);\n if (cid === undefined) continue;\n const meta = nodeMeta.get(cid);\n if (meta && meta.parentCodecMessageId === undefined) meta.parentCodecMessageId = h[HEADER_PARENT];\n }\n\n // Walk the structural parent chain from the current run's input node up to\n // the conversation root, then fold each node in chain order. The upward walk\n // naturally excludes un-taken branch siblings (an edit's alternate prompt, a\n // regenerate's superseded reply), so no per-ancestor truncation is needed.\n // (Open caveat, deferred with a golden test: regenerating a non-trailing\n // message of a multi-message reply — the node walk can't slice inside one\n // run's projection.)\n const messages: TMessage[] = [];\n let chainLength = 0;\n if (assistantParentFallback !== undefined) {\n const chain = buildBranchChain(nodeMeta, assistantParentFallback);\n chainLength = chain.length;\n for (const cid of chain) {\n const meta = nodeMeta.get(cid);\n // Skip any chain node belonging to the CURRENT run — it is folded once,\n // wholesale, at the tail below. For a continuation the run-id is reused\n // and `assistantParentFallback` points at a message INSIDE the current\n // run, so it would otherwise be folded twice and emit duplicate tool_use\n // ids.\n if (meta?.runId === runId) continue;\n const projection =\n meta?.runId === undefined\n ? foldInputMessages(codec, sortedMessages, cid)\n : foldRunMessages(codec, sortedMessages, meta.runId).projection;\n messages.push(...codec.getMessages(projection).map((m) => m.message));\n }\n }\n\n // Current run — folded from the same sorted messages, appended at the chain\n // tail (the chain ended at this run's input node).\n const { projection, folded } = foldRunMessages(codec, sortedMessages, runId);\n messages.push(...codec.getMessages(projection).map((m) => m.message));\n\n logger?.debug('loadConversation(); built', { runId, chainLength, totalMessages: messages.length, folded });\n return { messages, projection };\n};\n","/**\n * Pure stream piping function.\n *\n * Reads outputs from a ReadableStream, writes them to an encoder via\n * `publishOutput`, and handles cancel/error. No dependencies on run\n * state or session internals.\n */\n\nimport type { Logger } from '../../logger.js';\nimport type { CodecInputEvent, CodecOutputEvent, Encoder, WriteOptions } from '../codec/types.js';\nimport type { StreamResult } from './types.js';\n\n/**\n * Adapt an AbortSignal into a promise that resolves once the signal aborts,\n * paired with a cleanup that detaches the listener. With no signal the promise\n * never resolves (there is no cancellation path); an already-aborted signal\n * resolves immediately. `cleanup` is a no-op unless a listener was attached.\n * @param signal - The AbortSignal to watch, or undefined for no cancellation.\n * @returns The abort promise and a cleanup to call when racing is done.\n */\nconst abortSignalToPromise = (signal: AbortSignal | undefined): { promise: Promise<void>; cleanup: () => void } => {\n let listener: (() => void) | undefined;\n const promise =\n signal === undefined\n ? // eslint-disable-next-line @typescript-eslint/no-empty-function -- never-resolving promise: no signal means no cancellation path\n new Promise<void>(() => {})\n : signal.aborted\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n listener = () => {\n resolve();\n };\n signal.addEventListener('abort', listener, { once: true });\n });\n const cleanup = (): void => {\n if (listener && signal) signal.removeEventListener('abort', listener);\n };\n return { promise, cleanup };\n};\n\n/**\n * Pipe an output stream through an encoder to the channel.\n *\n * Returns when the stream completes, is cancelled (via signal), or errors.\n * The `reason` field of the result indicates which case occurred.\n * @param stream - The output stream to read from.\n * @param encoder - The encoder to publish outputs through.\n * @param signal - AbortSignal to monitor for cancellation.\n * @param onCancelled - Optional callback invoked when the stream is cancelled, before the stream ends.\n * @param resolveWriteOptions - Optional per-output hook returning {@link WriteOptions} overrides to pass to `encoder.publishOutput`.\n * @param logger - Optional logger for diagnostic output.\n * @returns A {@link StreamResult}: `reason` is why the pipe ended, and `error` holds the caught error when `reason` is `'error'`.\n */\nexport const pipeStream = async <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>(\n stream: ReadableStream<TOutput>,\n encoder: Encoder<TInput, TOutput>,\n signal: AbortSignal | undefined,\n onCancelled?: (write: (output: TOutput) => Promise<void>) => void | Promise<void>,\n resolveWriteOptions?: (output: TOutput) => WriteOptions | undefined,\n logger?: Logger,\n): Promise<StreamResult> => {\n logger?.trace('pipeStream();');\n\n const reader = stream.getReader();\n const abort = abortSignalToPromise(signal);\n\n let reason: StreamResult['reason'] = 'complete';\n let caughtError: Error | undefined;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop broken by return/break\n while (true) {\n // .then() is intentional: transforms the AbortSignal into a discriminant\n // for Promise.race — no async/await equivalent for this pattern.\n const result = await Promise.race([reader.read(), abort.promise.then(() => 'cancelled' as const)]);\n\n if (result === 'cancelled') {\n reason = 'cancelled';\n logger?.debug('pipeStream(); stream cancelled by AbortSignal');\n if (onCancelled) {\n await onCancelled(async (output: TOutput) => encoder.publishOutput(output));\n }\n await encoder.cancel('cancelled');\n break;\n }\n\n const { done, value } = result;\n if (done) {\n await encoder.close();\n logger?.debug('pipeStream(); stream completed');\n break;\n }\n\n await encoder.publishOutput(value, resolveWriteOptions?.(value));\n }\n } catch (error) {\n reason = 'error';\n caughtError = error instanceof Error ? error : new Error(String(error));\n logger?.error('pipeStream(); stream error', { error: caughtError.message });\n try {\n await encoder.close();\n } catch {\n // Best-effort: encoder close in the error path may also fail\n // (e.g. channel disconnected). The original error is preserved in\n // the StreamResult reason (\"error\").\n }\n } finally {\n abort.cleanup();\n reader.releaseLock();\n }\n\n return { reason, error: caughtError };\n};\n","/**\n * Server-side run state management and lifecycle event publishing.\n *\n * Owns the authoritative run lifecycle. Tracks active runs with their\n * AbortControllers and clientIds. Publishes run-start, run-resume, run-suspend, and\n * run-end events on the Ably channel so all clients can react to run\n * state changes.\n */\n\nimport type * as Ably from 'ably';\n\nimport { EVENT_RUN_END, EVENT_RUN_RESUME, EVENT_RUN_START, EVENT_RUN_SUSPEND } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { buildLifecycleHeaders } from './headers.js';\nimport type { RunEndReason } from './types.js';\n\n/**\n * Per-invocation metadata carried on a run's opening lifecycle event. A\n * continuation (re-entering an existing run) sets `continuation` and omits the\n * structural `parent` / `forkOf` / `regenerates` fields.\n */\nexport interface StartRunMetadata {\n /** Structural parent codec-message-id (fresh run-start only). */\n parent?: string;\n /** Forked user-prompt codec-message-id for an edit (fresh run-start only). */\n forkOf?: string;\n /** Regenerated assistant codec-message-id (fresh run-start only). */\n regenerates?: string;\n /** Agent-minted invocation id, carried on the lifecycle event. */\n invocationId?: string;\n /** ClientId of the triggering input event. */\n inputClientId?: string;\n /** Codec-message-id of the triggering input event. */\n inputCodecMessageId?: string;\n /** When true, publish `ai-run-resume` (re-entry) instead of `ai-run-start`. */\n continuation?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\n/** Manages active runs and publishes run lifecycle events on the channel. */\nexport interface RunManager {\n /**\n * Register a run and publish its opening lifecycle event. Publishes\n * `ai-run-start` for a fresh run, or `ai-run-resume` when `metadata.continuation`\n * is set (a subsequent invocation re-entering an existing run). A resume omits\n * the structural `parent` / `forkOf` / `regenerates` headers — the original\n * run-start owns the run's structure. Returns the run's AbortSignal.\n */\n startRun(\n runId: string,\n clientId?: string,\n controller?: AbortController,\n metadata?: StartRunMetadata,\n ): Promise<AbortSignal>;\n /**\n * Suspend a run. Publishes run-suspend on the channel and drops the run's\n * active-run entry — the agent process terminates on suspend, so there is no\n * live AbortController to retain. A cancel arriving during suspension is a\n * no-op; the resuming invocation re-registers the run via {@link startRun}.\n * Carries the same per-invocation attribution as {@link endRun}\n * (`inputClientId`, `inputCodecMessageId`), since a suspend is the terminal\n * event of the suspending invocation just as run-end is of an ending one.\n */\n suspendRun(runId: string, invocationId?: string, inputClientId?: string, inputCodecMessageId?: string): Promise<void>;\n /**\n * End a run. Publishes run-end on the channel (stamping `reason` as the\n * run-reason header) and drops the run's active-run entry. Carries the same\n * per-invocation attribution as {@link suspendRun} (`invocationId`,\n * `inputClientId`, `inputCodecMessageId`), since run-end is the terminal event\n * of the ending invocation.\n */\n endRun(\n runId: string,\n reason: RunEndReason,\n invocationId?: string,\n inputClientId?: string,\n inputCodecMessageId?: string,\n ): Promise<void>;\n /** Get the AbortSignal for a run. */\n getSignal(runId: string): AbortSignal | undefined;\n /** Get the clientId that owns a run. */\n getClientId(runId: string): string | undefined;\n /** Fire the AbortSignal for a run to cancel any in-flight work. */\n cancel(runId: string): void;\n /** Get all active run IDs. */\n getActiveRunIds(): string[];\n /** Cancel all active runs and clear state. */\n close(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal state\n// ---------------------------------------------------------------------------\n\ninterface ActiveRunEntry {\n controller: AbortController;\n clientId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nclass DefaultRunManager implements RunManager {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _logger: Logger | undefined;\n private readonly _activeRuns = new Map<string, ActiveRunEntry>();\n\n constructor(channel: Ably.RealtimeChannel, logger?: Logger) {\n this._channel = channel;\n this._logger = logger?.withContext({ component: 'RunManager' });\n }\n\n async startRun(\n runId: string,\n clientId?: string,\n externalController?: AbortController,\n metadata?: StartRunMetadata,\n ): Promise<AbortSignal> {\n this._logger?.trace('DefaultRunManager.startRun();', { runId, clientId });\n\n const controller = externalController ?? new AbortController();\n const resolvedClientId = clientId ?? '';\n this._activeRuns.set(runId, { controller, clientId: resolvedClientId });\n\n // A continuation re-enters an already-started run: publish `ai-run-resume`\n // rather than `ai-run-start`. Resume is a pure re-entry signal — the\n // original run-start already established the run's structure, so the\n // parent / forkOf / regenerates metadata is NOT re-stamped here (doing so\n // would point the run at content within itself). The agent learned this is\n // a continuation from the run-id on the triggering input; the re-entry is\n // conveyed to clients by the event name, not a header echo. The\n // invocation-id / input attribution headers are carried on both.\n const continuation = metadata?.continuation === true;\n\n const headers = buildLifecycleHeaders({\n runId,\n runClientId: resolvedClientId,\n parent: continuation ? undefined : metadata?.parent,\n forkOf: continuation ? undefined : metadata?.forkOf,\n regenerates: continuation ? undefined : metadata?.regenerates,\n invocationId: metadata?.invocationId,\n inputClientId: metadata?.inputClientId,\n inputCodecMessageId: metadata?.inputCodecMessageId,\n });\n\n await this._channel.publish({\n name: continuation ? EVENT_RUN_RESUME : EVENT_RUN_START,\n extras: { ai: { transport: headers } },\n });\n\n this._logger?.debug('DefaultRunManager.startRun(); run started', { runId });\n return controller.signal;\n }\n\n async suspendRun(\n runId: string,\n invocationId?: string,\n inputClientId?: string,\n inputCodecMessageId?: string,\n ): Promise<void> {\n this._logger?.trace('DefaultRunManager.suspendRun();', { runId });\n await this._publishTerminal(EVENT_RUN_SUSPEND, runId, { invocationId, inputClientId, inputCodecMessageId });\n this._logger?.debug('DefaultRunManager.suspendRun(); run suspended', { runId });\n }\n\n async endRun(\n runId: string,\n reason: RunEndReason,\n invocationId?: string,\n inputClientId?: string,\n inputCodecMessageId?: string,\n ): Promise<void> {\n this._logger?.trace('DefaultRunManager.endRun();', { runId, reason });\n await this._publishTerminal(EVENT_RUN_END, runId, { reason, invocationId, inputClientId, inputCodecMessageId });\n this._logger?.debug('DefaultRunManager.endRun(); run ended', { runId, reason });\n }\n\n /**\n * Publish a run's terminal lifecycle event (run-suspend or run-end) and drop\n * its active-run entry. Both events are the suspending/ending invocation's\n * terminal signal, carrying the same per-invocation correlation; they differ\n * only by event name and the run-reason header (run-end). Publishes BEFORE\n * dropping local state so a publish failure leaves the run in the active set.\n * @param eventName - The lifecycle event to publish (run-suspend or run-end).\n * @param runId - The run being suspended or ended.\n * @param attribution - Per-invocation correlation and the terminal reason.\n * @param attribution.reason - Terminal reason; set for run-end, omitted for run-suspend.\n * @param attribution.invocationId - The invocation's id.\n * @param attribution.inputClientId - ClientId of the triggering input event.\n * @param attribution.inputCodecMessageId - Codec-message-id of the triggering input event.\n */\n private async _publishTerminal(\n eventName: string,\n runId: string,\n attribution: {\n reason?: RunEndReason;\n invocationId?: string;\n inputClientId?: string;\n inputCodecMessageId?: string;\n },\n ): Promise<void> {\n const resolvedClientId = this._activeRuns.get(runId)?.clientId ?? '';\n const headers = buildLifecycleHeaders({ runId, runClientId: resolvedClientId, ...attribution });\n await this._channel.publish({ name: eventName, extras: { ai: { transport: headers } } });\n this._activeRuns.delete(runId);\n }\n\n getSignal(runId: string): AbortSignal | undefined {\n return this._activeRuns.get(runId)?.controller.signal;\n }\n\n getClientId(runId: string): string | undefined {\n return this._activeRuns.get(runId)?.clientId;\n }\n\n cancel(runId: string): void {\n this._logger?.debug('DefaultRunManager.cancel();', { runId });\n this._activeRuns.get(runId)?.controller.abort();\n }\n\n getActiveRunIds(): string[] {\n return [...this._activeRuns.keys()];\n }\n\n close(): void {\n this._logger?.trace('DefaultRunManager.close();', { activeRuns: this._activeRuns.size });\n for (const state of this._activeRuns.values()) {\n state.controller.abort();\n }\n this._activeRuns.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a run manager bound to the given channel.\n * @param channel - The Ably channel to publish lifecycle events on.\n * @param logger - Optional logger for diagnostic output.\n * @returns A new {@link RunManager} instance.\n */\nexport const createRunManager = (channel: Ably.RealtimeChannel, logger?: Logger): RunManager =>\n new DefaultRunManager(channel, logger);\n","/**\n * Core agent (server-side) session, parameterized by codec.\n *\n * Composes RunManager and pipeStream to handle the full server-side run\n * lifecycle. Cancel message routing is handled directly by the session's\n * single channel subscription — no separate cancel manager needed.\n *\n * The session exposes a single factory method — `createRun()` — which returns\n * a Run object with explicit lifecycle methods: start(), pipe(), addEvents(),\n * suspend(), and end() (suspend() and end() are both terminal).\n */\n\nimport * as Ably from 'ably';\n\nimport {\n EVENT_CANCEL,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_EVENT_ID,\n HEADER_FORK_OF,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_MSG_REGENERATE,\n HEADER_PARENT,\n HEADER_RUN_CLIENT_ID,\n HEADER_RUN_ID,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport { compareBySerial, getTransportHeaders } from '../../utils.js';\nimport { registerAgent } from '../agent.js';\nimport type { Codec, CodecInputEvent, CodecOutputEvent } from '../codec/types.js';\nimport { buildTransportHeaders } from './headers.js';\nimport { evictOldestIfFull } from './internal/bounded-map.js';\nimport { Invocation } from './invocation.js';\nimport { loadConversation, loadRunProjection } from './load-conversation.js';\nimport { pipeStream } from './pipe-stream.js';\nimport type { RunManager } from './run-manager.js';\nimport { createRunManager } from './run-manager.js';\nimport type {\n AgentSession,\n AgentSessionOptions,\n CancelRequest,\n EventsNode,\n LoadConversationOptions,\n MessageNode,\n PipeOptions,\n Run,\n RunEndReason,\n RunRuntime,\n RunView,\n StreamResult,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Run-state lookup helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Wait for every event-id in `expectedInputEventIds` to arrive as a channel\n * message before letting the run proceed to LLM work. Uses the session's\n * unfiltered channel dispatcher (registered in `connect()`) so that\n * messages replayed via channel rewind on attach reach the lookup — no\n * separate history fetch needed.\n *\n * Scope: this awaits the data-carrying input events a send publishes —\n * fresh prompts, edits, regenerates, tool results, and approvals. Control\n * events (cancel etc.) carry no `event-id`, are dispatched\n * separately, and never enter this lookup.\n *\n * Each client-published event in a send (user-message AND amend events\n * such as tool-approval responses and client tool outputs) is stamped\n * with its own `event-id`.\n * The lookup matches incoming messages against the expected set; ids\n * not in the set are ignored, duplicates (rewind redelivering a message\n * also seen live) are deduped by event-id. The wait completes when\n * every expected id has arrived, guaranteeing the channel state is\n * consistent with what the client promised before any downstream\n * processing (loadProjection, streamText) runs.\n *\n * User-message arrivals decode into MessageNodes that populate\n * `run.view.messages`; amend arrivals fold into a fresh projection that\n * has no target message, so they're orphaned and dropped — they only\n * count toward the wait. Collected nodes are returned sorted by Ably\n * `serial` ascending.\n *\n * Bounded by `timeoutMs` as a total budget across all N arrivals. The\n * caller's `signal` aborts the wait. On partial collection at timeout the\n * promise rejects with `InputEventNotFound` and an error message including\n * \"received X of Y\". If any decode throws mid-collection, the whole lookup\n * rejects with `InputEventNotFound` wrapping the decode error as cause —\n * already-collected messages are discarded.\n * @param opts - Lookup parameters.\n * @param opts.register - Session-provided registration that delivers the input events for the expected event-ids. Returns an unregister function.\n * @param opts.codec - Codec used to decode arriving messages.\n * @param opts.invocationId - Invocation identifier — used only for diagnostic logging and error messages.\n * @param opts.runId - Run identifier (used for logging and error messages).\n * @param opts.expectedInputEventIds - Input-event ids the lookup must observe before resolving.\n * @param opts.timeoutMs - Maximum total time to wait for all event-id arrivals.\n * @param opts.signal - AbortSignal that cancels the wait when the run is cancelled.\n * @param opts.logger - Optional logger for diagnostic output.\n * @returns The MessageNodes for arriving user-message events (sorted by Ably\n * serial — empty when every input event was a tool-resolution wire message that\n * decoded to a chunk and produced no node), and the transport headers of\n * the first matched wire message. `firstHeaders` is the canonical source for\n * run-level metadata (clientId, parent, forkOf, continuation flag) because\n * it lands whether or not the decode produced a MessageNode. `firstClientId`\n * carries the publisher's Ably-level `clientId` from that same message — the\n * source of `inputClientId` re-stamping on the agent's published events.\n */\ninterface InputEventLookupResult<TMessage> {\n nodes: MessageNode<TMessage>[];\n firstHeaders?: Record<string, string>;\n firstClientId?: string;\n /**\n * Raw Ably messages observed live for the matched input-event ids, in\n * arrival order. The agent forwards these to `loadRunProjection` so a\n * continuation invocation can fold the just-published client wires\n * (e.g. a tool-output-available) without waiting on Ably's channel\n * history indexing window.\n */\n rawMessages: Ably.InboundMessage[];\n}\n\nconst lookupInputEvents = async <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(opts: {\n register: (callback: (msg: Ably.InboundMessage) => void) => () => void;\n codec: Codec<TInput, TOutput, TProjection, TMessage>;\n invocationId: string;\n runId: string;\n expectedInputEventIds: readonly string[];\n timeoutMs: number;\n signal: AbortSignal;\n logger: Logger | undefined;\n}): Promise<InputEventLookupResult<TMessage>> => {\n const { register, codec, invocationId, runId, expectedInputEventIds, timeoutMs, signal, logger } = opts;\n const expectedSet = new Set(expectedInputEventIds);\n const expectedCount = expectedSet.size;\n\n const collected: MessageNode<TMessage>[] = [];\n const rawMessages: Ably.InboundMessage[] = [];\n const matchedInputEventIds = new Set<string>();\n let firstHeaders: Record<string, string> | undefined;\n let firstClientId: string | undefined;\n\n /**\n * Decode an inbound Ably message into MessageNodes via the codec.\n * @param m - The inbound Ably message to decode.\n * @returns The decoded MessageNodes carrying transport headers and serial.\n */\n const decode = (m: Ably.InboundMessage): MessageNode<TMessage>[] => {\n const decoder = codec.createDecoder();\n const headers = getTransportHeaders(m);\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID] ?? '';\n const { inputs, outputs } = decoder.decode(m);\n const events: (TInput | TOutput)[] = [...inputs, ...outputs];\n let projection = codec.init();\n for (const event of events) {\n projection = codec.fold(projection, event, { serial: m.serial ?? '', messageId: codecMessageId });\n }\n return codec.getMessages(projection).map(({ message }) => ({\n kind: 'message' as const,\n message,\n codecMessageId,\n parentId: headers[HEADER_PARENT],\n forkOf: headers[HEADER_FORK_OF],\n headers,\n serial: m.serial,\n }));\n };\n\n return new Promise<InputEventLookupResult<TMessage>>((resolve, reject) => {\n let settled = false;\n // Dedupe across rewind-redelivery: rewind may surface a message the\n // listener also saw live. Scoped to the active lookup so it cannot\n // grow unbounded.\n const seenSerials = new Set<string>();\n // Forward-declared so that cleanup() and onCancelled() can reference them\n // before they are assigned. cleanup may run synchronously inside\n // `register(...)` (when buffered input events drain on registration) before\n // `unregister`/`timer` have been assigned — the no-op fallback for\n // unregister and undefined-guard for timer handle that window. The\n // settled-flag re-check after `register` returns reconciles the\n // listener-detach that cleanup couldn't perform inside that window.\n /* eslint-disable prefer-const, unicorn/consistent-function-scoping, @typescript-eslint/no-empty-function -- forward-declared state for the sync-drain reconciliation pattern; see comment above. */\n let unregister: () => void = () => {};\n let timer: ReturnType<typeof setTimeout> | undefined;\n /* eslint-enable */\n const cleanup = (): void => {\n unregister();\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener('abort', onCancelled);\n };\n const onCancelled = (): void => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Ably.ErrorInfo(`unable to look up input event; run ${runId} was cancelled`, ErrorCode.InvalidArgument, 400),\n );\n };\n signal.addEventListener('abort', onCancelled, { once: true });\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- onCancelled may have settled the promise synchronously above when the signal was already aborted.\n if (settled) return;\n unregister = register((m) => {\n if (settled) return;\n if (m.serial !== undefined && seenSerials.has(m.serial)) return;\n if (m.serial !== undefined) seenSerials.add(m.serial);\n\n const wireHeaders = getTransportHeaders(m);\n\n // Only count messages whose event-id is in the expected set.\n const msgEventId = wireHeaders[HEADER_EVENT_ID];\n if (!msgEventId || !expectedSet.has(msgEventId) || matchedInputEventIds.has(msgEventId)) return;\n matchedInputEventIds.add(msgEventId);\n\n // Capture the trigger event's headers AND its Ably channel-level `clientId`\n // so run-level metadata (parent / forkOf / continuation flag from headers;\n // `inputClientId` from the wire publisher) is available even when the decode\n // produces zero MessageNodes — the case for continuation tool-resolution\n // trigger events whose chunks fold into a fresh empty projection without\n // an assistant to land on.\n if (firstHeaders === undefined) {\n firstHeaders = wireHeaders;\n firstClientId = m.clientId;\n }\n\n let decoded: MessageNode<TMessage>[];\n try {\n decoded = decode(m);\n } catch (error) {\n settled = true;\n cleanup();\n const cause = error instanceof Ably.ErrorInfo ? error : undefined;\n reject(\n new Ably.ErrorInfo(\n `unable to look up input event; decode failed for invocation ${invocationId}: ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.InputEventNotFound,\n 504,\n cause,\n ),\n );\n return;\n }\n for (const node of decoded) collected.push(node);\n rawMessages.push(m);\n if (matchedInputEventIds.size < expectedCount) return;\n settled = true;\n cleanup();\n // Sort by Ably serial ascending so callers see publish order regardless\n // of interleaved rewind+live delivery. Null serials sort last (defensive\n // — input events should always carry a serial).\n collected.sort(compareBySerial);\n logger?.debug('lookupInputEvents(); collected input events', {\n runId,\n invocationId,\n count: collected.length,\n });\n resolve({ nodes: collected, firstHeaders, firstClientId, rawMessages });\n });\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the register callback may have settled the promise synchronously during buffered input-event drain.\n if (settled) {\n // Sync drain inside register settled the promise; cleanup ran but\n // could not detach the listener because `unregister` was still the\n // no-op. Detach it now.\n unregister();\n return;\n }\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Ably.ErrorInfo(\n `unable to look up input event; received ${String(collected.length)} of ${String(expectedCount)} input events for invocation ${invocationId} within ${String(timeoutMs)}ms`,\n ErrorCode.InputEventNotFound,\n 504,\n ),\n );\n }, timeoutMs);\n });\n};\n\n// ---------------------------------------------------------------------------\n// Internal run record for cancel routing\n// ---------------------------------------------------------------------------\n\ninterface RegisteredRun {\n runId: string;\n /** Invocation-id this run is associated with, minted by the agent at `createRun` (or the `runtime.invocationId` override). */\n invocationId: string;\n controller: AbortController;\n /** Composite signal that fires when either the internal controller or the external signal aborts. */\n signal: AbortSignal;\n onCancel?: (request: CancelRequest) => Promise<boolean>;\n onError?: (error: Ably.ErrorInfo) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal state machines\n// ---------------------------------------------------------------------------\n\nenum SessionState {\n READY = 'ready',\n CLOSED = 'closed',\n}\n\nenum RunState {\n INITIALIZED = 'initialized',\n STARTED = 'started',\n ENDED = 'ended',\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-ST1\nclass DefaultAgentSession<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> implements AgentSession<TOutput, TProjection, TMessage> {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: AgentSessionOptions<TInput, TOutput, TProjection, TMessage>['codec'];\n private readonly _logger: Logger | undefined;\n private readonly _onError: ((error: Ably.ErrorInfo) => void) | undefined;\n private readonly _runManager: RunManager;\n private readonly _registeredRuns = new Map<string, RegisteredRun>();\n /**\n * Reverse index from a run's triggering input codec-message-id to its\n * run-id, populated once `Run.start()`'s input-event lookup resolves the\n * triggering input. Lets `_handleCancelMessage` route a cancel keyed by the\n * input codec-message-id (a fresh send whose run-id the client doesn't know)\n * to the registered run. Entries are removed when the run ends / suspends /\n * the session closes, alongside `_registeredRuns`.\n */\n private readonly _runIdByInputCodecMessageId = new Map<string, string>();\n /**\n * Cancels buffered by triggering input codec-message-id when they arrived\n * before the run was known — i.e. before `Run.start()`'s input-event lookup\n * resolved that input to a run. A fresh run has no run-id at the client's\n * send time (the agent mints it at run-start), so an early cancel can only be\n * keyed by the input codec-message-id, and the `inputCodecMessageId → run`\n * linkage doesn't exist until the lookup completes. `Run.start()` consults\n * this buffer as a PULL once it resolves its `resolvedInputCodecMessageId`,\n * honouring any cancel that arrived first. Mirrors `_inputEventBuffer`: FIFO\n * eviction at `_inputEventBufferLimit` entries, cleared on `close()`.\n */\n private readonly _deferredCancels = new Map<string, Ably.InboundMessage>();\n /**\n * Active input-event lookups keyed by `event-id`. The channel listener\n * dispatches each input event to the lookup that registered for its\n * `event-id`, so that messages replayed via channel rewind (and live\n * messages alike) reach the right lookup without each lookup having to\n * subscribe separately, and without depending on a client-minted\n * `invocation-id`.\n */\n private readonly _pendingInputEventLookups = new Map<string, (msg: Ably.InboundMessage) => void>();\n /**\n * Input events buffered by `event-id` when no lookup callback was\n * registered at delivery time. Rewind replays user messages on attach —\n * before `run.start()` runs — so without buffering they would be dropped.\n * Each `event-id` maps to an ordered array so rewind redelivery of the\n * same event before registration is preserved (the lookup later dedupes by\n * serial). `_registerInputEventListener` drains the buffer on registration.\n * FIFO eviction at `_inputEventBufferLimit` event entries (each entry counts\n * once regardless of array length).\n */\n private readonly _inputEventBuffer = new Map<string, Ably.InboundMessage[]>();\n private readonly _inputEventBufferLimit: number;\n private readonly _channelListener: (msg: Ably.InboundMessage) => void;\n private readonly _inputEventLookupTimeoutMs: number;\n\n private _state = SessionState.READY;\n private _connectPromise: Promise<void> | undefined;\n private _hasAttachedOnce: boolean;\n private readonly _onChannelStateChange: Ably.channelEventCallback;\n\n constructor(options: AgentSessionOptions<TInput, TOutput, TProjection, TMessage>) {\n this._codec = options.codec;\n // Spec: AIT-ST1a, AIT-ST1a2 — register this SDK on both the connection\n // (options.agents) and channel-attach (params.agent) paths. Idempotent\n // across sessions sharing one client.\n const registerOptions = registerAgent(options.client, options.codec);\n // Attach with a rewind window (default 2m) so a freshly-constructed\n // agent session can locate an input event that was published before it\n // attached (closes the lookup race when a per-request agent is spun\n // up after the client has already POSTed). Tunable via\n // `AgentSessionOptions.rewindWindow`.\n const channelOptions: Ably.ChannelOptions = {\n params: { ...registerOptions.params, rewind: options.rewindWindow ?? '2m' },\n };\n this._channel = options.client.channels.get(options.channelName, channelOptions);\n this._logger = options.logger?.withContext({ component: 'AgentSession' });\n this._onError = options.onError;\n this._runManager = createRunManager(this._channel, this._logger);\n this._inputEventLookupTimeoutMs = options.inputEventLookupTimeoutMs ?? 30000;\n this._inputEventBufferLimit = options.inputEventBufferLimit ?? 200;\n\n this._channelListener = (msg: Ably.InboundMessage) => {\n this._handleChannelMessage(msg);\n };\n\n // Spec: AIT-ST12, AIT-ST12a\n // Listen for channel state changes that break message continuity. The\n // session only consumes cancel messages from the channel, so losing one\n // is survivable — but the developer needs to know so they can decide\n // whether to cancel in-flight work. _hasAttachedOnce is seeded from the\n // channel's current state so pre-attached channels are handled correctly;\n // it distinguishes the initial attach from a genuine discontinuity.\n this._hasAttachedOnce = this._channel.state === 'attached';\n this._onChannelStateChange = (stateChange: Ably.ChannelStateChange) => {\n this._handleChannelStateChange(stateChange);\n };\n this._channel.on(this._onChannelStateChange);\n\n this._logger?.debug('DefaultAgentSession(); session created');\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n // Spec: AIT-ST2\n // eslint-disable-next-line @typescript-eslint/promise-function-async -- preserve reference equality across calls\n connect(): Promise<void> {\n if (this._state === SessionState.CLOSED) {\n return Promise.reject(new Ably.ErrorInfo('unable to connect; session is closed', ErrorCode.SessionClosed, 400));\n }\n if (this._connectPromise) return this._connectPromise;\n\n this._logger?.trace('DefaultAgentSession.connect();');\n // Subscribe unfiltered (before attach, per RTL7g — subscribe implicitly\n // attaches the channel). An unfiltered subscribe ensures that messages\n // replayed via channel rewind reach the dispatcher so input-event\n // lookups can match against them; the dispatcher then routes by name\n // (cancel vs. input event). A name-filtered subscribe would silently\n // drop replayed user messages because rewind delivers them to listeners\n // registered at attach time only.\n this._connectPromise = this._channel.subscribe(this._channelListener).then(\n () => {\n this._logger?.debug('DefaultAgentSession.connect(); subscribed and attached');\n },\n (error: unknown) => {\n const errInfo = new Ably.ErrorInfo(\n `unable to subscribe to channel; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n this._logger?.error('DefaultAgentSession.connect(); subscribe failed');\n this._onError?.(errInfo);\n throw errInfo;\n },\n );\n return this._connectPromise;\n }\n\n /**\n * Register a callback to receive the input events carrying any of the\n * given `eventIds`. Lookups must share the session's unfiltered\n * subscription rather than registering their own subscribe — Ably's\n * rewind only delivers to listeners present at attach time.\n *\n * The listener remains registered after the initial buffer drain so a\n * matching event that arrives live (rather than from the buffer) still\n * reaches the lookup until it unregisters itself. Today the only caller\n * registers a single trigger event-id; the array form keeps the\n * registration capable of awaiting several ids without changing callers.\n * @param eventIds - The `event-id`s this listener cares about.\n * @param callback - Invoked once per matching Ably message, in buffer-insertion order for drained entries.\n * @returns Unregister function. Safe to call multiple times.\n */\n private _registerInputEventListener(\n eventIds: readonly string[],\n callback: (msg: Ably.InboundMessage) => void,\n ): () => void {\n for (const eventId of eventIds) {\n this._pendingInputEventLookups.set(eventId, callback);\n }\n // Drain any buffered input events for these event-ids — rewind replays\n // user messages on attach before run.start() can register the callback.\n // Without this drain, the lookup waits the full\n // `inputEventLookupTimeoutMs` for a live arrival that never comes. Set\n // all listeners before draining so a drain that completes the lookup\n // synchronously cannot leave a later event-id unmapped.\n for (const eventId of eventIds) {\n const buffered = this._inputEventBuffer.get(eventId);\n if (buffered) {\n this._inputEventBuffer.delete(eventId);\n for (const m of buffered) callback(m);\n }\n }\n return () => {\n for (const eventId of eventIds) {\n if (this._pendingInputEventLookups.get(eventId) === callback) {\n this._pendingInputEventLookups.delete(eventId);\n }\n }\n };\n }\n\n // Spec: AIT-ST3\n createRun(invocation: Invocation, runtime?: RunRuntime<TOutput>): Run<TOutput, TProjection, TMessage> {\n this._logger?.trace('DefaultAgentSession.createRun();', { inputEventId: invocation.inputEventId });\n return this._createRun(invocation, runtime ?? {});\n }\n\n // Spec: AIT-ST11\n async close(): Promise<void> {\n if (this._state === SessionState.CLOSED) return;\n this._state = SessionState.CLOSED;\n this._logger?.trace('DefaultAgentSession.close();');\n if (this._connectPromise) {\n this._channel.unsubscribe(this._channelListener);\n }\n this._channel.off(this._onChannelStateChange);\n for (const reg of this._registeredRuns.values()) {\n reg.controller.abort();\n }\n this._registeredRuns.clear();\n this._runIdByInputCodecMessageId.clear();\n this._deferredCancels.clear();\n this._pendingInputEventLookups.clear();\n this._inputEventBuffer.clear();\n this._runManager.close();\n\n // Detach the channel this session attached. connect() subscribes (which\n // implicitly attaches), so we only detach when connect() ran. Best-effort:\n // a detach failure (e.g. the channel is already FAILED) must not throw out\n // of close().\n if (this._connectPromise) {\n try {\n await this._channel.detach();\n } catch (error) {\n // Swallowed (see above): a detach failure must not throw out of\n // close(). Logged at debug for observability.\n this._logger?.debug('DefaultAgentSession.close(); channel detach failed', { error });\n }\n }\n\n this._logger?.debug('DefaultAgentSession.close(); session closed');\n }\n\n // -------------------------------------------------------------------------\n // Cancel message routing\n // -------------------------------------------------------------------------\n\n private async _handleCancelMessage(msg: Ably.InboundMessage): Promise<void> {\n const headers = getTransportHeaders(msg);\n const runId = headers[HEADER_RUN_ID];\n const inputCodecMessageId = headers[HEADER_INPUT_CODEC_MESSAGE_ID];\n\n // Malformed cancel: drop with warn. A cancel must identify its target by\n // `run-id` (a continuation, whose run-id the client knows) and/or by\n // `input-codec-message-id` (a fresh send, before the agent minted the\n // run-id). Neither present means there is nothing to route to.\n if (!runId && !inputCodecMessageId) {\n this._logger?.warn('DefaultAgentSession._handleCancelMessage(); missing run-id and input-codec-message-id', {\n serial: msg.serial,\n });\n return;\n }\n\n // Primary path — match by run-id (continuations, whose run-id the client\n // already knows). Resolve the input-codec-message-id to a run-id when the\n // run-id wasn't supplied (a fresh-send cancel that arrived after the run's\n // input-event lookup resolved, so the linkage already exists).\n const resolvedRunId =\n runId ?? (inputCodecMessageId ? this._runIdByInputCodecMessageId.get(inputCodecMessageId) : undefined);\n const reg = resolvedRunId ? this._registeredRuns.get(resolvedRunId) : undefined;\n\n if (!reg) {\n // The run isn't known yet. A fresh-send cancel can race ahead of the\n // run's input-event lookup (which is what establishes the\n // input-codec-message-id → run linkage). Buffer it by\n // input-codec-message-id so `Run.start()` can pull and honour it once it\n // resolves the triggering input. A bare run-id cancel for an unknown run\n // is a no-op (the run never existed here, or already ended).\n if (inputCodecMessageId !== undefined) {\n this._bufferDeferredCancel(inputCodecMessageId, msg);\n }\n return;\n }\n\n await this._cancelRegistration(reg, msg);\n }\n\n /**\n * Buffer a cancel that arrived before its target run was known, keyed by the\n * triggering input's codec-message-id. FIFO-evicts the oldest entry at\n * `_inputEventBufferLimit` (mirroring `_inputEventBuffer`). A later cancel\n * for the same input replaces the earlier one — the intent is identical.\n * @param inputCodecMessageId - The triggering input's codec-message-id.\n * @param msg - The raw cancel message (passed to `onCancel`).\n */\n private _bufferDeferredCancel(inputCodecMessageId: string, msg: Ably.InboundMessage): void {\n const evicted = evictOldestIfFull(this._deferredCancels, inputCodecMessageId, this._inputEventBufferLimit);\n if (evicted !== undefined) {\n this._logger?.warn('DefaultAgentSession._bufferDeferredCancel(); deferred-cancel buffer full, dropping oldest', {\n evictedInputCodecMessageId: evicted,\n limit: this._inputEventBufferLimit,\n });\n }\n this._deferredCancels.set(inputCodecMessageId, msg);\n this._logger?.debug('DefaultAgentSession._bufferDeferredCancel(); buffered early cancel', {\n inputCodecMessageId,\n serial: msg.serial,\n });\n }\n\n /**\n * Pull and honour a cancel buffered before this run was known. Called from\n * `Run.start()` once the input-event lookup resolves the run's triggering\n * input codec-message-id — the point at which the\n * `input-codec-message-id → run` linkage first exists. No-op when no cancel\n * was buffered for that input.\n * @param reg - The now-known run registration.\n * @param inputCodecMessageId - The run's resolved triggering input codec-message-id.\n */\n private async _pullDeferredCancel(reg: RegisteredRun, inputCodecMessageId: string): Promise<void> {\n const buffered = this._deferredCancels.get(inputCodecMessageId);\n if (buffered === undefined) return;\n this._deferredCancels.delete(inputCodecMessageId);\n this._logger?.debug('DefaultAgentSession._pullDeferredCancel(); honouring buffered cancel', {\n runId: reg.runId,\n inputCodecMessageId,\n });\n await this._cancelRegistration(reg, buffered);\n }\n\n /**\n * Fire a cancel against a known run: consult its `onCancel` authorization\n * hook (if any), then abort the run's controller. Shared by the run-id match,\n * the input-codec-message-id match, and the buffered-cancel pull so all three\n * honour `onCancel` and surface handler errors identically.\n * @param reg - The target run registration.\n * @param msg - The raw cancel message (passed to `onCancel`).\n */\n private async _cancelRegistration(reg: RegisteredRun, msg: Ably.InboundMessage): Promise<void> {\n const { runId } = reg;\n this._logger?.debug('DefaultAgentSession._cancelRegistration(); matched run', { runId });\n\n const request: CancelRequest = { message: msg, runId };\n\n try {\n if (reg.onCancel) {\n const allowed = await reg.onCancel(request);\n if (!allowed) {\n this._logger?.debug('DefaultAgentSession._cancelRegistration(); cancel rejected by onCancel', {\n runId,\n });\n return;\n }\n }\n reg.controller.abort();\n this._logger?.debug('DefaultAgentSession._cancelRegistration(); run cancelled', { runId });\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to process cancel for run ${runId}; onCancel handler threw: ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.CancelListenerError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n this._logger?.error('DefaultAgentSession._cancelRegistration(); onCancel threw', { runId });\n (reg.onError ?? this._onError)?.(errInfo);\n }\n }\n\n // -------------------------------------------------------------------------\n // Channel state change handler\n // -------------------------------------------------------------------------\n\n // Spec: AIT-ST12, AIT-ST12a\n private _handleChannelStateChange(stateChange: Ably.ChannelStateChange): void {\n if (this._state === SessionState.CLOSED) return;\n\n const { current, resumed } = stateChange;\n\n // Track the initial attach so we don't treat it as a discontinuity\n if (current === 'attached' && !this._hasAttachedOnce) {\n this._hasAttachedOnce = true;\n return;\n }\n\n // Continuity-breaking states:\n // - FAILED, SUSPENDED, DETACHED: no more messages expected (or gap)\n // - ATTACHED with resumed: false (UPDATE): messages were lost\n const continuityLost =\n current === 'failed' || current === 'suspended' || current === 'detached' || (current === 'attached' && !resumed);\n\n if (!continuityLost) return;\n\n this._logger?.error('DefaultAgentSession._handleChannelStateChange(); channel continuity lost', {\n current,\n resumed,\n previous: stateChange.previous,\n });\n\n const err = new Ably.ErrorInfo(\n `unable to deliver cancel messages; channel continuity lost (${current}${current === 'attached' ? ', resumed: false' : ''})`,\n ErrorCode.ChannelContinuityLost,\n 500,\n stateChange.reason,\n );\n\n // Session-level notification only: continuity loss is not scoped to any\n // run. Per-run onError handlers are reserved for errors from that run's\n // own operations (publish failures, encoder errors). Developers that need\n // per-run reaction can iterate active runs from the session handler.\n this._onError?.(err);\n }\n\n // -------------------------------------------------------------------------\n // Channel subscription handler\n // -------------------------------------------------------------------------\n\n private _handleChannelMessage(msg: Ably.InboundMessage): void {\n try {\n if (msg.name === EVENT_CANCEL) {\n // Fire-and-forget async handler — errors are caught internally.\n this._handleCancelMessage(msg).catch((error: unknown) => {\n const errInfo = new Ably.ErrorInfo(\n `unable to route cancel message; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.CancelListenerError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n this._logger?.error('DefaultAgentSession._handleChannelMessage(); cancel routing error');\n this._onError?.(errInfo);\n });\n return;\n }\n\n // Dispatch client-published input events to the lookup registered\n // for their `event-id`. Every client-originated event in an\n // invocation (user-message AND amend events such as tool-approval\n // responses and client tool outputs) carries `event-id`; the lookup\n // waits for every promised id to arrive before letting the run start\n // LLM work. Routing by `event-id` rather than `invocation-id` keeps\n // the dispatcher independent of any client-minted invocation\n // identity. Server-side lifecycle messages (run-start, run-end,\n // cancel, error) never stamp `event-id`, so they're naturally\n // excluded.\n const headers = getTransportHeaders(msg);\n const eventId = headers[HEADER_EVENT_ID];\n if (eventId !== undefined) {\n const listener = this._pendingInputEventLookups.get(eventId);\n if (listener) {\n listener(msg);\n } else {\n // Buffer for a future `_registerInputEventListener` call. This is\n // load-bearing for the \"agent attaches after publish\" scenario\n // where channel rewind delivers user messages before\n // `run.start()` runs.\n const existing = this._inputEventBuffer.get(eventId);\n if (existing) {\n existing.push(msg);\n } else {\n // FIFO eviction: drop the oldest event entry (and all its buffered\n // redeliveries). Clients whose input event was evicted will fail\n // their lookup with `InputEventNotFound` — this warn is the only\n // operator-visible signal that capacity caused the failure.\n const evicted = evictOldestIfFull(this._inputEventBuffer, eventId, this._inputEventBufferLimit);\n if (evicted !== undefined) {\n this._logger?.warn(\n 'DefaultAgentSession._handleChannelMessage(); input-event buffer full, dropping oldest entry',\n { evictedEventId: evicted, limit: this._inputEventBufferLimit },\n );\n }\n this._inputEventBuffer.set(eventId, [msg]);\n }\n }\n }\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to process channel message; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n this._logger?.error('DefaultAgentSession._handleChannelMessage(); subscription error');\n this._onError?.(errInfo);\n }\n }\n\n // -------------------------------------------------------------------------\n // Connection guard\n // -------------------------------------------------------------------------\n\n private async _requireConnected(method: string): Promise<void> {\n if (!this._connectPromise) {\n throw new Ably.ErrorInfo(\n `unable to ${method}; connect() must be called before ${method}()`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n return this._connectPromise;\n }\n\n // -------------------------------------------------------------------------\n // Run creation\n // -------------------------------------------------------------------------\n\n private _createRun(invocation: Invocation, runtime: RunRuntime<TOutput>): Run<TOutput, TProjection, TMessage> {\n // The run-id is not carried in the invocation body — the agent mints it.\n // Mint a provisional id now (or take the `runtime.runId` override for\n // tests / in-process drivers) — this IS the id for a fresh run. A\n // continuation overrides it in `Run.start()` with the existing run-id read\n // off the triggering input event's wire headers (the run it re-enters).\n // Mirrors the invocationId mint below.\n let runId = runtime.runId ?? crypto.randomUUID();\n // The agent mints the invocation id — one per HTTP request that invokes\n // it. A per-run override (runtime.invocationId) supports deterministic ids\n // in tests and in-process drivers.\n const invocationId = runtime.invocationId ?? crypto.randomUUID();\n const inputEventLookupTimeoutMs = this._inputEventLookupTimeoutMs;\n const { onMessage, onCancelled, onCancel, onError: runOnError, signal: externalSignal } = runtime;\n\n const controller = new AbortController();\n let state = RunState.INITIALIZED;\n\n // Compose the internal controller signal with the external signal (e.g.\n // req.signal) so platform-level cancellation (request cancellation, function\n // timeout) cancels the run through the same path as Ably cancel messages.\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n\n // Spec: AIT-ST3a — register immediately so `close()` aborts an in-flight\n // start() and a post-lookup cancel can fire the AbortSignal. Keyed by the\n // provisional run-id; a continuation re-keys to the real id in start()\n // once the triggering input reveals it.\n const registration: RegisteredRun = {\n runId,\n invocationId,\n controller,\n signal,\n onCancel,\n onError: runOnError,\n };\n this._registeredRuns.set(runId, registration);\n\n // Capture instance members as locals so arrow functions close over them\n // without needing `this` (avoids unicorn/no-this-assignment).\n const logger = this._logger;\n const runManager = this._runManager;\n const codec = this._codec;\n const channel = this._channel;\n const registeredRuns = this._registeredRuns;\n const runIdByInputCodecMessageId = this._runIdByInputCodecMessageId;\n const deferredCancels = this._deferredCancels;\n const requireConnected = this._requireConnected.bind(this);\n const registerInputEventListener = this._registerInputEventListener.bind(this);\n const pullDeferredCancel = this._pullDeferredCancel.bind(this);\n const inputEventId = invocation.inputEventId;\n\n // `viewMessages` starts empty. `Run.start()` populates it via the\n // channel-rewind input-event lookup, pulling in user-message MessageNodes\n // as they arrive on the channel.\n const viewMessages: MessageNode<TMessage>[] = [];\n const view: RunView<TMessage> = {\n get messages() {\n return viewMessages;\n },\n };\n\n // Per-run metadata resolved from the input-event lookup result. The first\n // matched wire message's headers carry the run's `clientId`, `parent`, and\n // `forkOf`, and — for a continuation — the `run-id` it re-enters (a fresh\n // input carries none; the client stamps a run-id only when re-entering a\n // run it already knows). Its Ably-level publisher `clientId` becomes the\n // `inputClientId` re-stamped on the agent's own publishes. Captured\n // separately from `viewMessages` because tool-resolution wire messages\n // (`tool-output-available` etc.) decode to chunks and produce zero\n // MessageNodes — the metadata still needs to surface.\n let resolvedClientId: string | undefined;\n let resolvedInputClientId: string | undefined;\n let resolvedParent: string | undefined;\n let resolvedForkOf: string | undefined;\n let resolvedRegenerates: string | undefined;\n let resolvedInputCodecMessageId: string | undefined;\n let resolvedContinuation = false;\n let firstLookupHeaders: Record<string, string> | undefined;\n /**\n * The reply run's structural-parent fallback, computed once in\n * `Run.start()` (after the input-event lookup has populated `viewMessages`)\n * and consumed by every `Run.pipe()` publish. A per-stream\n * `streamOpts.parent` still overrides it. Storing it here keeps it stable\n * across pipes and decouples the assistant's structural parent from the\n * run-start wire's own `parent`.\n */\n let assistantParentFallback: string | undefined;\n /**\n * Raw Ably messages observed live by the input-event lookup. Passed to\n * `loadRunProjection` so the just-published client wires don't need\n * to wait on Ably's channel history indexing window. Empty when no\n * lookup ran or no messages matched.\n */\n let liveLookupMessages: readonly Ably.InboundMessage[] | undefined;\n\n /**\n * Remove this run from the session's routing maps. Drops the\n * `_registeredRuns` entry plus the `input-codec-message-id → run-id`\n * reverse index (and any stale deferred cancel still buffered for that\n * input), keeping the cancel-routing state consistent when the run ends,\n * suspends, or its start fails.\n */\n const deregisterRun = (): void => {\n registeredRuns.delete(runId);\n if (resolvedInputCodecMessageId !== undefined) {\n runIdByInputCodecMessageId.delete(resolvedInputCodecMessageId);\n deferredCancels.delete(resolvedInputCodecMessageId);\n }\n };\n\n // Most recently loaded projection for this run only, cached by\n // `Run.loadProjection()` and `Run.loadConversation()` so the `messages`\n // getter can return the run's folded messages. `undefined` before any\n // load call; the getter then falls back to the live `viewMessages`.\n let cachedProjection: TProjection | undefined;\n\n // Full multi-turn conversation, set by `Run.loadConversation()`. When set,\n // it takes priority over `cachedProjection` in the `messages` getter —\n // the getter then returns the complete ancestor-chain + current-run\n // messages instead of the current run alone.\n let cachedConversation: TMessage[] | undefined;\n\n const run: Run<TOutput, TProjection, TMessage> = {\n get runId() {\n return runId;\n },\n get invocationId() {\n return invocationId;\n },\n get abortSignal() {\n return signal;\n },\n get view() {\n return view;\n },\n get messages() {\n if (cachedConversation !== undefined) {\n return [...cachedConversation];\n }\n if (cachedProjection !== undefined) {\n return codec.getMessages(cachedProjection).map((m) => m.message);\n }\n return viewMessages.map((n) => n.message);\n },\n\n // Spec: AIT-ST4, AIT-ST4a, AIT-ST4b\n start: async (): Promise<void> => {\n logger?.trace('Run.start();', { runId, inputEventId });\n\n await requireConnected('start');\n\n // Spec: AIT-ST4a\n if (signal.aborted) {\n throw new Ably.ErrorInfo(\n `unable to start run; run ${runId} was cancelled before start()`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n if (state !== RunState.INITIALIZED) return;\n state = RunState.STARTED;\n\n // Look up the triggering input event on the channel so the agent\n // can read the user's message and per-run metadata (parent, forkOf,\n // continuation flag) before publishing run-start. Skip when\n // inputEventLookupTimeoutMs === 0 (tests and in-process drivers) or\n // when no inputEventId is set (invocation requires no channel lookup).\n if (inputEventId && inputEventLookupTimeoutMs > 0) {\n try {\n const found = await lookupInputEvents<TInput, TOutput, TProjection, TMessage>({\n register: (callback) => registerInputEventListener([inputEventId], callback),\n codec,\n invocationId,\n runId,\n expectedInputEventIds: [inputEventId],\n timeoutMs: inputEventLookupTimeoutMs,\n signal,\n logger,\n });\n for (const m of found.nodes) viewMessages.push(m);\n if (found.firstHeaders !== undefined) firstLookupHeaders = found.firstHeaders;\n if (found.firstClientId !== undefined) resolvedInputClientId = found.firstClientId;\n liveLookupMessages = found.rawMessages;\n } catch (error) {\n const errInfo =\n error instanceof Ably.ErrorInfo\n ? error\n : new Ably.ErrorInfo(\n `unable to look up input event; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.InputEventNotFound,\n 504,\n );\n // The rejection bubbles up to the developer's HTTP handler,\n // which surfaces the failure as a non-2xx response — that is\n // the signal the client sees. No channel publish: an\n // `ai-run-end` without a preceding `ai-run-start` would break\n // the lifecycle invariant for other channel observers.\n deregisterRun();\n logger?.error('Run.start(); input-event lookup failed', { runId, invocationId });\n throw errInfo;\n }\n }\n\n // Resolve per-run metadata from the first matched wire message's\n // headers — they carry `clientId`, `parent`, and `forkOf`.\n // Continuations of a suspended run pick up the suspended assistant's\n // parent in the same headers (the continuation wire message parents off\n // the assistant). A `run-id` on the triggering input marks a\n // continuation (re-entry via `ai-run-resume`); a fresh input carries\n // none and opens the run with `ai-run-start`. Fall back to the first\n // MessageNode's headers for the path where the lookup ran with\n // `viewMessages` already populated and no `firstHeaders` was captured.\n const sourceHeaders = firstLookupHeaders ?? viewMessages[0]?.headers;\n if (sourceHeaders) {\n resolvedClientId = sourceHeaders[HEADER_RUN_CLIENT_ID];\n resolvedParent = sourceHeaders[HEADER_PARENT];\n resolvedForkOf = sourceHeaders[HEADER_FORK_OF];\n resolvedRegenerates = sourceHeaders[HEADER_MSG_REGENERATE];\n resolvedInputCodecMessageId = sourceHeaders[HEADER_CODEC_MESSAGE_ID];\n\n // The triggering input's run-id (if any) IS this run's identity.\n // Present → a continuation re-entering that run: adopt the id,\n // overriding the provisional one minted at construction, and re-key\n // the registration so cancel routing / deregistration resolve to the\n // real run. Absent → a fresh run: the provisional id stands and the\n // run opens with run-start.\n const wireRunId = sourceHeaders[HEADER_RUN_ID];\n resolvedContinuation = wireRunId !== undefined;\n if (wireRunId !== undefined && wireRunId !== runId) {\n registeredRuns.delete(runId);\n runId = wireRunId;\n registration.runId = runId;\n registeredRuns.set(runId, registration);\n }\n }\n\n // Compute the reply run's structural-parent fallback now that the\n // lookup has populated `viewMessages`: the triggering user message,\n // or — for regenerate wires that match by inputEventId but produce no\n // MessageNodes — the input wire's own `parent`. `Run.pipe()` consumes\n // this for every assistant publish.\n assistantParentFallback = viewMessages.at(-1)?.codecMessageId ?? resolvedParent;\n\n // The triggering input's codec-message-id is now resolved, so the\n // `input-codec-message-id → run` linkage exists: index it for live\n // cancels and pull any cancel that arrived before the run was known\n // (a fresh-send cancel published before the agent minted this run-id).\n // Honouring it here may abort the controller before run-start; that is\n // fine — the abort propagates through the same signal a normal cancel\n // would use.\n if (resolvedInputCodecMessageId !== undefined) {\n runIdByInputCodecMessageId.set(resolvedInputCodecMessageId, runId);\n await pullDeferredCancel(registration, resolvedInputCodecMessageId);\n }\n\n try {\n await runManager.startRun(runId, resolvedClientId, controller, {\n // Stamp the reply run's STRUCTURAL parent (its input node, M_user) —\n // the same value the output path stamps — not the input wire's own\n // parent. Makes `parent` structural on every wire so the Tree's two\n // creation paths agree regardless of arrival order. Valid only now\n // that M_user is a separate input node (the two-node flip).\n parent: assistantParentFallback,\n forkOf: resolvedForkOf,\n regenerates: resolvedRegenerates,\n invocationId,\n inputClientId: resolvedInputClientId,\n inputCodecMessageId: resolvedInputCodecMessageId,\n continuation: resolvedContinuation,\n });\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to publish run-start for run ${runId}; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.RunLifecycleError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n logger?.error('Run.start(); failed to publish run-start', { runId });\n throw errInfo;\n }\n\n logger?.debug('Run.start(); run started', { runId, inputEventId });\n },\n\n // Spec: AIT-ST5c\n addEvents: async (nodes: EventsNode<TOutput>[]): Promise<void> => {\n logger?.trace('Run.addEvents();', { runId, count: nodes.length });\n\n await requireConnected('addEvents');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to add events; start() must be called before addEvents() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n const runOwnerClientId = runManager.getClientId(runId);\n\n try {\n for (const node of nodes) {\n const headers = buildTransportHeaders({\n role: 'assistant',\n runId,\n codecMessageId: node.codecMessageId,\n runClientId: runOwnerClientId,\n invocationId,\n inputClientId: resolvedInputClientId,\n inputCodecMessageId: resolvedInputCodecMessageId,\n });\n\n const encoder = codec.createEncoder(channel, {\n extras: { headers },\n onMessage,\n });\n\n for (const event of node.events) {\n await encoder.publishOutput(event);\n }\n\n await encoder.close();\n }\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to publish events for run ${runId}; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.RunLifecycleError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n logger?.error('Run.addEvents(); publish failed', { runId });\n throw errInfo;\n }\n\n logger?.debug('Run.addEvents(); events published', { runId, count: nodes.length });\n },\n\n loadProjection: async (): Promise<TProjection> => {\n logger?.trace('Run.loadProjection();', { runId });\n await requireConnected('loadProjection');\n const projection = await loadRunProjection<TInput, TOutput, TProjection, TMessage>({\n channel,\n codec,\n runId,\n signal,\n logger,\n liveMessages: liveLookupMessages,\n });\n cachedProjection = projection;\n return projection;\n },\n\n loadConversation: async (options?: LoadConversationOptions): Promise<TMessage[]> => {\n logger?.trace('Run.loadConversation();', { runId });\n await requireConnected('loadConversation');\n const { messages, projection } = await loadConversation<TInput, TOutput, TProjection, TMessage>({\n channel,\n codec,\n runId,\n signal,\n logger,\n liveMessages: liveLookupMessages,\n assistantParentFallback,\n pageLimit: options?.pageLimit ?? 200,\n maxMessages: options?.maxMessages ?? 2000,\n });\n cachedProjection = projection;\n cachedConversation = messages;\n return messages;\n },\n\n // Spec: AIT-ST6, AIT-ST6a, AIT-ST6b, AIT-ST6b1, AIT-ST6b2, AIT-ST6b3, AIT-ST6c\n pipe: async (stream: ReadableStream<TOutput>, streamOpts?: PipeOptions<TOutput>): Promise<StreamResult> => {\n logger?.trace('Run.pipe();', { runId });\n\n await requireConnected('pipe');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to pipe stream; start() must be called before pipe() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n const runOwnerClientId = runManager.getClientId(runId);\n\n // The assistant message's parent: an explicit per-stream\n // `streamOpts.parent` from the caller, else the reply run's\n // structural-parent fallback computed once at run-start\n // (`assistantParentFallback` — the triggering user message, or the\n // input wire's own parent for regenerate wires that produced no\n // MessageNodes). Owning the default here means agent routes don't have\n // to pass `{ parent: lastUserCodecMessageId }` to keep tree threading\n // correct; edit-then-regenerate sibling resolution relies on the\n // user→assistant chain being explicit.\n const assistantParent = streamOpts?.parent ?? assistantParentFallback;\n const assistantForkOf = streamOpts?.forkOf ?? resolvedForkOf;\n // Echo `msg-regenerate` on the assistant wire so that a\n // client receiving the assistant chunk before `ai-run-start`\n // (e.g. via history pagination across a page boundary, or a lost\n // lifecycle publish) can still populate `RunNode.regeneratesCodecMessageId`\n // when creating the Run from headers. Mirrors the symmetric\n // behaviour for `assistantForkOf` on edit runs.\n const assistantRegenerates = resolvedRegenerates;\n\n const codecMessageId = crypto.randomUUID();\n const defaultHeaders = buildTransportHeaders({\n role: 'assistant',\n runId,\n codecMessageId,\n runClientId: runOwnerClientId,\n parent: assistantParent,\n forkOf: assistantForkOf,\n invocationId,\n inputClientId: resolvedInputClientId,\n inputCodecMessageId: resolvedInputCodecMessageId,\n regenerates: assistantRegenerates,\n });\n const encoder = codec.createEncoder(channel, {\n extras: { headers: defaultHeaders },\n onMessage,\n messageId: codecMessageId,\n });\n\n const result = await pipeStream(stream, encoder, signal, onCancelled, streamOpts?.resolveWriteOptions, logger);\n\n if (result.error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to pipe response for run ${runId}; ${result.error.message}`,\n ErrorCode.StreamError,\n 500,\n result.error instanceof Ably.ErrorInfo ? result.error : undefined,\n );\n logger?.error('Run.pipe(); stream error', { runId });\n runOnError?.(errInfo);\n }\n\n logger?.debug('Run.pipe(); stream finished', { runId, reason: result.reason });\n return result;\n },\n\n suspend: async (): Promise<void> => {\n logger?.trace('Run.suspend();', { runId });\n\n await requireConnected('suspend');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to suspend run; start() must be called before suspend() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n // ENDED is the terminal state for either an end or a suspend on this\n // Run instance; a second terminal call is a no-op.\n if (state === RunState.ENDED) return;\n state = RunState.ENDED;\n\n try {\n await runManager.suspendRun(runId, invocationId, resolvedInputClientId, resolvedInputCodecMessageId);\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to publish run-suspend for run ${runId}; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.RunLifecycleError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n logger?.error('Run.suspend(); failed to publish run-suspend', { runId });\n throw errInfo;\n } finally {\n deregisterRun();\n }\n\n logger?.debug('Run.suspend(); run suspended', { runId });\n },\n\n // Spec: AIT-ST7, AIT-ST7a, AIT-ST7b\n end: async (reason: RunEndReason): Promise<void> => {\n logger?.trace('Run.end();', { runId, reason });\n\n await requireConnected('end');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to end run; start() must be called before end() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n if (state === RunState.ENDED) return;\n state = RunState.ENDED;\n\n try {\n await runManager.endRun(runId, reason, invocationId, resolvedInputClientId, resolvedInputCodecMessageId);\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to publish run-end for run ${runId}; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.RunLifecycleError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n logger?.error('Run.end(); failed to publish run-end', { runId });\n throw errInfo;\n } finally {\n deregisterRun();\n }\n\n logger?.debug('Run.end(); run ended', { runId, reason });\n },\n };\n\n return run;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an agent (server-side) session bound to the given Realtime client\n * and channel name. The caller owns the client's lifecycle; the session\n * owns its channel.\n * @param options - Session configuration.\n * @returns A new {@link AgentSession} instance.\n */\nexport const createAgentSession = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n options: AgentSessionOptions<TInput, TOutput, TProjection, TMessage>,\n): AgentSession<TOutput, TProjection, TMessage> => new DefaultAgentSession(options);\n","/**\n * Invocation — value object wrapping the JSON body that a client sends to\n * an agent's HTTP endpoint to start a run.\n *\n * The data shape is the wire contract; the {@link Invocation} class is a\n * runtime view of that data with the same fields. {@link Invocation.fromJSON}\n * is the entry point used by agent handlers:\n *\n * ```ts\n * const data = (await req.json()) as InvocationData;\n * const invocation = Invocation.fromJSON(data);\n * const run = session.createRun(invocation, { signal: req.signal });\n * await run.start();\n * await run.loadProjection(); // fetch run projection from the channel\n * const messages = run.messages;\n * ```\n *\n * The body carries only what the agent needs out-of-band before the channel\n * is observable: the session/channel name and the `inputEventId` that triggered\n * the invocation. The agent mints the `invocationId` itself (one per HTTP\n * request) and returns it on the HTTP response, so it is not a body field. Run\n * identity also lives on the channel: the agent mints the `runId` for a fresh\n * run and reads the existing `runId` off the triggering input event for a\n * continuation — so the body carries no run-id either. Per-message metadata —\n * `clientId`, `parent`, `forkOf`, continuation status — likewise lives on the\n * channel and is resolved by the agent from the triggering input event, not\n * from the body. The `inputClientId` the agent re-stamps on its own publishes\n * comes from the publisher's Ably `clientId` on the matched input event, not\n * from a body field.\n */\n\n// ---------------------------------------------------------------------------\n// Wire shape\n// ---------------------------------------------------------------------------\n\n/**\n * Wire shape of a single invocation — the JSON body sent from the client\n * transport's HTTP POST to the agent endpoint.\n */\nexport interface InvocationData {\n /**\n * Identifier for the specific input event on the channel that triggered\n * this invocation. The agent locates the event via the `event-id`\n * header. Its wire headers carry the run-id for a continuation (absent for\n * a fresh run), so run identity is resolved from the channel, not the body.\n */\n inputEventId: string;\n /** Logical name of the session (chat) — used as the Ably channel name. */\n sessionName: string;\n}\n\n// ---------------------------------------------------------------------------\n// Runtime view\n// ---------------------------------------------------------------------------\n\n/**\n * Runtime view of an {@link InvocationData}. Constructed via\n * {@link Invocation.fromJSON}. Read-only; carries no behaviour beyond\n * exposing its fields.\n */\n// Spec: AIT-ST13\nexport class Invocation {\n /**\n * Identifier for the specific input event on the channel that triggered\n * this invocation. Run identity is resolved from that event's wire headers\n * (or minted), not from the body.\n */\n readonly inputEventId: string;\n /** Logical name of the session (chat). Used as the Ably channel name. */\n readonly sessionName: string;\n\n private constructor(data: InvocationData) {\n this.inputEventId = data.inputEventId;\n this.sessionName = data.sessionName;\n }\n\n /**\n * Build an Invocation from its JSON wire shape.\n * @param data - Parsed JSON body matching {@link InvocationData}.\n * @returns A new Invocation exposing the same fields.\n */\n static fromJSON(data: InvocationData): Invocation {\n return new Invocation(data);\n }\n\n /**\n * Serialise this invocation to its JSON wire shape — the body a client\n * POSTs to the agent's endpoint to wake a run. Round-trips through\n * {@link Invocation.fromJSON}.\n * @returns The {@link InvocationData} carrying this invocation's identity.\n */\n toJSON(): InvocationData {\n return {\n inputEventId: this.inputEventId,\n sessionName: this.sessionName,\n };\n }\n}\n","/**\n * Tree — materializes a branching conversation as a forest of nodes. Each turn\n * is two nodes: a user {@link InputNode} keyed by its client-owned\n * codec-message-id and an agent {@link RunNode} keyed by the agent-minted\n * run-id, parented to the input node.\n *\n * Each node holds a per-node codec {@link TProjection} which the Tree folds\n * from inbound events. The Tree owns the complete conversation state across\n * every observed node. The {@link View} walks the parent chain to extract a\n * flat message list for rendering.\n *\n * `applyMessage()` is the entry point for inbound channel messages — it\n * classifies a run-less user input into an input node (keyed by\n * codec-message-id) or routes a run-bearing wire to its reply run (keyed by\n * run-id), folds events into that node's projection, and maintains a secondary\n * `codecMessageId -> nodeKey` index. `applyRunLifecycle()` handles run-start /\n * run-suspend / run-resume / run-end events.\n *\n * Sibling structure: editing a prompt produces a sibling input node linked by\n * {@link InputNode.forkOf}; regenerating a reply produces a sibling reply run\n * sharing the same input-node parent (no fork-of).\n */\n\nimport type * as Ably from 'ably';\n\nimport {\n HEADER_CODEC_MESSAGE_ID,\n HEADER_FORK_OF,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_INVOCATION_ID,\n HEADER_MSG_REGENERATE,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_RUN_CLIENT_ID,\n HEADER_RUN_ID,\n} from '../../constants.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport type { Logger } from '../../logger.js';\nimport type { CodecInputEvent, CodecOutputEvent, Reducer } from '../codec/types.js';\nimport type { ConversationNode, InputNode, OutputEvent, RunLifecycleEvent, RunNode, Tree } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Internal node type\n// ---------------------------------------------------------------------------\n\ninterface InternalNode<TProjection> {\n node: ConversationNode<TProjection>;\n /** Insertion sequence — tiebreaker for nodes with no sort serial (optimistic). */\n insertSeq: number;\n}\n\n/**\n * The primary key a node is indexed under: a reply run's `runId`, or an input\n * node's `codecMessageId` (the client owns it before the agent mints a runId).\n * @param node - The node to key.\n * @returns The node's primary key.\n */\nexport const nodeKey = <TProjection>(node: ConversationNode<TProjection>): string =>\n node.kind === 'run' ? node.runId : node.codecMessageId;\n\n/**\n * The serial a node sorts by: a reply run's `startSerial`, an input node's\n * `serial`. Undefined for an optimistic (not-yet-acked) node, which tail-sorts.\n * @param node - The node to read.\n * @returns The sort serial, or undefined for an optimistic node.\n */\nconst sortSerial = <TProjection>(node: ConversationNode<TProjection>): string | undefined =>\n node.kind === 'run' ? node.startSerial : node.serial;\n\n/**\n * Add a value to a `Map<K, Set<V>>`, creating the bucket Set on first use.\n * @param map - The Map to mutate.\n * @param key - The bucket key.\n * @param value - The value to add.\n */\nconst addToSetMap = <K, V>(map: Map<K, Set<V>>, key: K, value: V): void => {\n let set = map.get(key);\n if (!set) {\n set = new Set();\n map.set(key, set);\n }\n set.add(value);\n};\n\n/**\n * Remove a value from a `Map<K, Set<V>>`, dropping the bucket when it empties.\n * @param map - The Map to mutate.\n * @param key - The bucket key.\n * @param value - The value to remove.\n */\nconst deleteFromSetMap = <K, V>(map: Map<K, Set<V>>, key: K, value: V): void => {\n const set = map.get(key);\n if (!set) return;\n set.delete(value);\n if (set.size === 0) map.delete(key);\n};\n\n// ---------------------------------------------------------------------------\n// Internal interface — extended surface consumed by View / ClientSession\n// ---------------------------------------------------------------------------\n\n/** Internal tree surface used by View and ClientSession — not part of the public Tree API. */\nexport interface TreeInternal<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n> extends Tree<TOutput, TProjection> {\n /**\n * Walk the visible node chain (both input nodes and reply runs) along the\n * selected branches, in chronological order. The View renders from this.\n * @param selections - Per-group selected member key, keyed by group root.\n * @returns The visible nodes in chronological order.\n */\n visibleNodes(selections?: Map<string, string>): ConversationNode<TProjection>[];\n\n /**\n * Get the \"group root\" key for a sibling group — the stable key the\n * selection map is keyed by (the earliest edit version for input nodes, the\n * original reply for a regenerate group).\n */\n getGroupRoot(key: string): string;\n\n /**\n * The reply runs parented at an input node (its codec-message-id), in\n * iteration order. Empty when none have been observed. Used to resolve a\n * user prompt to its reply run(s).\n * @param inputCodecMessageId - The input node's codec-message-id.\n * @returns The reply runs parented at that input.\n */\n getReplyRuns(inputCodecMessageId: string): RunNode<TProjection>[];\n\n /**\n * Apply an inbound channel message to the tree.\n *\n * Classifies the message and routes it to the owning node:\n * 1. Run-less user input (no run-id, a `user`-role message carrying a\n * codec-message-id and input events): creates or promotes the input node\n * keyed by that codec-message-id, folds the input events.\n * 2. Run-bearing wire (assistant output, continuation tool-resolution, or a\n * fresh agent-minted run): routes to the reply run by run-id (reconciling\n * an optimistic insert by codec-message-id), folds events.\n * @param events - Decoded codec events, split by wire direction. Both are\n * folded into the node's projection, inputs first.\n * @param events.inputs - Client-published events (`ai-input` wire).\n * @param events.outputs - Agent-published events (`ai-output` wire).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for optimistic inserts.\n */\n applyMessage(\n events: { inputs: TInput[]; outputs: TOutput[] },\n headers: Record<string, string>,\n serial?: string,\n ): void;\n\n /**\n * Apply a run-lifecycle event.\n *\n * - `start`: creates the reply run (if missing) or, for an existing run,\n * sets RunNode.status to 'active', promotes startSerial, and backfills\n * structural metadata (parent / forkOf / regenerates / invocationId).\n * - `suspend`: sets RunNode.status to 'suspended' and records `endSerial`.\n * The run stays live so a resume under the same `runId` picks up where it\n * left off.\n * - `resume`: re-activates an existing suspended Run (status back to\n * 'active') without touching its structure or serials — a pure re-entry\n * signal. A no-op if the Run is not yet known.\n * - `end`: sets RunNode.status to the terminal reason and records\n * `endSerial`.\n *\n * Always emits a 'run' event to subscribers.\n * @param event - Lifecycle event payload, including the channel serial.\n */\n applyRunLifecycle(event: RunLifecycleEvent): void;\n\n /**\n * Get the node keyed by `key`, or undefined if `key` names no node. The\n * key is a {@link nodeKey} — a runId (reply run) or an input node's\n * codec-message-id — so the result is a {@link ConversationNode} union:\n * narrow on `kind` before reading kind-specific fields. Pairs with\n * {@link getNodeByCodecMessageId}, which resolves an arbitrary owned\n * codec-message-id (including an assistant message's) to its node.\n * @param key - The node key to look up.\n * @returns The node, or undefined if not found.\n */\n getNode(key: string): ConversationNode<TProjection> | undefined;\n\n /**\n * Remove a node from the tree by its key ({@link nodeKey} — a runId or an\n * input node's codec-message-id). Children become unreachable because their\n * parent is no longer on the active path.\n * @param key - The node key to remove.\n */\n delete(key: string): void;\n\n /** Forward a raw Ably message event to tree subscribers. */\n emitAblyMessage(msg: Ably.InboundMessage): void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/** EventEmitter events map for the tree. */\ninterface TreeEventsMap<TOutput extends CodecOutputEvent> {\n update: undefined;\n 'ably-message': Ably.InboundMessage;\n run: RunLifecycleEvent;\n output: OutputEvent<TOutput>;\n}\n\n// Spec: AIT-CT13\nexport class DefaultTree<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n> implements TreeInternal<TInput, TOutput, TProjection> {\n private readonly _codec: Reducer<TInput | TOutput, TProjection>;\n private readonly _logger: Logger;\n private readonly _emitter: EventEmitter<TreeEventsMap<TOutput>>;\n\n /**\n * All nodes indexed by their primary key ({@link nodeKey}): a reply run's\n * runId, or an input node's codec-message-id.\n */\n private readonly _nodeIndex = new Map<string, InternalNode<TProjection>>();\n\n /**\n * Maps every observed `codec-message-id` to its owning node's key\n * ({@link nodeKey}). For a reply run that is the runId of every message the\n * run published; for an input node it is the input's own codec-message-id.\n * Resolves fork-of / parent codec-message-ids to node keys, routes\n * continuation amend wires to existing nodes, and backs UI lookups that hold\n * a codec-message-id.\n */\n private readonly _codecMessageIdToNodeKey = new Map<string, string>();\n\n /**\n * All nodes sorted by their sort serial ({@link sortSerial}: `startSerial`\n * for runs, `serial` for input nodes), lexicographically. Nodes with no sort\n * serial (optimistic) sort after all serial-bearing nodes, ordered among\n * themselves by insertion sequence.\n */\n private readonly _sortedNodes: InternalNode<TProjection>[] = [];\n\n /**\n * Parent index: parent node key (the key its children's\n * `parentCodecMessageId` resolves to) to the set of child node keys. Root\n * nodes (no parent) are indexed under the key `undefined`. Kind-blind — a\n * reply run and an input node parent off each other through the same index.\n */\n private readonly _parentIndex = new Map<string | undefined, Set<string>>();\n\n /**\n * Reverse edge: an input node's codec-message-id to the set of reply-run ids\n * parented at it. Lets the View resolve a user prompt to its (selected) reply\n * run, and groups regenerate siblings (which all parent at the same input\n * node).\n */\n private readonly _replyRunsByInput = new Map<string, Set<string>>();\n\n /** Monotonically increasing counter for insertion sequence. */\n private _seqCounter = 0;\n\n /** Incremented on structural changes; unchanged on projection-only updates. */\n private _structuralVersion = 0;\n\n /**\n * Cached sibling-group lookups keyed by node key. The walk over forkOf\n * chains and the per-parent fan-out are pure functions of the node\n * graph, so the cache is keyed against {@link _structuralVersion}:\n * any topology mutation drops the cache and the next lookup\n * recomputes. Hits matter most during a single render pass where\n * the View calls `getSiblingNodes` once per visible node plus extra\n * per-message branch-anchor probes from React components.\n */\n private _siblingCache = new Map<string, InternalNode<TProjection>[]>();\n private _siblingCacheVersion = -1;\n\n constructor(codec: Reducer<TInput | TOutput, TProjection>, logger: Logger) {\n this._codec = codec;\n this._logger = logger;\n this._emitter = new EventEmitter<TreeEventsMap<TOutput>>(logger);\n }\n\n // -------------------------------------------------------------------------\n // Sorted list maintenance\n // -------------------------------------------------------------------------\n\n /**\n * Compare two nodes (Run or input) for sorted list ordering.\n * Serial-bearing nodes sort by their sort serial (`startSerial` for runs,\n * `serial` for input nodes), lexicographically.\n * Nodes with no sort serial sort after all serial-bearing nodes.\n * Among them, sort by insertion sequence.\n *\n * Optimistic (null-serial) nodes intentionally tail-sort so they reorder\n * into place when the server relay arrives and `applyMessage` promotes\n * startSerial — see {@link applyMessage}'s `_removeSortedNode` /\n * `_insertSortedNode` pair on the promotion path.\n * @param a - First node to compare.\n * @param b - Second node to compare.\n * @returns Negative if a sorts before b, positive if after, zero if equal.\n */\n // Spec: AIT-CT13a\n private _compareNodes(a: InternalNode<TProjection>, b: InternalNode<TProjection>): number {\n const sa = sortSerial(a.node);\n const sb = sortSerial(b.node);\n if (sa === undefined && sb === undefined) return a.insertSeq - b.insertSeq;\n if (sa === undefined) return 1;\n if (sb === undefined) return -1;\n if (sa < sb) return -1;\n if (sa > sb) return 1;\n return a.insertSeq - b.insertSeq;\n }\n\n /**\n * Insert a node into the sorted list at the correct position via binary search.\n * @param internal - The node to insert.\n */\n private _insertSortedNode(internal: InternalNode<TProjection>): void {\n const startSerial = sortSerial(internal.node);\n\n // Fast path: null-startSerial always appends to end.\n if (startSerial === undefined) {\n this._sortedNodes.push(internal);\n return;\n }\n\n let lo = 0;\n let hi = this._sortedNodes.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const midNode = this._sortedNodes[mid];\n if (!midNode) break; // unreachable\n if (this._compareNodes(midNode, internal) <= 0) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n this._sortedNodes.splice(lo, 0, internal);\n }\n\n /**\n * Remove a node from the sorted list.\n * @param internal - The node to remove.\n */\n private _removeSortedNode(internal: InternalNode<TProjection>): void {\n const idx = this._sortedNodes.indexOf(internal);\n if (idx !== -1) this._sortedNodes.splice(idx, 1);\n }\n\n /**\n * Insert a freshly-created node into the primary store, the parent index, and\n * the sorted list, then bump the structural version. Kind-specific secondary\n * indexing — the codec-message-id map for input nodes, the reply→input edge\n * for reply runs — is the caller's responsibility.\n * @param key - The node's primary key ({@link nodeKey}).\n * @param entry - The internal node to insert.\n * @param parentCodecMessageId - The node's structural parent, or undefined for a root.\n */\n private _insertNode(key: string, entry: InternalNode<TProjection>, parentCodecMessageId: string | undefined): void {\n this._nodeIndex.set(key, entry);\n this._addToParentIndex(parentCodecMessageId, key);\n this._insertSortedNode(entry);\n this._structuralVersion++;\n }\n\n /**\n * Re-sort a node whose sort key just changed and bump the structural version.\n * The caller mutates the serial field (`serial` for input nodes, `startSerial`\n * for runs); this keeps the sorted list and version in step. Used on the\n * optimistic-serial promotion paths when the server relay/echo arrives.\n * @param entry - The internal node whose serial was just promoted.\n */\n private _promoteSerial(entry: InternalNode<TProjection>): void {\n this._removeSortedNode(entry);\n this._insertSortedNode(entry);\n this._structuralVersion++;\n }\n\n /**\n * Fold a batch of events into a node's projection in place, isolating each\n * fold in a try/catch so a throwing reducer can't abort the rest of the batch\n * or the surrounding apply.\n * @param entry - The internal node whose projection is folded in place.\n * @param events - The decoded events to fold, in wire order.\n * @param serial - Ably channel serial; coerced to '' for an optimistic insert.\n * @param messageId - The reducer routing key (codec-message-id), or undefined.\n */\n private _foldInto(\n entry: InternalNode<TProjection>,\n events: (TInput | TOutput)[],\n serial: string | undefined,\n messageId: string | undefined,\n ): void {\n for (const event of events) {\n try {\n entry.node.projection = this._codec.fold(entry.node.projection, event, { serial: serial ?? '', messageId });\n } catch (error) {\n this._logger.error('Tree._foldInto(); fold threw', { key: nodeKey(entry.node), messageId, err: error });\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Parent index maintenance\n // -------------------------------------------------------------------------\n\n private _addToParentIndex(parentNodeKey: string | undefined, childKey: string): void {\n addToSetMap(this._parentIndex, parentNodeKey, childKey);\n }\n\n private _removeFromParentIndex(parentNodeKey: string | undefined, childKey: string): void {\n deleteFromSetMap(this._parentIndex, parentNodeKey, childKey);\n }\n\n /**\n * Resolve a node's structural parent to the parent node's key\n * ({@link nodeKey}), or undefined for a root. The parent is named by a\n * codec-message-id (`parentCodecMessageId`); this maps it through the\n * codec-message-id index to the owning node's key (a runId for a reply run,\n * a codec-message-id for an input node). Returns undefined when the parent\n * hasn't been observed yet (the node is treated as a root until it arrives).\n * @param node - The node whose parent to resolve.\n * @returns The parent node's key, or undefined.\n */\n private _parentKeyOf(node: ConversationNode<TProjection>): string | undefined {\n const parentCodecMessageId = node.parentCodecMessageId;\n return parentCodecMessageId === undefined ? undefined : this._codecMessageIdToNodeKey.get(parentCodecMessageId);\n }\n\n // -------------------------------------------------------------------------\n // Sibling grouping\n // -------------------------------------------------------------------------\n\n /**\n * Walk an input node's `forkOf` chain to the group root — the earliest edit\n * version sharing the same structural parent. Stops at a missing target, a\n * non-input target, a parent mismatch, or a cycle.\n * @param node - The input node to walk from.\n * @returns The group-root input node (the node itself when it is the root).\n */\n private _inputGroupRoot(node: InputNode<TProjection>): InputNode<TProjection> {\n let current = node;\n const visited = new Set<string>([nodeKey(current)]);\n while (current.forkOf !== undefined) {\n if (visited.has(current.forkOf)) break;\n const forkTarget = this._nodeIndex.get(current.forkOf);\n if (forkTarget?.node.kind !== 'input' || forkTarget.node.parentCodecMessageId !== current.parentCodecMessageId) {\n break;\n }\n current = forkTarget.node;\n visited.add(nodeKey(current));\n }\n return current;\n }\n\n /**\n * Get the sibling group that the node keyed by `key` belongs to. Kind-split:\n *\n * - **Reply runs** — every reply run sharing the same input-node parent is a\n * sibling (the original reply + its regenerators all parent at the same\n * input node M_user). No fork-of involved.\n * - **Input nodes** — edit versions: nodes sharing a parent AND linked by a\n * `forkOf` chain to the group root.\n *\n * Returned ordered by startSerial (original/oldest first). A group of one is\n * returned as a single-element array (no branching).\n * @param key - The node key ({@link nodeKey}) to look up the group for.\n * @returns The ordered list of sibling nodes.\n */\n // Spec: AIT-CT13b\n private _getSiblingGroup(key: string): InternalNode<TProjection>[] {\n if (this._siblingCacheVersion !== this._structuralVersion) {\n this._siblingCache.clear();\n this._siblingCacheVersion = this._structuralVersion;\n }\n const cached = this._siblingCache.get(key);\n if (cached) return cached;\n\n const entry = this._nodeIndex.get(key);\n if (!entry) return [];\n\n // The \"original\" anchors the group's parent + kind. For an input node,\n // walk the forkOf chain to the earliest version sharing the parent; for a\n // reply run the node itself anchors (all same-parent runs are siblings).\n let original = entry.node;\n if (original.kind === 'input') {\n original = this._inputGroupRoot(original);\n }\n\n // `_parentIndex` is keyed by the raw structural `parentCodecMessageId` (not\n // the resolved parent node key) so a run observed before its input node\n // still files/groups correctly — the parent codec-message-id is known at\n // creation, the resolved key may not be.\n const parentKey = original.parentCodecMessageId;\n const siblings: InternalNode<TProjection>[] = [];\n const candidateKeys = this._parentIndex.get(parentKey);\n if (candidateKeys) {\n for (const childKey of candidateKeys) {\n const childEntry = this._nodeIndex.get(childKey);\n if (childEntry && this._isSiblingOf(childEntry.node, original)) {\n siblings.push(childEntry);\n }\n }\n }\n\n siblings.sort((a, b) => this._compareNodes(a, b));\n // Cache against the queried key AND every member of the group: a single\n // group is the same array regardless of which member triggered the lookup,\n // so subsequent queries against any member hit without recomputing.\n for (const sib of siblings) {\n this._siblingCache.set(nodeKey(sib.node), siblings);\n }\n this._siblingCache.set(key, siblings);\n return siblings;\n }\n\n /**\n * Whether `node` belongs to the sibling group anchored at `original`.\n * Requires the same kind and the same structural parent; reply runs need\n * nothing more (same-parent runs are regenerate siblings), input nodes must\n * additionally be forkOf-linked to the original (edit versions).\n * @param node - The candidate node.\n * @param original - The group's anchor node.\n * @returns True if `node` is a sibling of `original`.\n */\n private _isSiblingOf(node: ConversationNode<TProjection>, original: ConversationNode<TProjection>): boolean {\n if (node.kind !== original.kind) return false;\n if (node.parentCodecMessageId !== original.parentCodecMessageId) return false;\n // Same-parent reply runs are regenerate siblings — no fork-of needed.\n if (node.kind === 'run') return true;\n // Input nodes: must be forkOf-linked to the original (edit versions).\n const originalKey = nodeKey(original);\n if (nodeKey(node) === originalKey) return true;\n let current: ConversationNode<TProjection> = node;\n const visited = new Set<string>([nodeKey(current)]);\n while (current.kind === 'input' && current.forkOf !== undefined) {\n if (current.forkOf === originalKey) return true;\n if (visited.has(current.forkOf)) break;\n const target = this._nodeIndex.get(current.forkOf);\n if (!target) break;\n current = target.node;\n visited.add(nodeKey(current));\n }\n return false;\n }\n\n /**\n * Get the \"group root\" key for a sibling group — the stable key the\n * selection map is keyed by. For an input node (edit versions) that is the\n * earliest fork-of ancestor; for a reply run (regenerate group) it is the\n * oldest same-parent run (the original reply).\n * @param key - Any node key in the sibling group.\n * @returns The group root's key.\n */\n getGroupRoot(key: string): string {\n const entry = this._nodeIndex.get(key);\n if (!entry) return key;\n\n if (entry.node.kind === 'input') {\n return nodeKey(this._inputGroupRoot(entry.node));\n }\n\n // Reply run: the oldest same-parent run is the original reply.\n const group = this._getSiblingGroup(key);\n const root = group[0]?.node;\n return root ? nodeKey(root) : key;\n }\n\n // -------------------------------------------------------------------------\n // Public query methods\n // -------------------------------------------------------------------------\n\n /**\n * Walk the visible node chain along the selected branches, kind-blind. An\n * input node and a reply run reach each other through the same\n * parent-membership check, so seed-only user→user chains and the\n * input→reply→input weave both resolve here. Sibling groups (edit versions /\n * regenerate runs) collapse to the selected member.\n * @param selections - Per-group selected member key, keyed by group root.\n * @returns The visible nodes (both kinds) in chronological order.\n */\n visibleNodes(selections: Map<string, string> = new Map<string, string>()): ConversationNode<TProjection>[] {\n this._logger.trace('DefaultTree.visibleNodes();');\n const result: ConversationNode<TProjection>[] = [];\n const currentPath = new Set<string>();\n const resolvedGroups = new Map<string, string>(); // groupRootKey -> selected key\n\n for (const internal of this._sortedNodes) {\n const node = internal.node;\n const key = nodeKey(node);\n\n // Step 1: Parent reachability (kind-blind — the parent may be an input\n // node or a reply run; resolve its key and check the active path).\n const parentKey = this._parentKeyOf(node);\n if (parentKey !== undefined && !currentPath.has(parentKey)) {\n continue;\n }\n\n // Step 2: Sibling selection.\n const group = this._getSiblingGroup(key);\n if (group.length > 1) {\n const groupRootKey = this.getGroupRoot(key);\n let selectedKey = resolvedGroups.get(groupRootKey);\n if (selectedKey === undefined) {\n const preferredKey = selections.get(groupRootKey);\n if (preferredKey !== undefined && group.some((n) => nodeKey(n.node) === preferredKey)) {\n selectedKey = preferredKey;\n } else {\n const latest = group.at(-1);\n if (!latest) break; // unreachable: group.length > 1\n selectedKey = nodeKey(latest.node);\n }\n resolvedGroups.set(groupRootKey, selectedKey);\n }\n if (key !== selectedKey) {\n continue;\n }\n }\n\n currentPath.add(key);\n result.push(node);\n }\n\n return result;\n }\n\n getRunNode(runId: string): RunNode<TProjection> | undefined {\n this._logger.trace('DefaultTree.getRunNode();', { runId });\n const node = this._nodeIndex.get(runId)?.node;\n return node?.kind === 'run' ? node : undefined;\n }\n\n getNode(key: string): ConversationNode<TProjection> | undefined {\n this._logger.trace('DefaultTree.getNode();', { key });\n return this._nodeIndex.get(key)?.node;\n }\n\n getNodeByCodecMessageId(codecMessageId: string): ConversationNode<TProjection> | undefined {\n this._logger.trace('DefaultTree.getNodeByCodecMessageId();', { codecMessageId });\n const key = this._codecMessageIdToNodeKey.get(codecMessageId);\n return key === undefined ? undefined : this._nodeIndex.get(key)?.node;\n }\n\n getReplyRuns(inputCodecMessageId: string): RunNode<TProjection>[] {\n const runIds = this._replyRunsByInput.get(inputCodecMessageId);\n if (!runIds) return [];\n const result: RunNode<TProjection>[] = [];\n for (const runId of runIds) {\n const node = this._nodeIndex.get(runId)?.node;\n if (node?.kind === 'run') result.push(node);\n }\n return result;\n }\n\n getSiblingNodes(key: string): ConversationNode<TProjection>[] {\n this._logger.trace('DefaultTree.getSiblingNodes();', { key });\n return this._getSiblingGroup(key).map((n) => n.node);\n }\n\n // -------------------------------------------------------------------------\n // Mutation\n // -------------------------------------------------------------------------\n\n applyMessage(\n events: { inputs: TInput[]; outputs: TOutput[] },\n headers: Record<string, string>,\n serial?: string,\n ): void {\n const wireRunId = headers[HEADER_RUN_ID];\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n\n // Classify: with NO run-id, a user message carrying a codec-message-id and\n // at least one input event forms an INPUT node keyed by that\n // codec-message-id — the client owns it; the agent mints the reply run-id\n // separately. Everything else needs a run-id to route to a reply run.\n // Capturing the id (not a boolean) narrows it to `string` for the input path.\n const inputNodeCodecMessageId =\n wireRunId === undefined &&\n codecMessageId !== undefined &&\n headers[HEADER_ROLE] === 'user' &&\n events.inputs.length > 0\n ? codecMessageId\n : undefined;\n\n if (wireRunId === undefined && inputNodeCodecMessageId === undefined) {\n this._logger.warn('Tree.applyMessage(); message has no run-id and is not a user input; skipping');\n return;\n }\n\n // Fold inputs first, then outputs, preserving wire order.\n const all: (TInput | TOutput)[] = [...events.inputs, ...events.outputs];\n\n // Wire-only metadata-carrier messages (e.g. `ait-regenerate`) decode to\n // zero events and don't need a node at the tree level — the eventual reply\n // run is created later by run-start, and any regenerate / parent\n // information the wire carried is reread from the run-start headers.\n // Skipping here avoids a phantom node that would inflate sibling counts.\n const existingKey = inputNodeCodecMessageId ?? wireRunId;\n if (all.length === 0 && existingKey !== undefined && !this._nodeIndex.has(existingKey)) {\n return;\n }\n\n // `update` is the structural channel: emit it only when this apply\n // actually changes the tree shape (new node, startSerial promotion).\n // Content-only folds (streaming chunks into an existing node) flow through\n // `output` instead, so they leave `_structuralVersion` untouched.\n const structuralBefore = this._structuralVersion;\n\n if (inputNodeCodecMessageId !== undefined) {\n this._applyInputMessage(inputNodeCodecMessageId, headers, serial, all);\n } else if (wireRunId !== undefined) {\n this._applyRunMessage(wireRunId, events, headers, serial);\n }\n\n if (this._structuralVersion !== structuralBefore) this._emitter.emit('update');\n }\n\n /**\n * Apply a run-less user input wire: create (or promote the serial of) the\n * input node keyed by its codec-message-id, fold the input events into its\n * own projection, and emit an `output` event (with empty outputs — input\n * folds carry none) so the View observes the optimistic insert.\n * @param codecMessageId - The input node's codec-message-id (its primary key).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for an optimistic insert.\n * @param all - The decoded input events to fold, in wire order.\n */\n private _applyInputMessage(\n codecMessageId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n all: (TInput | TOutput)[],\n ): void {\n let entry = this._nodeIndex.get(codecMessageId);\n if (!entry) {\n entry = this._createInputNodeFromHeaders(codecMessageId, headers, serial);\n this._insertNode(codecMessageId, entry, entry.node.parentCodecMessageId);\n this._codecMessageIdToNodeKey.set(codecMessageId, codecMessageId);\n this._logger.debug('Tree.applyMessage(); created input node', { codecMessageId });\n } else if (entry.node.kind === 'input' && serial && !entry.node.serial) {\n // Promote optimistic serial when the relay/echo arrives.\n this._logger.debug('Tree.applyMessage(); promoting input serial', { codecMessageId, serial });\n entry.node.serial = serial;\n this._promoteSerial(entry);\n }\n\n this._foldInto(entry, all, serial, codecMessageId);\n\n // An input node owns no agent outputs; the event still fires (empty\n // outputs) so consumers observe the projection change. It has no run-id —\n // the causal routing key is the input's own codec-message-id.\n this._emitter.emit('output', {\n runId: undefined,\n inputCodecMessageId: codecMessageId,\n codecMessageId,\n serial,\n events: [],\n });\n }\n\n /**\n * Apply a reply-run wire (assistant output, continuation tool-resolution, or\n * a fresh run keyed by the agent-minted run-id): create or reconcile the run\n * node, fold its events, maintain the codec-message-id and reply→input\n * indices, and emit the `output` event. Derives the codec-message-id,\n * triggering-input id, fold list, and outputs from `events`/`headers`,\n * mirroring `applyMessage`.\n * @param wireRunId - The run-id from the inbound wire (the node's primary key).\n * @param events - The decoded inputs and outputs from the wire.\n * @param events.inputs - Client-published events (`ai-input` wire).\n * @param events.outputs - Agent-published events (`ai-output` wire).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for an optimistic insert.\n */\n private _applyRunMessage(\n wireRunId: string,\n events: { inputs: TInput[]; outputs: TOutput[] },\n headers: Record<string, string>,\n serial: string | undefined,\n ): void {\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n // The triggering input's codec-message-id (the agent's echo), surfaced on\n // the `output` event as the stream's causal routing key.\n const inputCodecMessageId = headers[HEADER_INPUT_CODEC_MESSAGE_ID];\n // Fold inputs first, then outputs, preserving wire order.\n const all: (TInput | TOutput)[] = [...events.inputs, ...events.outputs];\n const outputs = events.outputs;\n\n let run = this._nodeIndex.get(wireRunId);\n\n // Reconcile an optimistic insert with its serial-bearing echo by\n // codec-message-id rather than the wire run-id — covers assistant content\n // that pins a codec-message-id before its run-id is indexed.\n if (!run && codecMessageId !== undefined) {\n const indexedKey = this._codecMessageIdToNodeKey.get(codecMessageId);\n const indexed = indexedKey === undefined ? undefined : this._nodeIndex.get(indexedKey);\n if (indexed?.node.kind === 'run' && indexed.node.startSerial === undefined) run = indexed;\n }\n\n if (!run) {\n run = this._createRunFromHeaders(wireRunId, headers, serial);\n this._insertNode(wireRunId, run, run.node.parentCodecMessageId);\n this._indexReplyRun(run.node, wireRunId);\n this._logger.debug('Tree.applyMessage(); created new Run', { runId: wireRunId });\n } else if (serial && run.node.kind === 'run' && !run.node.startSerial) {\n // Promote optimistic startSerial when the relay/echo arrives.\n this._logger.debug('Tree.applyMessage(); promoting startSerial', { runId: wireRunId, serial });\n run.node.startSerial = serial;\n this._promoteSerial(run);\n }\n\n // Index the codec-message-id against the node that actually owns it.\n const ownerKey = nodeKey(run.node);\n if (codecMessageId) this._codecMessageIdToNodeKey.set(codecMessageId, ownerKey);\n\n this._foldInto(run, all, serial, codecMessageId);\n\n this._emitter.emit('output', { runId: ownerKey, inputCodecMessageId, codecMessageId, serial, events: outputs });\n }\n\n /**\n * Record a reply run against its input-node parent (the reverse edge powering\n * `getReplyRuns` and regenerate sibling grouping). A reply run's\n * `parentCodecMessageId` is its input node's codec-message-id (the master\n * invariant), so no resolution is needed.\n * @param node - The reply run node.\n * @param runId - The run's id.\n */\n private _indexReplyRun(node: ConversationNode<TProjection>, runId: string): void {\n if (node.parentCodecMessageId === undefined) return;\n addToSetMap(this._replyRunsByInput, node.parentCodecMessageId, runId);\n }\n\n applyRunLifecycle(event: RunLifecycleEvent): void {\n this._logger.trace('DefaultTree.applyRunLifecycle();', { type: event.type, runId: event.runId });\n // Structural channel: emit `update` only when the lifecycle event changes\n // the tree shape. Only run-start can do that (a new Run, startSerial\n // promotion, or structural-metadata backfill); suspend/resume/end mutate\n // status/endSerial on an existing node — content, not structure — so the\n // conditional naturally never fires for them.\n const structuralBefore = this._structuralVersion;\n switch (event.type) {\n case 'start': {\n this._applyRunStart(event);\n break;\n }\n case 'suspend': {\n this._applyRunSuspend(event);\n break;\n }\n case 'resume': {\n this._applyRunResume(event);\n break;\n }\n case 'end': {\n this._applyRunEnd(event);\n break;\n }\n }\n this._emitter.emit('run', event);\n if (this._structuralVersion !== structuralBefore) this._emitter.emit('update');\n }\n\n /**\n * Apply a run-start lifecycle event's structural effect: create the reply\n * run if it doesn't exist yet, or backfill an optimistic / wire-created\n * node's structure and metadata from the canonical run-start. Mutates\n * `_structuralVersion` when the tree shape changes; the caller owns the\n * `run`/`update` emits.\n * @param event - The run-start lifecycle event.\n */\n private _applyRunStart(event: RunLifecycleEvent & { type: 'start' }): void {\n const existing = this._nodeIndex.get(event.runId);\n if (existing?.node.kind === 'run') {\n const node = existing.node;\n if (node.status !== 'active') {\n node.status = 'active';\n }\n if (event.serial && !node.startSerial) {\n node.startSerial = event.serial;\n this._promoteSerial(existing);\n }\n // Backfill structural metadata if the Run was created from an\n // assistant wire that arrived before run-start (history pagination\n // boundary or out-of-order delivery). The run-start lifecycle event is\n // the canonical source for parent/forkOf/regenerates; only fill in\n // fields the wire didn't already populate. A run-start is always a\n // first start (continuations re-enter via `ai-run-resume`, which\n // carries no structural metadata), so it is unconditionally\n // authoritative here. `parent` is the run's STRUCTURAL parent (its\n // input node) — reachability and the reply→input edge read it.\n if (node.parentCodecMessageId === undefined && event.parent !== undefined) {\n node.parentCodecMessageId = event.parent;\n this._removeFromParentIndex(undefined, event.runId);\n this._addToParentIndex(node.parentCodecMessageId, event.runId);\n this._indexReplyRun(node, event.runId);\n this._structuralVersion++;\n }\n if (node.forkOf === undefined && event.forkOf !== undefined) {\n const forkOfKey = this._codecMessageIdToNodeKey.get(event.forkOf);\n if (forkOfKey !== undefined && forkOfKey !== event.runId) {\n node.forkOf = forkOfKey;\n this._structuralVersion++;\n }\n }\n if (node.regeneratesCodecMessageId === undefined && event.regenerates !== undefined) {\n node.regeneratesCodecMessageId = event.regenerates;\n this._structuralVersion++;\n }\n // Adopt the agent-minted invocation-id onto the optimistic node. The\n // agent mints it, so a node created from an optimistic insert (or an\n // assistant wire that arrived before run-start) carries an empty id\n // until the agent's run-start delivers it. Metadata, not structure —\n // consumers re-read it on the `run` emit, so no structural-version\n // bump.\n if (node.invocationId === '' && event.invocationId !== '') {\n node.invocationId = event.invocationId;\n }\n } else if (!existing) {\n const run = this._createRunFromLifecycle(event);\n this._insertNode(event.runId, run, run.node.parentCodecMessageId);\n this._indexReplyRun(run.node, event.runId);\n }\n }\n\n /**\n * Apply a run-suspend lifecycle event: pause the run without ending it —\n * mark the node 'suspended' and record the serial it paused at, but keep the\n * Run live so a resume under the same runId resumes it. Status/endSerial are\n * content, not structure, so this never mutates `_structuralVersion`; the\n * caller owns the emits.\n * @param event - The run-suspend lifecycle event.\n */\n private _applyRunSuspend(event: RunLifecycleEvent & { type: 'suspend' }): void {\n const run = this._nodeIndex.get(event.runId);\n if (run?.node.kind === 'run') {\n run.node.status = 'suspended';\n run.node.endSerial = event.serial;\n }\n }\n\n /**\n * Apply a run-resume lifecycle event: re-enter an already-started run by\n * flipping a suspended run back to 'active'. Pure re-entry — it carries no\n * parent/forkOf and does not promote startSerial (the original run-start owns\n * the run's structure). Only a suspended run resumes: a no-op when the run\n * isn't known (e.g. a resume replayed from a newer history page before its\n * run-start) and a no-op for an already-active or terminal\n * (complete/cancelled/error) run — a stray resume must never resurrect a run\n * that has ended. The caller owns the emits.\n * @param event - The run-resume lifecycle event.\n */\n private _applyRunResume(event: RunLifecycleEvent & { type: 'resume' }): void {\n const run = this._nodeIndex.get(event.runId);\n if (run?.node.kind === 'run' && run.node.status === 'suspended') {\n run.node.status = 'active';\n }\n }\n\n /**\n * Apply a run-end lifecycle event: record the terminal reason as the node's\n * status and the serial it ended at. Status/endSerial are content, not\n * structure, so this never mutates `_structuralVersion`; the caller owns the\n * emits.\n * @param event - The run-end lifecycle event.\n */\n private _applyRunEnd(event: RunLifecycleEvent & { type: 'end' }): void {\n const run = this._nodeIndex.get(event.runId);\n if (run?.node.kind === 'run') {\n run.node.status = event.reason;\n run.node.endSerial = event.serial;\n }\n }\n\n delete(key: string): void {\n const entry = this._nodeIndex.get(key);\n if (!entry) return;\n\n this._logger.debug('Tree.delete();', { key });\n\n this._removeFromParentIndex(entry.node.parentCodecMessageId, key);\n this._removeSortedNode(entry);\n this._nodeIndex.delete(key);\n // Drop the reply→input reverse edge.\n if (entry.node.kind === 'run' && entry.node.parentCodecMessageId !== undefined) {\n deleteFromSetMap(this._replyRunsByInput, entry.node.parentCodecMessageId, key);\n }\n // _codecMessageIdToNodeKey entries pointing at this node linger but are\n // harmless; they'll be overwritten if the node is re-created and remain\n // dangling otherwise. Cleanup not worth the index walk.\n\n this._structuralVersion++;\n this._emitter.emit('update');\n }\n\n // -------------------------------------------------------------------------\n // Internal helpers\n // -------------------------------------------------------------------------\n\n /**\n * Build a fresh RunNode from a wire message's headers. Used when an\n * inbound message arrives before any run-start event for its runId.\n * @param runId - The run-id from the inbound wire.\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for optimistic inserts.\n * @returns A newly-allocated internal run node ready for insertion.\n */\n private _createRunFromHeaders(\n runId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n ): InternalNode<TProjection> {\n const forkOfMsgId = headers[HEADER_FORK_OF];\n return this._buildRunNode({\n runId,\n parentCodecMessageId: headers[HEADER_PARENT],\n // forkOf is resolved to the fork target's node key (an input node's\n // codec-message-id, or a run's id) — the same space `_isSiblingOf` walks.\n forkOf: forkOfMsgId ? this._codecMessageIdToNodeKey.get(forkOfMsgId) : undefined,\n regeneratesCodecMessageId: headers[HEADER_MSG_REGENERATE],\n clientId: headers[HEADER_RUN_CLIENT_ID] ?? '',\n invocationId: headers[HEADER_INVOCATION_ID] ?? '',\n startSerial: serial,\n });\n }\n\n /**\n * Allocate a RunNode from already-resolved fields. Shared by the\n * header-driven and lifecycle-driven run creators: both build the identical\n * RunNode literal and stamp an insert sequence.\n * @param params - The resolved run fields.\n * @param params.runId - The run's id (its primary key).\n * @param params.parentCodecMessageId - Structural parent codec-message-id, or undefined for a root.\n * @param params.forkOf - The resolved fork target's node key (already mapped through the codec-message-id index), or undefined.\n * @param params.regeneratesCodecMessageId - The codec-message-id this run regenerates, or undefined.\n * @param params.clientId - The publishing client's id.\n * @param params.invocationId - The agent invocation id.\n * @param params.startSerial - Ably channel serial; undefined for optimistic inserts.\n * @returns A newly-allocated internal run node ready for insertion.\n */\n private _buildRunNode(params: {\n runId: string;\n parentCodecMessageId: string | undefined;\n forkOf: string | undefined;\n regeneratesCodecMessageId: string | undefined;\n clientId: string;\n invocationId: string;\n startSerial: string | undefined;\n }): InternalNode<TProjection> {\n const node: RunNode<TProjection> = {\n kind: 'run',\n runId: params.runId,\n parentCodecMessageId: params.parentCodecMessageId,\n forkOf: params.forkOf,\n regeneratesCodecMessageId: params.regeneratesCodecMessageId,\n clientId: params.clientId,\n invocationId: params.invocationId,\n status: 'active',\n projection: this._codec.init(),\n startSerial: params.startSerial,\n endSerial: undefined,\n };\n\n return { node, insertSeq: this._seqCounter++ };\n }\n\n /**\n * Build a fresh InputNode from a run-less user input wire's headers.\n * @param codecMessageId - The input's codec-message-id (its primary key).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for optimistic inserts.\n * @returns A newly-allocated internal input node ready for insertion.\n */\n private _createInputNodeFromHeaders(\n codecMessageId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n ): InternalNode<TProjection> {\n const forkOfMsgId = headers[HEADER_FORK_OF];\n const node: InputNode<TProjection> = {\n kind: 'input',\n codecMessageId,\n parentCodecMessageId: headers[HEADER_PARENT],\n // An edit's fork-of names the original prompt's codec-message-id, which\n // IS that input node's key — no resolution needed.\n forkOf: forkOfMsgId,\n projection: this._codec.init(),\n serial,\n };\n return { node, insertSeq: this._seqCounter++ };\n }\n\n /**\n * Build a fresh RunNode from a run-start lifecycle event. Used when a\n * run-start event arrives before any message for its runId.\n * @param event - The run-start lifecycle event from the agent, including\n * its channel serial.\n * @returns A newly-allocated internal run node ready for insertion.\n */\n private _createRunFromLifecycle(event: RunLifecycleEvent & { type: 'start' }): InternalNode<TProjection> {\n const forkOfMsgId = event.forkOf;\n return this._buildRunNode({\n runId: event.runId,\n parentCodecMessageId: event.parent,\n forkOf: forkOfMsgId ? this._codecMessageIdToNodeKey.get(forkOfMsgId) : undefined,\n regeneratesCodecMessageId: event.regenerates,\n clientId: event.clientId,\n invocationId: event.invocationId,\n startSerial: event.serial,\n });\n }\n\n // -------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT8b, AIT-CT8e\n on(event: 'update', handler: () => void): () => void;\n on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;\n on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;\n on(event: 'output', handler: (event: OutputEvent<TOutput>) => void): () => void;\n on(\n event: 'update' | 'ably-message' | 'run' | 'output',\n handler:\n | (() => void)\n | ((msg: Ably.InboundMessage) => void)\n | ((event: RunLifecycleEvent) => void)\n | ((event: OutputEvent<TOutput>) => void),\n ): () => void {\n // CAST: overload signatures enforce correct handler types per event name.\n const cb = handler as (arg: TreeEventsMap<TOutput>[keyof TreeEventsMap<TOutput>]) => void;\n this._emitter.on(event, cb);\n return () => {\n this._emitter.off(event, cb);\n };\n }\n\n /**\n * Forward a raw Ably message event to tree subscribers.\n * @param msg - The raw Ably message to emit.\n */\n emitAblyMessage(msg: Ably.InboundMessage): void {\n this._logger.trace('DefaultTree.emitAblyMessage();');\n this._emitter.emit('ably-message', msg);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Tree that materializes branching conversation history from a flat\n * oplog of Ably messages as a two-node-per-turn forest (input node + reply run).\n * @param codec - Codec used to fold inbound events into per-Run projections.\n * @param logger - Logger for diagnostic output.\n * @returns A new {@link DefaultTree} instance. The session uses DefaultTree\n * directly for internal methods (applyMessage, applyRunLifecycle,\n * emitAblyMessage). Public consumers see the narrower {@link Tree} interface.\n */\nexport const createTree = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(\n codec: Reducer<TInput | TOutput, TProjection>,\n logger: Logger,\n): DefaultTree<TInput, TOutput, TProjection> => new DefaultTree<TInput, TOutput, TProjection>(codec, logger);\n","/**\n * loadHistory — load conversation history from an Ably channel and return\n * the raw wire messages as a paginated HistoryPage result.\n *\n * This does NOT decode: it pages back through Ably history until `limit`\n * complete messages are present, then hands the raw Ably messages\n * (oldest-first) to the caller. The View re-decodes them into the Tree\n * itself, so load-history only needs a cheap, header-based completion\n * counter to decide when to stop paging — the decoder never runs here.\n *\n * The `limit` option controls the number of complete **messages** per page,\n * not the number of Ably wire messages fetched. A message is complete when\n * its terminal wire signal — `status: \"complete\"` / `\"cancelled\"`, or a\n * `discrete` create — has been seen. Runs that span a\n * page boundary are handled by the counter requiring both a start and a\n * terminal signal before counting a message complete.\n *\n * Because Ably history returns newest-first, each page's `rawMessages` are\n * reversed to chronological (oldest-first) so the caller can fold them in\n * order.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_CODEC_MESSAGE_ID, HEADER_DISCRETE, HEADER_STATUS, HEADER_STREAM } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport type { HistoryPage, LoadHistoryOptions } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Shared state across pages within one history traversal\n// ---------------------------------------------------------------------------\n\ninterface HistoryState {\n /** All raw Ably messages collected so far, in newest-first order (as received from Ably). */\n rawMessages: Ably.InboundMessage[];\n /**\n * How many complete messages have been served to the consumer so far.\n * Drives the buffered-page logic: when a single fetch gathers more than\n * `limit` completions, later pages are served from the buffer without\n * fetching, advancing this counter `limit` at a time.\n */\n returnedCount: number;\n /** How many raw Ably messages have been served to the consumer so far. */\n returnedRawCount: number;\n /** The last Ably page cursor for continued pagination. */\n lastAblyPage: Ably.PaginatedResult<Ably.InboundMessage> | undefined;\n /**\n * `codec-message-id`s for which a start signal has been seen: any\n * `message.create` / `message.update` / `message.append` with\n * `stream: \"true\"` (the decoder establishes a tracker via create or\n * first-contact), or a `message.create` carrying `discrete` (a discrete\n * message, created and terminated in one wire message).\n */\n startedCodecMessageIds: Set<string>;\n /**\n * `codec-message-id`s with a terminal wire signal: either `discrete`\n * on a `message.create` (discrete message) or `status: \"complete\"`\n * / `\"cancelled\"` on any action (closed stream).\n */\n terminatedCodecMessageIds: Set<string>;\n /**\n * `codec-message-id`s that are both started AND terminated — counted as\n * complete. The fetch loop reads this set's size to decide when to stop\n * paging. Maintained incrementally by {@link countNewCompletions}. Grows\n * monotonically.\n */\n completedCodecMessageIds: Set<string>;\n logger: Logger;\n}\n\n// ---------------------------------------------------------------------------\n// Incremental completion counting (header scan, no decode)\n// ---------------------------------------------------------------------------\n\n/**\n * Scan newly-added raw messages and track which `codec-message-id`s have\n * become complete. Used by {@link fetchUntilLimit} to decide when enough\n * completed messages have been collected, without running the decoder.\n *\n * A codec-message-id is considered complete only when BOTH of these have been seen:\n * - a \"start\" signal: either `discrete` on a `message.create`\n * (discrete messages are created and terminated by the same wire\n * message), OR any `message.create` / `message.update` / `message.append`\n * with `stream: \"true\"` (the decoder establishes a tracker via\n * create or first-contact).\n * - a \"terminal\" signal: `discrete` on the create, or\n * `status: \"complete\"` / `\"cancelled\"` on any later action.\n *\n * Why update and append count as starts: Ably history can compact a live\n * `create + append + ... + append{status:complete}` sequence into a single\n * `message.update` with `STREAM=true` and `STATUS=complete`. The decoder\n * handles that via first-contact. Counting only `message.create` as a start\n * would cause the fetch loop to page past a compacted run without ever\n * marking it complete.\n *\n * Requiring both halves matters when a streaming run spans a page\n * boundary: the terminal arrives in the newer page (fetched first) while\n * the start sits in an older page. Counting the terminal alone would stop\n * the fetch loop prematurely - the decoder would have no stream state to\n * resolve, and the message wouldn't make it into the result.\n *\n * Messages skipped for counting:\n * - Missing `codec-message-id`: lifecycle events not tied to a domain message.\n * - `message.delete`: clears the tracker, doesn't produce output.\n *\n * Amend-class wire messages (events targeting an existing message via\n * `HEADER_CODEC_MESSAGE_ID`) flow through the same counter — the Sets naturally\n * dedup so a tool-output amend on an already-seen codec-message-id is idempotent.\n *\n * Known edge case: if Ably history is truncated and a terminal survives\n * while every start signal for its codec-message-id has rolled off, the counter will\n * never mark that `codec-message-id` complete. The loop keeps fetching until it runs\n * out of pages, then returns whatever raw messages it collected.\n * @param state - The shared history traversal state.\n * @param newMessages - The Ably messages just pushed onto `state.rawMessages`.\n */\nconst countNewCompletions = (state: HistoryState, newMessages: readonly Ably.InboundMessage[]): void => {\n for (const msg of newMessages) {\n const headers = getTransportHeaders(msg);\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n if (!codecMessageId) continue;\n\n const action = msg.action;\n const isDiscreteCreate = action === 'message.create' && HEADER_DISCRETE in headers;\n // Any content-producing action on a streamed serial counts as a start:\n // the decoder uses create or first-contact (update/append) to establish\n // its tracker. Delete clears tracker state and emits nothing, so it\n // never counts as a start.\n const hasStreamContent =\n headers[HEADER_STREAM] === 'true' &&\n (action === 'message.create' || action === 'message.update' || action === 'message.append');\n const status = headers[HEADER_STATUS];\n const isTerminal = status === 'complete' || status === 'cancelled';\n\n if (isDiscreteCreate || hasStreamContent) state.startedCodecMessageIds.add(codecMessageId);\n if (isDiscreteCreate || isTerminal) state.terminatedCodecMessageIds.add(codecMessageId);\n if (state.startedCodecMessageIds.has(codecMessageId) && state.terminatedCodecMessageIds.has(codecMessageId)) {\n state.completedCodecMessageIds.add(codecMessageId);\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Fetch Ably pages until we have enough completed messages\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch Ably history pages until we have enough completed messages.\n *\n * The loop uses {@link countNewCompletions} - a cheap O(new messages) header\n * scan - to decide when to stop, rather than running the decoder per page.\n * @param state - The shared history traversal state.\n * @param ablyPage - The current Ably paginated result to start from.\n * @param limit - Target number of completed messages beyond what has already been returned.\n */\nconst fetchUntilLimit = async (\n state: HistoryState,\n ablyPage: Ably.PaginatedResult<Ably.InboundMessage>,\n limit: number,\n): Promise<void> => {\n state.rawMessages.push(...ablyPage.items);\n state.lastAblyPage = ablyPage;\n countNewCompletions(state, ablyPage.items);\n\n const target = state.returnedCount + limit;\n while (state.completedCodecMessageIds.size < target && ablyPage.hasNext()) {\n state.logger.debug('loadHistory.fetchUntilLimit(); fetching next page', {\n collected: state.rawMessages.length,\n completed: state.completedCodecMessageIds.size,\n });\n const nextPage = await ablyPage.next();\n if (!nextPage) break;\n ablyPage = nextPage;\n state.rawMessages.push(...nextPage.items);\n state.lastAblyPage = nextPage;\n countNewCompletions(state, nextPage.items);\n }\n};\n\n// ---------------------------------------------------------------------------\n// Build HistoryPage result from current state\n// ---------------------------------------------------------------------------\n\n/**\n * Build a HistoryPage of raw wire messages from the current fetch state.\n * @param state - The shared history traversal state.\n * @param limit - Max complete messages per page.\n * @returns A page of raw history messages with a `next()` cursor.\n */\nconst buildResult = (state: HistoryState, limit: number): HistoryPage => {\n // Advance the served-completion counter by up to `limit`, mirroring the\n // page granularity the consumer asked for. `rawMessages` for this page are\n // all wires fetched since the previous page (empty for buffered pages).\n const totalCompleted = state.completedCodecMessageIds.size;\n const served = Math.min(limit, Math.max(0, totalCompleted - state.returnedCount));\n state.returnedCount += served;\n\n const moreCompleted = totalCompleted > state.returnedCount;\n const moreAblyPages = state.lastAblyPage?.hasNext() ?? false;\n\n // Raw Ably messages for this page in chronological order (oldest first).\n const newRawCount = state.rawMessages.length - state.returnedRawCount;\n const rawMessages = newRawCount > 0 ? state.rawMessages.slice(state.returnedRawCount).toReversed() : [];\n state.returnedRawCount = state.rawMessages.length;\n\n return {\n rawMessages,\n hasNext: () => moreCompleted || moreAblyPages,\n next: async () => {\n if (moreCompleted) {\n return buildResult(state, limit);\n }\n if (!moreAblyPages || !state.lastAblyPage) return;\n const nextAbly = await state.lastAblyPage.next();\n if (!nextAbly) return;\n await fetchUntilLimit(state, nextAbly, limit);\n return buildResult(state, limit);\n },\n };\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Load conversation history from a channel and return the raw wire messages.\n *\n * Attaches the channel if not already attached, then calls\n * `channel.history({ untilAttach: true })` to guarantee no gap between\n * historical and live messages. The attach is idempotent.\n *\n * The `limit` option controls the number of complete messages\n * returned per page, not the number of Ably wire messages fetched.\n * @param channel - The Ably channel to load history from.\n * @param options - Pagination options.\n * @param logger - Logger for diagnostic output.\n * @returns The first page of raw history messages.\n */\n// Spec: AIT-CT11, AIT-CT11b\nexport const loadHistory = async (\n channel: Ably.RealtimeChannel,\n options: LoadHistoryOptions | undefined,\n logger: Logger,\n): Promise<HistoryPage> => {\n const limit = options?.limit ?? 100;\n const state: HistoryState = {\n rawMessages: [],\n returnedCount: 0,\n returnedRawCount: 0,\n lastAblyPage: undefined,\n startedCodecMessageIds: new Set<string>(),\n terminatedCodecMessageIds: new Set<string>(),\n completedCodecMessageIds: new Set<string>(),\n logger,\n };\n\n logger.trace('loadHistory();', { limit });\n\n // Request more Ably messages than the domain limit to account for\n // the many-to-one ratio (multiple wire messages per message).\n const wireLimit = limit * 10;\n\n await channel.attach();\n const ablyPage = await channel.history({ untilAttach: true, limit: wireLimit });\n await fetchUntilLimit(state, ablyPage, limit);\n return buildResult(state, limit);\n};\n","/**\n * DefaultView — a paginated, branch-aware projection over the Tree.\n *\n * Wraps a Tree (RunNode-keyed) and manages a pagination window that controls\n * which Runs are visible to the UI. New live Runs appear immediately; older\n * Runs are revealed progressively via `loadOlder()`.\n *\n * `getMessages()` reads the Tree's visible node chain (input nodes + reply\n * runs, with sibling selection applied) and concatenates each node's\n * `codec.getMessages(node.projection)` to produce the flat\n * `CodecMessage<TMessage>[]` the UI renders.\n *\n * Each View owns its own branch selection state and pagination window,\n * allowing multiple independent Views over the same Tree.\n *\n * Events are scoped to the visible window — 'update' only fires when the\n * visible output changes, 'ably-message' only for messages corresponding to\n * visible Runs, and 'run' only for runs with visible content.\n */\n\nimport * as Ably from 'ably';\n\nimport { HEADER_CODEC_MESSAGE_ID, HEADER_RUN_ID } from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport type { Logger } from '../../logger.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport type { Codec, CodecInputEvent, CodecMessage, CodecOutputEvent } from '../codec/types.js';\nimport { applyWireMessage } from './decode-fold.js';\nimport { loadHistory } from './load-history.js';\nimport { nodeKey, type TreeInternal } from './tree.js';\nimport type {\n ActiveRun,\n BranchSelection,\n ConversationNode,\n HistoryPage,\n OutputEvent,\n RunInfo,\n RunLifecycleEvent,\n RunNode,\n SendOptions,\n View,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Events map\n// ---------------------------------------------------------------------------\n\ninterface ViewEventsMap {\n update: undefined;\n 'ably-message': Ably.InboundMessage;\n run: RunLifecycleEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Send delegate\n// ---------------------------------------------------------------------------\n\n/**\n * Internal delegate function provided by the session for executing sends.\n * The View pre-computes the visible branch's flat message list and the\n * codec-message-id of its tail (for auto-parent routing) before calling\n * the delegate, so the delegate has no back-reference to the View.\n *\n * Each TInput carries its own routing metadata (`parent` / `target` /\n * `codecMessageId`) via the {@link CodecInputEvent} base; the delegate\n * reads those fields directly without runtime classification.\n *\n * `parentCodecMessageId` is the codec-message-id of the last message in\n * the visible branch (extracted from the tail Run's projection per codec\n * convention), or `undefined` for an empty conversation. The session\n * uses it as the auto-parent for fresh user messages.\n */\nexport type SendDelegate<TInput extends CodecInputEvent> = (\n input: TInput[],\n options: SendOptions | undefined,\n parentCodecMessageId: string | undefined,\n) => Promise<ActiveRun>;\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for creating a View. */\nexport interface ViewOptions<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage> {\n /** The tree to project. */\n tree: TreeInternal<TInput, TOutput, TProjection>;\n /** The Ably channel to load history from. */\n channel: Ably.RealtimeChannel;\n /** The codec used to project messages, mint regenerate inputs, and decode history. */\n codec: Codec<TInput, TOutput, TProjection, TMessage>;\n /** Delegate for executing sends through the session. */\n sendDelegate: SendDelegate<TInput>;\n /** Logger for diagnostic output. */\n logger: Logger;\n /** Called when the view is closed, allowing the owner to clean up references. */\n onClose?: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Branch selection\n// ---------------------------------------------------------------------------\n\n/**\n * Internal tagged union representing why a branch was selected for an\n * edit-fork group. Stored per group-root runId in the View's\n * `_branchSelections` map. Not the public-facing {@link BranchSelection}\n * — that's a UI-facing bundle returned by `view.branchSelection(id)`.\n */\ntype BranchSelectionState =\n /** Explicit navigation via `selectSibling()`. The selected input-node key. */\n | { kind: 'user'; selectedKey: string }\n /** This view initiated an edit fork — auto-selected the new input node. */\n | { kind: 'auto'; selectedKey: string }\n /** An external fork appeared — pinned to the currently-visible sibling to prevent drift. */\n | { kind: 'pinned'; selectedKey: string };\n\n/**\n * Selection state for a regenerate group. Keyed by the anchor codec-message-id (the\n * assistant codec-message-id being regenerated). Distinct from {@link BranchSelectionState}\n * because regenerate groups are message-level (group members share an\n * anchor codec-message-id), not edit forks of the user prompt.\n *\n * Unlike fork-of groups, regenerate groups do not \"pin to current visible\"\n * when a new member appears externally — the default for a regenerate\n * slot is always the latest member, so an external regenerator auto-rolls\n * forward unless the user has explicitly selected an earlier member.\n */\ntype RegenSelection =\n /** Explicit navigation via `selectSibling()`. The selected reply-run id. */\n | { kind: 'user'; selectedRunId: string }\n /** This view initiated a regenerate — auto-selected the new reply run when it arrived. */\n | { kind: 'auto'; selectedRunId: string }\n /**\n * This view's `regenerate()` is in flight. Keyed (in `_regenSelections`) by\n * the regenerate group's root; `carrierCodecMessageId` is the regenerate\n * carrier event's id, used to recognise the new reply run when it appears.\n */\n | { kind: 'pending'; carrierCodecMessageId: string };\n\n/**\n * A resolved branch point: the group `kind` plus the sibling nodes that make\n * up the alternatives. `fork-of` is an edit-style branch anchored at the user\n * input node; `regen` is a regenerate-style branch anchored at the assistant\n * slot. `groupRoot` is the group's key (input group root for fork-of, the\n * original reply's group root for regen).\n */\ntype MessageBranchPoint<TProjection> =\n | { kind: 'fork-of'; groupRoot: string; siblings: ConversationNode<TProjection>[] }\n | { kind: 'regen'; groupRoot: string; siblings: ConversationNode<TProjection>[] };\n\n// ---------------------------------------------------------------------------\n// Send-input normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * Normalise the two input shapes `View.send` accepts (a single TInput\n * or an array) into the array shape the SendDelegate consumes.\n * @param input - The raw input from `View.send`.\n * @returns The normalised input array.\n */\nconst _normaliseSend = <TInput extends CodecInputEvent>(input: TInput | TInput[]): TInput[] =>\n Array.isArray(input) ? input : [input];\n\n// ---------------------------------------------------------------------------\n// Fetch tuning\n// ---------------------------------------------------------------------------\n\n/**\n * Multiplier applied to the user-supplied Run-unit `loadOlder(limit)`\n * when issuing the first `loadHistory` page request. `loadHistory`\n * counts complete domain *messages* per page, not Runs; a typical Run\n * produces ~2 messages (user + assistant). Asking for `limit * factor`\n * messages on the first page reduces extra round-trips when the actual\n * messages-per-Run ratio is around the factor. `_loadUntilVisible`\n * still loops on the Run count regardless, so this is purely a\n * fetch-efficiency hint.\n */\nconst _RUN_TO_MESSAGE_FETCH_FACTOR = 3;\n\n/**\n * Project a Tree `RunNode` down to the View-facing `RunInfo` shape:\n * drop the codec projection and the structural fields that callers\n * reach via `session.tree` when they need them.\n * @param run - The tree's RunNode.\n * @returns A projection-free RunInfo.\n */\nconst _toRunInfo = <TProjection>(run: RunNode<TProjection>): RunInfo => ({\n runId: run.runId,\n clientId: run.clientId,\n status: run.status,\n invocationId: run.invocationId,\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport class DefaultView<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> implements View<TInput, TMessage> {\n private readonly _tree: TreeInternal<TInput, TOutput, TProjection>;\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: Codec<TInput, TOutput, TProjection, TMessage>;\n private readonly _sendDelegate: SendDelegate<TInput>;\n private readonly _logger: Logger;\n private readonly _emitter: EventEmitter<ViewEventsMap>;\n private readonly _onClose?: () => void;\n\n /**\n * View-local branch selections: group-root runId → selection intent.\n * Fork points not present here default to the latest sibling.\n */\n private readonly _branchSelections = new Map<string, BranchSelectionState>();\n\n /**\n * View-local regenerate-group selections: anchor codec-message-id (the assistant\n * codec-message-id being regenerated) → selection intent. Distinct from\n * {@link _branchSelections} because a regenerate group is a set of\n * same-parent reply runs — message-level alternatives at a single\n * conversation slot, not edit forks of the prompt. Groups not present here default to the latest\n * member (the most recent regenerator, or the original if no regen has\n * landed).\n */\n private readonly _regenSelections = new Map<string, RegenSelection>();\n\n /** Spec: AIT-CT11c — runIds loaded from history but not yet revealed to the UI. */\n private readonly _withheldRunIds = new Set<string>();\n\n /** Snapshot of visible node keys — used to detect structural changes and for selection pinning. */\n private _lastVisibleNodeKeys: string[] = [];\n\n /**\n * Snapshot of visible projection references — used to detect in-place\n * projection updates (streaming). One entry per visible Run.\n */\n private _lastVisibleProjections: TProjection[] = [];\n\n /**\n * Snapshot of the visible flat message chain with codec-message-ids —\n * exposed verbatim via `getMessages()` and the internal correlation\n * source for parent/branch routing.\n */\n private _lastVisibleMessagePairs: CodecMessage<TMessage>[] = [];\n\n /** Cached visible node-key Set — for O(1) lookup in event scoping. */\n private _lastVisibleNodeKeySet = new Set<string>();\n\n /** Whether there are more history pages to fetch from the channel. */\n private _hasMoreHistory = false;\n\n /** Internal state for continuing history pagination. */\n private _lastHistoryPage: HistoryPage | undefined;\n\n /** Buffer of withheld nodes (input + reply), drained newest-first by successive loadOlder() calls. */\n private readonly _withheldBuffer: ConversationNode<TProjection>[] = [];\n\n /** Unsubscribe functions for tree event subscriptions. */\n private readonly _unsubs: (() => void)[] = [];\n\n /**\n * Cached result of the last flat-nodes computation. Drives the visible\n * message snapshot exposed via `getMessages()`; refreshed by\n * `_computeFlatNodes()` on structural changes, selection changes,\n * and history reveal.\n */\n private _cachedNodes: ConversationNode<TProjection>[] = [];\n\n private _loadingOlder = false;\n private _processingHistory = false;\n private _closed = false;\n\n constructor(options: ViewOptions<TInput, TOutput, TProjection, TMessage>) {\n this._tree = options.tree;\n this._channel = options.channel;\n this._codec = options.codec;\n this._sendDelegate = options.sendDelegate;\n this._onClose = options.onClose;\n this._logger = options.logger.withContext({ component: 'View' });\n this._logger.trace('DefaultView();');\n this._emitter = new EventEmitter<ViewEventsMap>(this._logger);\n\n // Compute initial cache and snapshot visible state\n this._cachedNodes = this._computeFlatNodes();\n this._updateVisibleSnapshot(this._cachedNodes);\n\n // Subscribe to tree events and re-emit scoped versions\n this._unsubs.push(\n this._tree.on('update', () => {\n this._onTreeUpdate();\n }),\n this._tree.on('ably-message', (msg) => {\n this._onTreeAblyMessage(msg);\n }),\n this._tree.on('run', (event) => {\n this._onTreeRun(event);\n }),\n this._tree.on('output', (event) => {\n this._onTreeOutput(event);\n }),\n );\n }\n\n /**\n * Handle decoded outputs folded into a Run (streaming delta). If the run\n * is on the visible chain, recompute the flat message list and emit\n * `update`.\n * @param event - The output event from the Tree.\n */\n private _onTreeOutput(event: OutputEvent<TOutput>): void {\n if (this._processingHistory) return;\n // The fold target may be a reply run (event.runId) or a user input node\n // (event.runId undefined — the agent mints run-ids, so an input fold has\n // none). Gate on whichever key the visible set holds.\n const folded =\n (event.runId !== undefined && this._lastVisibleNodeKeySet.has(event.runId)) ||\n (event.inputCodecMessageId !== undefined && this._lastVisibleNodeKeySet.has(event.inputCodecMessageId));\n if (!folded) return;\n\n // The Tree emits `output` once per inbound message fold (with empty\n // `events` for inputs-only folds), so it fires whenever a visible Run's\n // projection changed and we always re-emit. The Reducer contract permits\n // in-place mutation, which means we cannot use projection-ref or\n // TMessage-ref equality to detect change: a streaming chunk legitimately\n // mutates the same UIMessage object, and a ref-equality short-circuit\n // would suppress every update. React state setters at the subscriber\n // boundary already dedup by array reference, so a redundant emit is a\n // no-op for unchanged hook consumers.\n this._lastVisibleProjections = this._cachedNodes.map((n) => n.projection);\n this._lastVisibleMessagePairs = this._extractMessages(this._cachedNodes);\n this._emitter.emit('update');\n }\n\n // -------------------------------------------------------------------------\n // Public query methods\n // -------------------------------------------------------------------------\n\n getMessages(): CodecMessage<TMessage>[] {\n return this._lastVisibleMessagePairs;\n }\n\n runs(): RunInfo[] {\n // `_cachedNodes` is the visible node chain (inputs + reply runs) with\n // pagination and sibling selection already applied. RunInfo is reply-run\n // shaped, so filter to runs before projecting.\n return this._cachedNodes\n .filter((node): node is RunNode<TProjection> => node.kind === 'run')\n .map((node) => _toRunInfo(node));\n }\n\n /**\n * Compute the fresh visible node chain. The Tree's `visibleNodes` already\n * applies kind-blind reachability and sibling selection (edit versions /\n * regenerate runs collapse to the selected member), so the View only layers\n * its pagination window on top: drop nodes whose key is currently withheld.\n * @returns A fresh array of visible nodes (inputs + reply runs).\n */\n private _computeFlatNodes(): ConversationNode<TProjection>[] {\n const treeNodes = this._treeVisibleNodes();\n if (this._withheldRunIds.size === 0) return treeNodes;\n return treeNodes.filter((node) => !this._withheldRunIds.has(nodeKey(node)));\n }\n\n /**\n * Recompute the visible node chain, refresh the cache + snapshot, and emit\n * `update` unconditionally. Use after a mutation that always changes the\n * visible output (e.g. an explicit selection or a withheld-batch reveal).\n */\n private _recomputeAndEmit(): void {\n this._cachedNodes = this._computeFlatNodes();\n this._updateVisibleSnapshot(this._cachedNodes);\n this._emitter.emit('update');\n }\n\n /**\n * Recompute the visible node chain and, only if it differs from the current\n * snapshot, refresh the cache + snapshot and emit `update`. Use after a\n * mutation that may or may not move the visible window (e.g. a structural\n * tree update, or a deferred regenerate promotion that may already match).\n */\n private _recomputeAndEmitIfChanged(): void {\n const nodes = this._computeFlatNodes();\n if (this._visibleChanged(nodes)) {\n this._cachedNodes = nodes;\n this._updateVisibleSnapshot(nodes);\n this._emitter.emit('update');\n }\n }\n\n /**\n * Resolve the reply Run that owns a codec-message-id, narrowing the Tree's\n * node union to a {@link RunNode}. A user-input codec-message-id resolves to\n * an input node and yields `undefined` here — callers that must handle input\n * nodes use {@link _tree.getNodeByCodecMessageId} directly.\n * @param codecMessageId - The codec-message-id to resolve.\n * @returns The owning RunNode, or undefined if absent or not a reply Run.\n */\n private _runByCodecMessageId(codecMessageId: string): RunNode<TProjection> | undefined {\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n return node?.kind === 'run' ? node : undefined;\n }\n\n /**\n * Extract the flat TMessage[] from a visible node chain.\n *\n * In the two-node model the Tree's `visibleNodes` has already selected one\n * member per sibling group (the chosen edit version, the chosen regenerate\n * run), so a regenerate is just a sibling reply run that appears in place of\n * the original. Each visible node contributes its own messages in projection\n * order; the flat list is their concatenation.\n *\n * Deferred caveat: a mid-reply regenerate that replaces a non-head message\n * inside a multi-message reply run is not expressible as a sibling run in\n * this model and is not handled here (see the `regenerate-of-multi-message`\n * golden test).\n * @param nodes - The visible nodes (inputs + reply runs) in chronological order.\n * @returns The flat message list, each message paired with its codec-message-id.\n */\n private _extractMessages(nodes: ConversationNode<TProjection>[]): CodecMessage<TMessage>[] {\n const messages: CodecMessage<TMessage>[] = [];\n for (const node of nodes) {\n for (const m of this._codec.getMessages(node.projection)) {\n messages.push(m);\n }\n }\n return messages;\n }\n\n hasOlder(): boolean {\n return this._withheldBuffer.length > 0 || this._hasMoreHistory;\n }\n\n /**\n * Reveal up to `limit` older Runs in this view.\n *\n * The pagination unit is the **Run**, not the message. A single Run\n * typically materialises into multiple messages (e.g. user + assistant\n * pair) so revealing `limit` Runs may add several messages to the flat\n * list returned by {@link getMessages}. Channel pages don't align to\n * Run boundaries, so {@link _loadUntilVisible} keeps fetching channel\n * pages until at least `limit` Runs are buffered (or the channel is\n * exhausted).\n * @param limit - Maximum number of older Runs to reveal. Defaults to 100.\n */\n async loadOlder(limit = 100): Promise<void> {\n if (this._closed || this._loadingOlder) return;\n this._loadingOlder = true;\n this._logger.trace('DefaultView.loadOlder();', { limit });\n\n try {\n // Drain withheld buffer first (older nodes, released newest-first). The\n // buffer holds a union of input + reply nodes, so this splices the newest\n // `limit` NODES, not `limit` runs. Because an input node travels with the\n // reply run it precedes, a drain may surface fewer than `limit` runs.\n if (this._withheldBuffer.length > 0) {\n const batch = this._withheldBuffer.splice(-limit, limit);\n this._releaseWithheld(batch);\n return;\n }\n\n // Buffer exhausted - load from channel history.\n if (!this._hasMoreHistory && !this._lastHistoryPage) {\n await this._loadFirstPage(limit);\n return;\n }\n\n if (!this._hasMoreHistory) return;\n\n if (!this._lastHistoryPage?.hasNext()) {\n this._hasMoreHistory = false;\n return;\n }\n\n const nextPage = await this._lastHistoryPage.next();\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- close() may be called during await\n if (this._closed || !nextPage) {\n if (!nextPage) this._hasMoreHistory = false;\n return;\n }\n\n await this._revealFromPage(nextPage, limit);\n } catch (error) {\n this._logger.error('DefaultView.loadOlder(); failed', { error });\n throw error;\n } finally {\n this._loadingOlder = false;\n }\n }\n\n // -------------------------------------------------------------------------\n // Run lookup\n // -------------------------------------------------------------------------\n\n runOf(codecMessageId: string): RunInfo | undefined {\n this._logger.trace('DefaultView.runOf();', { codecMessageId });\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (!node) return undefined;\n if (node.kind === 'run') return _toRunInfo(node);\n // Input node: resolve to its selected reply run (undefined if none started).\n const reply = this._selectedReplyRun(node.codecMessageId);\n return reply ? _toRunInfo(reply) : undefined;\n }\n\n /**\n * Resolve the reply run currently selected for an input node, honouring the\n * View's regenerate selection. Falls back to the latest reply run when no\n * selection has been recorded; undefined when no reply run has started.\n * @param inputCodecMessageId - The input node's codec-message-id.\n * @returns The selected reply RunNode, or undefined.\n */\n private _selectedReplyRun(inputCodecMessageId: string): RunNode<TProjection> | undefined {\n const replies = this._tree.getReplyRuns(inputCodecMessageId);\n if (replies.length === 0) return undefined;\n if (replies.length === 1) return replies[0];\n // Multiple reply runs = a regenerate group. Honour the View's selection\n // (keyed by group root) else default to the latest.\n const groupRoot = this._tree.getGroupRoot(replies[0]?.runId ?? '');\n const sel = this._regenSelections.get(groupRoot);\n const selectedKey = sel && sel.kind !== 'pending' ? sel.selectedRunId : undefined;\n if (selectedKey !== undefined) {\n const chosen = replies.find((r) => r.runId === selectedKey);\n if (chosen) return chosen;\n }\n // Latest by startSerial; getReplyRuns is set-ordered, so sort defensively.\n return replies.toSorted((a, b) => (a.startSerial ?? '').localeCompare(b.startSerial ?? '')).at(-1);\n }\n\n run(runId: string): RunInfo | undefined {\n this._logger.trace('DefaultView.run();', { runId });\n const run = this._tree.getRunNode(runId);\n return run ? _toRunInfo(run) : undefined;\n }\n\n // -------------------------------------------------------------------------\n // Branch navigation (msg-anchored)\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT13c, AIT-CT13d — branch points are codec-message-id\n // anchored. The View resolves the anchor (the user prompt for edits,\n // the assistant slot for regens) and routes the selection to the\n // appropriate internal selection map. Tree-level introspection\n // (RunNode access, runId-keyed queries) remains on the {@link Tree}.\n\n branchSelection(codecMessageId: string): BranchSelection<TMessage> {\n const branch = this._resolveMessageBranchPoint(codecMessageId);\n if (branch) {\n // Each sibling contributes its head message as the branch-arrow slot:\n // for an edit fork that is the alternate user prompt; for a regenerate\n // group it is the variant's first (anchor-equivalent) message.\n const siblings = branch.siblings.flatMap((s) => {\n const first = this._codec.getMessages(s.projection).at(0);\n return first ? [first.message] : [];\n });\n\n if (siblings.length > 0) {\n const index = this._resolveSelectedIndex(branch);\n const clamped = Math.max(0, Math.min(index, siblings.length - 1));\n const selected = siblings[clamped];\n return {\n hasSiblings: siblings.length > 1,\n siblings,\n index: clamped,\n selected,\n };\n }\n }\n\n // Known non-anchor message: the bundle's invariant is that\n // `siblings` contains the rendered message itself for any known\n // codec-message-id, so plain bubbles get `siblings.length === 1`\n // (not `0`) and the indexing space matches between read and write.\n // Resolve the owning node kind-blind — a plain user prompt is an input\n // node, an assistant message lives in a reply run; both carry a projection.\n const owner = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (owner) {\n const found = this._codec.getMessages(owner.projection).find((m) => m.codecMessageId === codecMessageId);\n if (found !== undefined) {\n return { hasSiblings: false, siblings: [found.message], index: 0, selected: found.message };\n }\n }\n\n // Unknown id, or the owner Run is known but the codec doesn't surface\n // a message with this id from the projection (e.g. an event-only fold\n // such as a tool result that mutates an assistant in-place without\n // exposing its own TMessage). Treat both as \"no rendered message\",\n // returning the safe empty bundle.\n return { hasSiblings: false, siblings: [], index: 0, selected: undefined };\n }\n\n // Spec: AIT-CT13c, AIT-CT13d\n selectSibling(codecMessageId: string, index: number): void {\n this._logger.trace('DefaultView.selectSibling();', { codecMessageId, index });\n const branch = this._resolveMessageBranchPoint(codecMessageId);\n if (!branch) return;\n const clamped = Math.max(0, Math.min(index, branch.siblings.length - 1));\n const selected = branch.siblings[clamped];\n if (!selected) return; // unreachable: clamped is always in bounds\n if (branch.kind === 'fork-of') {\n this._branchSelections.set(branch.groupRoot, { kind: 'user', selectedKey: nodeKey(selected) });\n this._logger.debug('DefaultView.selectSibling(); fork-of', {\n codecMessageId,\n index: clamped,\n selectedKey: nodeKey(selected),\n });\n } else {\n this._regenSelections.set(branch.groupRoot, { kind: 'user', selectedRunId: nodeKey(selected) });\n this._logger.debug('DefaultView.selectSibling(); regenerate', {\n codecMessageId,\n index: clamped,\n selectedRunId: nodeKey(selected),\n groupRoot: branch.groupRoot,\n });\n }\n this._recomputeAndEmit();\n }\n\n /**\n * Resolve the currently selected sibling's index inside a branch group.\n * Pending selections fall back to the latest sibling. The caller clamps\n * the returned index against any post-extraction filtering.\n * @param branch - Resolved branch-point descriptor from `_resolveMessageBranchPoint`.\n * @returns The selected sibling's index within `branch.siblings`.\n */\n private _resolveSelectedIndex(branch: MessageBranchPoint<TProjection>): number {\n if (branch.kind === 'fork-of') {\n const sel = this._branchSelections.get(branch.groupRoot);\n if (!sel) return branch.siblings.length - 1;\n const idx = branch.siblings.findIndex((n) => nodeKey(n) === sel.selectedKey);\n return idx === -1 ? branch.siblings.length - 1 : idx;\n }\n const sel = this._regenSelections.get(branch.groupRoot);\n if (!sel || sel.kind === 'pending') return branch.siblings.length - 1;\n const idx = branch.siblings.findIndex((n) => nodeKey(n) === sel.selectedRunId);\n return idx === -1 ? branch.siblings.length - 1 : idx;\n }\n\n /**\n * Resolve the branch point anchored at `codecMessageId`, if any.\n *\n * Returns the resolved group `kind` along with the sibling list so the\n * caller can update the correct selection map without re-entering the\n * runId-based `select()` dispatch (which biases to fork-of first and\n * would mis-route a regen-anchor codec-message-id when the owning Run is in\n * BOTH groups — e.g. R1 owns both a user prompt that got edited and\n * an assistant that got regenerated).\n *\n * Two anchor cases:\n * - **fork-of** — `codecMessageId` is the first message of a Run in a fork-of\n * sibling group (edit-style branch point anchored at the user prompt).\n * - **regen** — `codecMessageId` is the regen-anchor itself (in the owner Run)\n * or content of a regenerator Run (regen-style branch point anchored\n * at the assistant slot).\n * @param codecMessageId - The codec-message-id to look up.\n * @returns The kind + sibling list + group key (runId for fork-of,\n * anchor codec-message-id for regen), or undefined when `codecMessageId` is not an\n * anchor in either group type.\n */\n private _resolveMessageBranchPoint(codecMessageId: string): MessageBranchPoint<TProjection> | undefined {\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (!node) return undefined;\n\n // Edit-fork branch point: `codecMessageId` is a user INPUT node that has\n // sibling input nodes (alternate prompts via fork-of). The anchor is the\n // input node's own codec-message-id.\n if (node.kind === 'input') {\n const siblings = this._tree.getSiblingNodes(node.codecMessageId);\n if (siblings.length > 1) {\n return { kind: 'fork-of', groupRoot: this._tree.getGroupRoot(node.codecMessageId), siblings };\n }\n return undefined;\n }\n\n // Regenerate branch point: `codecMessageId` is owned by a reply run that has\n // sibling reply runs (the original reply + its regenerators, all parented at\n // the same input node). Anchor on the head message of the run so arrows\n // appear once per variant, not on every follow-up message.\n const siblings = this._tree.getSiblingNodes(node.runId);\n if (siblings.length > 1) {\n const firstMsg = this._codec.getMessages(node.projection).at(0);\n if (firstMsg?.codecMessageId === codecMessageId) {\n return { kind: 'regen', groupRoot: this._tree.getGroupRoot(node.runId), siblings };\n }\n }\n\n return undefined;\n }\n\n // -------------------------------------------------------------------------\n // Write operations\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT3, AIT-CT4\n async send(input: TInput | TInput[], options?: SendOptions): Promise<ActiveRun> {\n this._logger.trace('DefaultView.send();');\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to send; view is closed', ErrorCode.InvalidArgument, 400);\n }\n\n const normalised = _normaliseSend<TInput>(input);\n\n // The codec-message-id of the visible branch tail — the delegate uses it\n // for auto-parent routing on fresh user messages.\n const parentCodecMessageId = this._lastVisibleMessagePairs.at(-1)?.codecMessageId;\n\n const result = await this._sendDelegate(normalised, options, parentCodecMessageId);\n this._applyForkAutoSelect(result, options);\n return result;\n }\n\n /**\n * Auto-select / pin branch selections after a forking send.\n * @param result - The ActiveRun returned by the delegate.\n * @param options - The SendOptions passed by the caller.\n */\n private _applyForkAutoSelect(result: ActiveRun, options: SendOptions | undefined): void {\n // Spec: AIT-CT13e\n if (!options?.forkOf) return;\n\n // An edit inserts a NEW user input node optimistically; its codec-message-id\n // is the (only) optimistic id and IS its node key. Edit forks are input-node\n // sibling groups, so the selection is keyed by the input group root and the\n // selected member is the new input node's key.\n const editedInputKey = result.optimisticCodecMessageIds.at(0);\n if (editedInputKey === undefined) return;\n const groupRoot = this._tree.getGroupRoot(editedInputKey);\n\n this._branchSelections.set(groupRoot, { kind: 'auto', selectedKey: editedInputKey });\n this._recomputeAndEmit();\n }\n\n /**\n * Auto-select / pin the regenerate group anchored at `anchorCodecMessageId` so\n * the new Run's content appears as soon as the agent's run-start lands.\n *\n * `View.regenerate()` calls this with the assistant codec-message-id being\n * regenerated. The Run doesn't exist yet on the channel (the regenerate\n * wire is wire-only); the selection is recorded as `pending` and\n * promoted to `auto` by `_pinRegenSelections` once the corresponding\n * Run is created in the tree.\n * @param result - The ActiveRun returned by the delegate (run-id is the new regenerator's).\n * @param anchorCodecMessageId - The codec-message-id of the assistant being regenerated.\n */\n private _applyRegenerateAutoSelect(result: ActiveRun, anchorCodecMessageId: string): void {\n // A regenerate produces a new reply run parented at the SAME input node as\n // the original reply (the regenerate group). The agent mints the run-id, so\n // we cannot pin by it synchronously. Resolve the group root from the\n // original reply run owning the anchor, and pin a pending selection keyed by\n // that group root, carrying the regenerate carrier's codec-message-id\n // (`result.inputCodecMessageId`) so we can promote when the new reply run lands.\n const anchorRun = this._runByCodecMessageId(anchorCodecMessageId);\n if (!anchorRun) return;\n const groupRoot = this._tree.getGroupRoot(anchorRun.runId);\n\n this._regenSelections.set(groupRoot, {\n kind: 'pending',\n carrierCodecMessageId: result.inputCodecMessageId,\n });\n this._logger.debug('DefaultView._applyRegenerateAutoSelect(); deferring regenerate selection', {\n anchorCodecMessageId,\n groupRoot,\n carrier: result.inputCodecMessageId,\n });\n\n // The new reply run may already be in the tree (run-start raced ahead of the\n // sendDelegate resolution). Promote now and recompute so the visible set\n // catches up without waiting for the next structural change.\n this._resolvePendingRegenSelections();\n this._recomputeAndEmitIfChanged();\n }\n\n // Spec: AIT-CT5, AIT-CT13d\n async regenerate(messageId: string, options?: SendOptions): Promise<ActiveRun> {\n this._logger.trace('DefaultView.regenerate();', { messageId });\n\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to regenerate; view is closed', ErrorCode.InvalidArgument, 400);\n }\n\n // `messageId` is the assistant being regenerated. The new Run is a\n // continuation of the regenerated message's Run, not a fork: the\n // message-level replacement (new assistant supersedes the original)\n // happens at projection extraction time. We still resolve the parent\n // user prompt so the new assistant's wire `parent` is correct,\n // and we send the truncated history (through the parent inclusive)\n // so the LLM re-answers the right message.\n const targetRun = this._runByCodecMessageId(messageId);\n if (!targetRun) {\n throw new Ably.ErrorInfo(\n `unable to regenerate; message not found in tree: ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const parentCodecMessageId = this._findParentMsgId(targetRun, messageId);\n if (!parentCodecMessageId) {\n throw new Ably.ErrorInfo(\n `unable to regenerate; parent user message not found for ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n // Canonical regen anchor: when the user clicks Regenerate on an\n // already-regenerated assistant, the new alternative SHOULD belong\n // to the SAME branch point as the previous regen — but ONLY when\n // the target is the position-equivalent of the group anchor (the\n // head message of the regenerator Run). For a trailing follow-up\n // message inside a regenerator Run (e.g. the LLM text after the\n // regenerated tool call), the user expects the regen to anchor at\n // the specific message they clicked, not roll up to the group root.\n // Rebasing trailing regens to the group root produces a confusing\n // \"N+1 / N+1\" counter on the tool-call bubble and runs the whole\n // turn from scratch instead of just regenerating the text.\n let regenAnchorMsgId = messageId;\n if (targetRun.regeneratesCodecMessageId !== undefined) {\n const firstMsg = this._codec.getMessages(targetRun.projection).at(0);\n if (firstMsg?.codecMessageId === messageId) {\n regenAnchorMsgId = targetRun.regeneratesCodecMessageId;\n }\n }\n\n const sendOptions: SendOptions = {\n ...options,\n parent: parentCodecMessageId,\n };\n\n // Mint a regenerate input via the codec. The codec's well-known\n // `Regenerate` carries `target: regenAnchorMsgId` and `parent:\n // parentCodecMessageId`; the session reads those fields off the input\n // directly when building transport headers (`fork-of` and\n // `parent`). The agent's input-event lookup catches the wire signal;\n // no tree-upsert / projection fold runs locally.\n const regenerate = this._codec.createRegenerate(regenAnchorMsgId, parentCodecMessageId);\n const result = await this._sendDelegate([regenerate], sendOptions, parentCodecMessageId);\n this._applyRegenerateAutoSelect(result, regenAnchorMsgId);\n return result;\n }\n\n // Spec: AIT-CT6\n async edit(messageId: string, inputs: TInput | TInput[], options?: SendOptions): Promise<ActiveRun> {\n this._logger.trace('DefaultView.edit();', { messageId });\n\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to edit; view is closed', ErrorCode.InvalidArgument, 400);\n }\n\n // The edit target is a user prompt — a run-less INPUT node — so resolve\n // it kind-blind, not via the reply-run-only lookup.\n const targetNode = this._tree.getNodeByCodecMessageId(messageId);\n if (!targetNode) {\n throw new Ably.ErrorInfo(\n `unable to edit; message not found in tree: ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const parentCodecMessageId = this._findParentMsgId(targetNode, messageId);\n\n return this.send(inputs, {\n ...options,\n forkOf: messageId,\n parent: parentCodecMessageId,\n });\n }\n\n /**\n * Find the codec-message-id of the message immediately preceding `targetMsgId` in\n * the visible conversation.\n *\n * Consults the View's visible message chain first so message-level\n * replacements (regenerate) are respected: regenerating an\n * already-regenerated assistant lands the predecessor on the user\n * prompt the regen is responding to, NOT on the hidden original\n * assistant that occupies the same conversation slot. Falls back to a\n * projection-walk for the rare case where `targetMsgId` isn't on the\n * visible chain (e.g. caller is operating on a Run that's selection-\n * hidden by the current branch).\n * @param targetNode - The node (input node or reply run) that owns `targetMsgId`.\n * @param targetMsgId - The codec-message-id to find the parent of.\n * @returns The parent codec-message-id, or undefined if no predecessor exists.\n */\n private _findParentMsgId(targetNode: ConversationNode<TProjection>, targetMsgId: string): string | undefined {\n const visible = this._lastVisibleMessagePairs;\n const visIdx = visible.findIndex((m) => m.codecMessageId === targetMsgId);\n if (visIdx > 0) {\n return visible[visIdx - 1]?.codecMessageId;\n }\n if (visIdx === 0) return undefined;\n\n const messages = this._codec.getMessages(targetNode.projection);\n const idx = messages.findIndex((m) => m.codecMessageId === targetMsgId);\n if (idx > 0) {\n return messages[idx - 1]?.codecMessageId;\n }\n if (idx === 0 && targetNode.parentCodecMessageId !== undefined) {\n // The structural predecessor is the node owning parentCodecMessageId\n // (an input node, or a prior reply run). Its tail message is the parent.\n const parentNode = this._tree.getNodeByCodecMessageId(targetNode.parentCodecMessageId);\n if (parentNode) {\n return this._codec.getMessages(parentNode.projection).at(-1)?.codecMessageId;\n }\n }\n return undefined;\n }\n\n // -------------------------------------------------------------------------\n // Event subscription\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT8a, AIT-CT8b, AIT-CT8e\n on(event: 'update', handler: () => void): () => void;\n on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;\n on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;\n on(\n event: 'update' | 'ably-message' | 'run',\n handler: (() => void) | ((msg: Ably.InboundMessage) => void) | ((event: RunLifecycleEvent) => void),\n ): () => void {\n // CAST: overload signatures enforce correct handler types per event name.\n const cb = handler as (arg: ViewEventsMap[keyof ViewEventsMap]) => void;\n this._emitter.on(event, cb);\n return () => {\n this._emitter.off(event, cb);\n };\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n close(): void {\n if (this._closed) return;\n this._logger.info('DefaultView.close();');\n this._closed = true;\n this._loadingOlder = false;\n for (const unsub of this._unsubs) unsub();\n this._unsubs.length = 0;\n this._emitter.off();\n this._branchSelections.clear();\n this._regenSelections.clear();\n this._withheldRunIds.clear();\n this._withheldBuffer.length = 0;\n this._onClose?.();\n }\n\n // -------------------------------------------------------------------------\n // Private: history loading\n // -------------------------------------------------------------------------\n\n private async _loadFirstPage(limit: number): Promise<void> {\n // loadHistory's limit counts complete domain messages per page (not\n // Runs); see `_RUN_TO_MESSAGE_FETCH_FACTOR` for the scaling rationale.\n const messageLimit = limit * _RUN_TO_MESSAGE_FETCH_FACTOR;\n const firstPage = await loadHistory(this._channel, { limit: messageLimit }, this._logger);\n if (this._closed) return;\n await this._revealFromPage(firstPage, limit);\n }\n\n /**\n * Walk channel history from `page` until at least `limit` new Runs are\n * observed (or the channel is exhausted), then reveal the newest batch and\n * withhold the rest. Snapshots the already-visible nodes up front so only\n * newly-observed Runs count toward `limit`. No-op if the view closed during\n * the page walk.\n * @param page - The decoded history page to start from.\n * @param limit - Max Runs to reveal in this batch.\n */\n private async _revealFromPage(page: HistoryPage, limit: number): Promise<void> {\n // Snapshot before loading: every node already in the tree stays visible.\n const beforeRunIds = new Set(this._treeVisibleNodes().map((n) => nodeKey(n)));\n\n const { newVisible, lastPage } = await this._loadUntilVisible(page, limit, beforeRunIds);\n if (this._closed) return;\n this._lastHistoryPage = lastPage;\n this._hasMoreHistory = lastPage.hasNext();\n this._splitReveal(newVisible, limit);\n }\n\n /**\n * Reveal the newest `limit` Runs from `newVisible` and withhold the rest\n * so subsequent `loadOlder` calls can drain them. Called by\n * {@link _revealFromPage} to enforce the Run-unit pagination contract.\n * @param newVisible - Newly observed Runs from the history fetch.\n * @param limit - Max Runs to reveal in this batch.\n */\n private _splitReveal(newVisible: ConversationNode<TProjection>[], limit: number): void {\n // Reveal granularity is the reply RUN; an input node travels with the reply\n // run it precedes. Walk newest-first, counting reply runs toward `limit`,\n // and split the union list at the resulting boundary so an input + its reply\n // are revealed or withheld together.\n let runs = 0;\n let splitIdx = newVisible.length; // index of first revealed node\n for (let i = newVisible.length - 1; i >= 0; i--) {\n const node = newVisible[i];\n if (node?.kind === 'run') {\n if (runs === limit) break;\n runs++;\n }\n splitIdx = i;\n }\n const batch = newVisible.slice(splitIdx);\n const withheld = newVisible.slice(0, splitIdx);\n for (const n of withheld) {\n this._withheldRunIds.add(nodeKey(n));\n }\n this._withheldBuffer.push(...withheld);\n this._releaseWithheld(batch);\n }\n\n /**\n * Replay a history page's raw messages into the Tree. Dispatches by Ably\n * message name to run-lifecycle vs. regular wire messages, mirroring the\n * live `client-session._handleMessage` decode loop. Uses a fresh decoder\n * since the session's live decoder maintains its own stream-tracker state.\n * @param page - The history page returned by `loadHistory`.\n */\n private _processHistoryPage(page: HistoryPage): void {\n this._processingHistory = true;\n try {\n // Reconstruct the tree via the shared decode-fold engine — the same path\n // the client's live loop uses, so history replay can't drift from it.\n const decoder = this._codec.createDecoder();\n for (const rawMsg of page.rawMessages) {\n applyWireMessage(this._tree, decoder, rawMsg);\n }\n\n // Emit ably-message in a batch AFTER the whole page is applied, so a\n // subscriber resolving the owning Run sees the fully-rebuilt tree.\n for (const msg of page.rawMessages) {\n this._tree.emitAblyMessage(msg);\n }\n } finally {\n this._processingHistory = false;\n }\n }\n\n private async _loadUntilVisible(\n firstPage: HistoryPage,\n target: number,\n beforeRunIds: Set<string>,\n ): Promise<{ newVisible: ConversationNode<TProjection>[]; lastPage: HistoryPage }> {\n this._processHistoryPage(firstPage);\n let page = firstPage;\n\n const newVisibleCount = (): number => {\n let count = 0;\n for (const n of this._treeVisibleNodes()) {\n // Pagination counts reply RUNS toward the target (an input node travels\n // with the reply run it precedes — see `_splitReveal`).\n if (n.kind === 'run' && !beforeRunIds.has(nodeKey(n))) count++;\n }\n return count;\n };\n\n while (newVisibleCount() < target && page.hasNext()) {\n const nextPage = await page.next();\n if (!nextPage || this._closed) break;\n this._processHistoryPage(nextPage);\n page = nextPage;\n }\n\n const newVisible = this._treeVisibleNodes().filter((n) => !beforeRunIds.has(nodeKey(n)));\n return { newVisible, lastPage: page };\n }\n\n // Spec: AIT-CT11a\n private _releaseWithheld(nodes: ConversationNode<TProjection>[]): void {\n for (const n of nodes) {\n this._withheldRunIds.delete(nodeKey(n));\n }\n if (nodes.length > 0) {\n this._recomputeAndEmit();\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: scoped event forwarding\n // -------------------------------------------------------------------------\n\n private _updateVisibleSnapshot(nodes?: ConversationNode<TProjection>[]): void {\n const resolved = nodes ?? this._cachedNodes;\n // Identity key = nodeKey (runId for reply runs, codecMessageId for inputs),\n // so the visible set scopes events for both kinds and input-node parents.\n this._lastVisibleNodeKeys = resolved.map((n) => nodeKey(n));\n this._lastVisibleNodeKeySet = new Set(this._lastVisibleNodeKeys);\n this._lastVisibleProjections = resolved.map((n) => n.projection);\n this._lastVisibleMessagePairs = this._extractMessages(resolved);\n }\n\n private _onTreeUpdate(): void {\n // Suppress update forwarding while processing history pages. During\n // _processHistoryPage, each tree.applyMessage() fires this handler\n // synchronously — but _withheldRunIds hasn't been populated yet, so\n // _computeFlatNodes() would return unfiltered history. Without this guard,\n // subscribers briefly see all history Runs before the pagination window\n // is applied. The final update is emitted by _releaseWithheld after\n // withholding is set up.\n if (this._processingHistory) return;\n\n // The Tree emits `update` only on structural change (new/removed Run,\n // sort-reorder, startSerial promotion, run-start backfill), so every\n // update reaching here warrants a full re-walk. Content-only folds flow\n // through `output` (_onTreeOutput) instead.\n\n // Pin selections for previously-visible Runs that now have siblings.\n // This prevents new forks (from other views' edits/regenerates) from\n // shifting this view to a branch the user didn't navigate to.\n this._pinBranchSelections();\n this._resolvePendingRegenSelections();\n\n this._recomputeAndEmitIfChanged();\n }\n\n /**\n * Build the unified selection map the Tree's `visibleNodes` consumes:\n * `groupRootKey -> selectedKey`, covering both edit forks (input-node groups,\n * keyed by the input group root) and regenerate groups (reply-run groups,\n * keyed by the original reply's group root). Pending entries (no chosen\n * member yet) are omitted so the Tree falls back to the latest sibling.\n * @returns The merged group-root → selected-key map.\n */\n private _resolveSelections(): Map<string, string> {\n const resolved = new Map<string, string>();\n for (const [groupRoot, sel] of this._branchSelections) {\n resolved.set(groupRoot, sel.selectedKey);\n }\n for (const [groupRoot, sel] of this._regenSelections) {\n if (sel.kind === 'pending') continue;\n resolved.set(groupRoot, sel.selectedRunId);\n }\n return resolved;\n }\n\n /**\n * The Tree's visible node chain under this view's current selections — the\n * reachable, sibling-resolved nodes before the View's pagination window is\n * applied.\n * @returns The selection-resolved visible node chain.\n */\n private _treeVisibleNodes(): ConversationNode<TProjection>[] {\n return this._tree.visibleNodes(this._resolveSelections());\n }\n\n /**\n * For each previously-visible Run that now has siblings but no explicit\n * selection, pin the selection to that Run's runId. This preserves the\n * current branch when new forks appear from other views or external\n * sources.\n *\n * Exception: if the fork was initiated by this view (tracked as a\n * `pending` BranchSelection), select the newest sibling (the awaited Run)\n * instead of pinning the old one.\n */\n private _pinBranchSelections(): void {\n for (const key of this._lastVisibleNodeKeys) {\n const node = this._tree.getNode(key);\n // Edit forks are INPUT-node sibling groups; only input nodes pin here.\n // Regenerate (reply-run) groups roll forward via _resolvePendingRegenSelections.\n if (node?.kind !== 'input') continue;\n const siblings = this._tree.getSiblingNodes(key);\n if (siblings.length <= 1) continue;\n const groupRoot = this._tree.getGroupRoot(key);\n const existing = this._branchSelections.get(groupRoot);\n\n // Spec: AIT-CT13f — external edit fork: pin to the currently-visible\n // sibling so a fork from another view doesn't drift this view's branch.\n if (existing) continue;\n this._branchSelections.set(groupRoot, { kind: 'pinned', selectedKey: key });\n }\n }\n\n /**\n * Roll `pending` and `auto` regenerate selections forward to the newest\n * group member. A regenerate slot defaults to the latest member, so each\n * new regenerator (this view's awaited run, or an external one) auto-rolls\n * the slot forward — UNLESS the user explicitly selected an earlier member\n * (`user`), which pins and is left untouched. The agent mints the run-id, so\n * we can't match the awaited run by id — once the group grows we adopt the\n * newest as the selected member.\n */\n private _resolvePendingRegenSelections(): void {\n for (const [groupRoot, sel] of this._regenSelections) {\n if (sel.kind === 'user') continue;\n const group = this._tree.getSiblingNodes(groupRoot).filter((n): n is RunNode<TProjection> => n.kind === 'run');\n if (group.length <= 1) continue;\n const newest = group.at(-1);\n if (!newest) continue;\n this._regenSelections.set(groupRoot, { kind: 'auto', selectedRunId: newest.runId });\n }\n }\n\n private _onTreeAblyMessage(msg: Ably.InboundMessage): void {\n // Re-emit only if the message corresponds to a visible Run\n const headers = getTransportHeaders(msg);\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n const runId = headers[HEADER_RUN_ID];\n\n if (!codecMessageId && !runId) {\n // Lifecycle / control events with no run/message identity (cancel, error)\n // are always forwarded.\n this._emitter.emit('ably-message', msg);\n return;\n }\n\n if (runId && this._lastVisibleNodeKeySet.has(runId)) {\n this._emitter.emit('ably-message', msg);\n }\n }\n\n private _onTreeRun(event: RunLifecycleEvent): void {\n // Check if the run is already on the visible branch.\n if (this._lastVisibleNodeKeySet.has(event.runId)) {\n this._emitter.emit('run', event);\n return;\n }\n\n // For run-start, use branch metadata to predict visibility before\n // messages arrive. Own runs have optimistic inserts (caught above).\n // Remote runs carry parent/forkOf from the agent.\n if (event.type === 'start' && this._isRunStartVisible(event)) {\n this._lastVisibleNodeKeySet.add(event.runId);\n this._emitter.emit('run', event);\n }\n }\n\n /**\n * Predict whether a run-start's messages will be visible on this view's\n * branch using the parent/forkOf metadata from the event.\n * @param event - The run-start lifecycle event.\n * @returns True if the run is expected to be visible on this view's branch.\n */\n private _isRunStartVisible(event: RunLifecycleEvent & { type: 'start' }): boolean {\n const { parent } = event;\n\n // No parent metadata — can't determine branch, forward as default.\n if (parent === undefined) return true;\n\n // The wire `parent` is a codec-message-id (the prior message). Resolve it\n // kind-blind to its owning NODE — an input node (the user prompt this run\n // replies to) or a prior reply run — and check that node's key against the\n // visible set. Input-node keys are populated into the set by\n // _updateVisibleSnapshot.\n const parentNode = this._tree.getNodeByCodecMessageId(parent);\n if (!parentNode) return true; // unknown parent: forward conservatively\n return this._lastVisibleNodeKeySet.has(nodeKey(parentNode));\n }\n\n private _visibleChanged(newNodes: ConversationNode<TProjection>[]): boolean {\n if (newNodes.length !== this._lastVisibleNodeKeys.length) return true;\n for (const [i, node] of newNodes.entries()) {\n if (nodeKey(node) !== this._lastVisibleNodeKeys[i]) return true;\n if (node.projection !== this._lastVisibleProjections[i]) return true;\n }\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a View that projects a paginated window over a Tree.\n * @param options - The tree, channel, codec, and logger to use.\n * @returns A new {@link DefaultView} instance.\n */\nexport const createView = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage>(\n options: ViewOptions<TInput, TOutput, TProjection, TMessage>,\n): DefaultView<TInput, TOutput, TProjection, TMessage> => new DefaultView(options);\n","/**\n * Core client-side session, parameterized by codec.\n *\n * Composes the conversation Tree to handle the full client-side lifecycle.\n * `connect()` subscribes to the Ably channel (which implicitly attaches it).\n * The same subscription, decoder, and channel are reused across runs.\n *\n * The client publishes user messages directly to the channel via the shared\n * codec encoder. It does not send HTTP: waking an agent is the application's\n * concern — it POSTs `run.toInvocation().toJSON()` to its own endpoint if and\n * when it wants one woken (the Vercel ChatTransport does this for useChat\n * parity). The agent locates the triggering input event by its `event-id`\n * header and publishes run lifecycle events (run-start, run-end) plus assistant\n * chunks, minting and stamping the invocation-id itself. The channel is the\n * durable session record; agents that weren't running at publish time can\n * resume by reading channel rewind.\n */\n\nimport * as Ably from 'ably';\n\nimport {\n EVENT_CANCEL,\n EVENT_RUN_END,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_ERROR_CODE,\n HEADER_ERROR_MESSAGE,\n HEADER_EVENT_ID,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_INVOCATION_ID,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_RUN_ID,\n HEADER_RUN_REASON,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport type { Logger } from '../../logger.js';\nimport { LogLevel, makeLogger } from '../../logger.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport { registerAgent } from '../agent.js';\nimport type { CodecInputEvent, CodecOutputEvent, Decoder, Encoder } from '../codec/types.js';\nimport { applyWireMessage } from './decode-fold.js';\nimport { buildTransportHeaders } from './headers.js';\nimport { Invocation } from './invocation.js';\nimport type { DefaultTree } from './tree.js';\nimport { createTree } from './tree.js';\nimport type { ActiveRun, ClientSession, ClientSessionOptions, RunEndReason, SendOptions, Tree, View } from './types.js';\nimport { createView, type DefaultView } from './view.js';\n\n/**\n * Returned from `on()` when the session is already closed — the subscription\n * is silently ignored since no further events will fire.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-function -- intentional no-op\nconst noopUnsubscribe = (): void => {};\n\n// ---------------------------------------------------------------------------\n// Internal state machine\n// ---------------------------------------------------------------------------\n\nenum ClientSessionState {\n READY = 'ready',\n CLOSED = 'closed',\n}\n\n// ---------------------------------------------------------------------------\n// Event map for the session's typed EventEmitter\n// ---------------------------------------------------------------------------\n\ninterface ClientSessionEventsMap {\n error: Ably.ErrorInfo;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CT1\nclass DefaultClientSession<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> implements ClientSession<TInput, TOutput, TProjection, TMessage> {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: ClientSessionOptions<TInput, TOutput, TProjection, TMessage>['codec'];\n private readonly _clientId: string | undefined;\n private readonly _logger: Logger;\n\n // Typed event emitter — the session emits only 'error'; all data events live on Tree/View\n private readonly _emitter: EventEmitter<ClientSessionEventsMap>;\n\n // Sub-components\n private readonly _tree: DefaultTree<TInput, TOutput, TProjection>;\n private readonly _view: DefaultView<TInput, TOutput, TProjection, TMessage>;\n private readonly _views = new Set<DefaultView<TInput, TOutput, TProjection, TMessage>>();\n private readonly _decoder: Decoder<TInput, TOutput>;\n /**\n * Shared encoder for the lifetime of the session. The client only ever\n * uses `publishInput` (input wire), so the encoder's stream tracker map\n * stays empty across the session. Closed once on session close.\n */\n private readonly _encoder: Encoder<TInput, TOutput>;\n\n // Spec: AIT-CT10, AIT-CT10a\n readonly tree: Tree<TOutput, TProjection>;\n readonly view: View<TInput, TMessage>;\n\n // Channel subscription is established lazily on connect()\n private _connectPromise: Promise<void> | undefined;\n private readonly _onMessage: (msg: Ably.InboundMessage) => void;\n\n private _state = ClientSessionState.READY;\n private _hasAttachedOnce: boolean;\n private readonly _onChannelStateChange: Ably.channelEventCallback;\n\n /**\n * Backing settlers for each in-flight run's `ActiveRun.runId` promise.\n * Resolved with the agent-minted run-id when the matching `ai-run-start`\n * (fresh send) or `ai-run-resume` (continuation) is observed; rejected if\n * the session closes first. There is no deadline —\n * `send()` resolves on publish and does not block on run-start.\n *\n * Keyed by the triggering input's codec-message-id — the handle the client\n * owns at send time, which the agent echoes back on run-start as\n * `input-codec-message-id`. This is uniform across fresh sends and\n * continuations (a continuation is itself an input event — tool-approval or\n * tool-result — with its own codec-message-id), so reconciliation never\n * depends on a client-minted run/invocation id.\n */\n private readonly _pendingRunStarts = new Map<\n string,\n { resolve: (runId: string) => void; reject: (e: Ably.ErrorInfo) => void }\n >();\n\n constructor(options: ClientSessionOptions<TInput, TOutput, TProjection, TMessage>) {\n // Spec: AIT-CT1a, AIT-CT1a2 — register this SDK on both the connection\n // (options.agents) and channel-attach (params.agent) paths. Idempotent\n // across sessions sharing one client.\n const channelOptions = registerAgent(options.client, options.codec);\n this._channel = options.client.channels.get(options.channelName, channelOptions);\n this._codec = options.codec;\n this._clientId = options.clientId;\n this._logger = (options.logger ?? makeLogger({ logLevel: LogLevel.Silent })).withContext({\n component: 'ClientSession',\n });\n\n this._emitter = new EventEmitter<ClientSessionEventsMap>(this._logger);\n this._hasAttachedOnce = this._channel.state === 'attached';\n\n // Compose sub-components\n this._tree = createTree<TInput, TOutput, TProjection>(this._codec, this._logger);\n this._view = createView<TInput, TOutput, TProjection, TMessage>({\n tree: this._tree,\n channel: this._channel,\n codec: this._codec,\n sendDelegate: this._internalSend.bind(this),\n logger: this._logger,\n onClose: () => this._views.delete(this._view),\n });\n this._decoder = this._codec.createDecoder();\n this._encoder = this._codec.createEncoder(\n this._channel,\n this._clientId === undefined ? undefined : { clientId: this._clientId },\n );\n\n this._views.add(this._view);\n\n // Public accessors (typed as narrow interfaces)\n this.tree = this._tree;\n this.view = this._view;\n\n // Seed tree with initial messages — the session assigns a codecMessageId\n // per seed message. Each seed becomes a run-less input node (no run-id —\n // the client never mints one); the parent chain mirrors the original seed\n // sequence (a user→user input chain the Tree threads kind-blind).\n if (options.messages) {\n let prevMsgId: string | undefined;\n for (const msg of options.messages) {\n const codecMessageId = crypto.randomUUID();\n const seedHeaders: Record<string, string> = {\n [HEADER_CODEC_MESSAGE_ID]: codecMessageId,\n [HEADER_ROLE]: 'user',\n };\n if (prevMsgId) seedHeaders[HEADER_PARENT] = prevMsgId;\n this._tree.applyMessage({ inputs: [this._codec.createUserMessage(msg)], outputs: [] }, seedHeaders);\n prevMsgId = codecMessageId;\n }\n }\n\n // Spec: AIT-CT2\n // Listener function reference — bound now so it can be unsubscribed on close.\n this._onMessage = (ablyMessage: Ably.InboundMessage) => {\n this._handleMessage(ablyMessage);\n };\n\n // Listen for channel state changes that break message continuity.\n // _hasAttachedOnce is seeded from the channel's current state so that\n // pre-attached channels are handled correctly. It distinguishes the\n // initial attach (expected) from a genuine discontinuity.\n this._onChannelStateChange = (stateChange: Ably.ChannelStateChange) => {\n this._handleChannelStateChange(stateChange);\n };\n this._channel.on(this._onChannelStateChange);\n }\n\n // ---------------------------------------------------------------------------\n // Public connection API\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT2\n // eslint-disable-next-line @typescript-eslint/promise-function-async -- preserve reference equality across calls\n connect(): Promise<void> {\n if (this._state === ClientSessionState.CLOSED) {\n return Promise.reject(new Ably.ErrorInfo('unable to connect; session is closed', ErrorCode.SessionClosed, 400));\n }\n if (this._connectPromise) return this._connectPromise;\n\n this._logger.trace('DefaultClientSession.connect();');\n // Subscribe before attach (RTL7g) — subscribe implicitly attaches the channel.\n this._connectPromise = this._channel.subscribe(this._onMessage).then(\n () => {\n this._logger.debug('DefaultClientSession.connect(); subscribed and attached');\n },\n (error: unknown) => {\n const errInfo = new Ably.ErrorInfo(\n `unable to subscribe to channel; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n error instanceof Ably.ErrorInfo ? error : undefined,\n );\n this._logger.error('DefaultClientSession.connect(); subscribe failed');\n this._emitter.emit('error', errInfo);\n throw errInfo;\n },\n );\n return this._connectPromise;\n }\n\n private async _requireConnected(method: string): Promise<void> {\n if (!this._connectPromise) {\n throw new Ably.ErrorInfo(\n `unable to ${method}; connect() must be called before ${method}()`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n return this._connectPromise;\n }\n\n // ---------------------------------------------------------------------------\n // Message subscription handler\n // ---------------------------------------------------------------------------\n\n private _handleMessage(ablyMessage: Ably.InboundMessage): void {\n if (this._state === ClientSessionState.CLOSED) return;\n\n try {\n // Spec: AIT-CT16a\n // Live-only: surface an agent error carried on a run-end BEFORE applying\n // it, preserving the original 'error'-before-tree-'run' emit ordering.\n // Consumers that expose a per-run stream (e.g. the Vercel ChatTransport)\n // error their stream off this event. The agent only publishes run-end\n // after run-start, so no pending-run-start tracker is outstanding.\n if (ablyMessage.name === EVENT_RUN_END) {\n const headers = getTransportHeaders(ablyMessage);\n // CAST: agent always writes a valid RunEndReason; default to 'complete' for robustness\n const reason = (headers[HEADER_RUN_REASON] ?? 'complete') as RunEndReason;\n if (reason === 'error') {\n const codeRaw = headers[HEADER_ERROR_CODE];\n const parsedCode = codeRaw === undefined ? Number.NaN : Number(codeRaw);\n const code = Number.isFinite(parsedCode) ? parsedCode : ErrorCode.SessionSubscriptionError;\n const message = headers[HEADER_ERROR_MESSAGE] ?? 'agent reported an error';\n const statusCode = code >= 10000 && code < 60000 ? Math.floor(code / 100) : 500;\n const errInfo = new Ably.ErrorInfo(message, code, statusCode);\n this._logger.error('ClientSession._handleMessage(); agent error received', {\n runId: headers[HEADER_RUN_ID],\n invocationId: headers[HEADER_INVOCATION_ID],\n code,\n });\n this._emitter.emit('error', errInfo);\n }\n }\n\n // Reconstruct the tree via the shared decode-fold engine — the same path\n // the View's history replay uses, so the live loop can't drift from it.\n const event = applyWireMessage(this._tree, this._decoder, ablyMessage);\n\n // Live-only: resolve the pending `runId` promise on a fresh run-start or\n // a continuation run-resume. Key by the echoed `input-codec-message-id`\n // — the mirror of the arming key on `_pendingRunStarts` (see that\n // field's JSDoc). Every send carries at least one input, so the agent\n // always echoes it.\n if (event && (event.type === 'start' || event.type === 'resume')) {\n const startedKey = getTransportHeaders(ablyMessage)[HEADER_INPUT_CODEC_MESSAGE_ID];\n if (startedKey !== undefined) {\n const pending = this._pendingRunStarts.get(startedKey);\n if (pending) {\n this._pendingRunStarts.delete(startedKey);\n // Resolve the run handle's `runId` promise with the agent-minted id.\n pending.resolve(event.runId);\n }\n }\n }\n\n // Emit ably-message AFTER the apply so View subscribers can find the\n // owning node in `_lastVisibleNodeKeySet` (keyed by run-id for reply runs\n // and codec-message-id for inputs), which is refreshed by the tree\n // 'update' events the apply triggers.\n this._tree.emitAblyMessage(ablyMessage);\n } catch (error) {\n const cause = error instanceof Ably.ErrorInfo ? error : undefined;\n this._emitter.emit(\n 'error',\n new Ably.ErrorInfo(\n `unable to process channel message; ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n cause,\n ),\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // Channel state change handler\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT19, AIT-CT19a\n private _handleChannelStateChange(stateChange: Ably.ChannelStateChange): void {\n if (this._state === ClientSessionState.CLOSED) return;\n\n const { current, resumed } = stateChange;\n\n // Track the initial attach so we don't treat it as a discontinuity\n if (current === 'attached' && !this._hasAttachedOnce) {\n this._hasAttachedOnce = true;\n return;\n }\n\n // Continuity-breaking states:\n // - FAILED, SUSPENDED, DETACHED: no more messages expected (or gap)\n // - ATTACHED with resumed: false (UPDATE): messages were lost\n const continuityLost =\n current === 'failed' || current === 'suspended' || current === 'detached' || (current === 'attached' && !resumed);\n\n if (!continuityLost) return;\n\n this._logger.error('ClientSession._handleChannelStateChange(); channel continuity lost', {\n current,\n resumed,\n previous: stateChange.previous,\n });\n\n const err = new Ably.ErrorInfo(\n `unable to deliver events; channel continuity lost (${current}${current === 'attached' ? ', resumed: false' : ''})`,\n ErrorCode.ChannelContinuityLost,\n 500,\n stateChange.reason,\n );\n\n // Surface the loss via the session `error` event. Consumers that expose a\n // per-run stream (e.g. the Vercel ChatTransport) error their stream off\n // this event; observer-run state lives entirely in the Tree's projection\n // and stays consistent regardless of continuity loss.\n this._emitter.emit('error', err);\n }\n\n // ---------------------------------------------------------------------------\n // Cancel helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Tear down local state for a send whose channel publish failed.\n * Idempotent.\n * @param codecMessageIds - The codec-message-ids of the failed send's\n * optimistic input nodes (the client mints no run-id, so the optimistic\n * inserts are keyed by their codec-message-ids).\n */\n private _cleanupFailedSend(codecMessageIds: string[]): void {\n for (const codecMessageId of codecMessageIds) {\n // Drop the optimistic input node only if the publish never produced a\n // server-assigned serial (i.e. nothing live observed it). A server-acked\n // node is part of the canonical channel state and must stay; the View /\n // observers already see it. A fresh send's optimistic inserts are input\n // nodes (keyed by codec-message-id).\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (node?.kind === 'input' && node.serial === undefined) {\n // An input node's key is its codec-message-id, so delete by it directly.\n this._tree.delete(node.codecMessageId);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT10b\n createView(): View<TInput, TMessage> {\n if (this._state === ClientSessionState.CLOSED) {\n throw new Ably.ErrorInfo('unable to create view; session is closed', ErrorCode.SessionClosed, 400);\n }\n this._logger.trace('DefaultClientSession.createView();');\n const view = createView<TInput, TOutput, TProjection, TMessage>({\n tree: this._tree,\n channel: this._channel,\n codec: this._codec,\n sendDelegate: this._internalSend.bind(this),\n logger: this._logger,\n onClose: () => this._views.delete(view),\n });\n this._views.add(view);\n return view;\n }\n\n // Spec: AIT-CT3, AIT-CT4\n private async _internalSend(\n input: TInput[],\n sendOptions: SendOptions | undefined,\n parentCodecMessageId: string | undefined,\n ): Promise<ActiveRun> {\n if (this._state === ClientSessionState.CLOSED) {\n throw new Ably.ErrorInfo('unable to send; session is closed', ErrorCode.SessionClosed, 400);\n }\n await this._requireConnected('send');\n // CAST: re-check after await — close() may have been called while waiting for connect.\n // TypeScript's control flow narrows _state after the first check, but the\n // await yields and close() can mutate _state concurrently.\n if ((this._state as ClientSessionState) === ClientSessionState.CLOSED) {\n throw new Ably.ErrorInfo('unable to send; session is closed', ErrorCode.SessionClosed, 400);\n }\n\n // Spec: AIT-CT20\n const state = this._channel.state;\n if (state !== 'attached' && state !== 'attaching') {\n throw new Ably.ErrorInfo(`unable to send; channel is ${state}`, ErrorCode.ChannelNotReady, 400);\n }\n\n this._logger.trace('ClientSession._internalSend();');\n\n const isContinuation = sendOptions?.runId !== undefined;\n\n // The agent mints run-ids, not the client. A fresh send carries no run-id\n // (the agent mints it and echoes it on run-start); only a continuation\n // reuses the existing run-id the caller passed.\n const runId = sendOptions?.runId;\n\n // Spec: AIT-CT3d\n // Auto-compute parent from the visible branch tail when not explicitly\n // provided. The View pre-resolves the codec-message-id of the last visible message\n // since the session is codec-agnostic and can't extract it from TMessage.\n let autoParent: string | undefined;\n if (sendOptions?.parent === undefined && !sendOptions?.forkOf) {\n autoParent = parentCodecMessageId;\n }\n\n const codecMessageIds = new Set<string>();\n interface ItemState {\n input: TInput;\n codecMessageId: string;\n inputEventId: string;\n headers: Record<string, string>;\n /** Inputs that reference an existing codec-message without contributing fresh local content (regenerate, tool resolutions) are wire-only — no optimistic projection fold. Fresh user-messages always fold, even when they pin their own codecMessageId. */\n isWireOnly: boolean;\n }\n const items: ItemState[] = [];\n\n // Per-input wire prep: read routing fields off the input directly, then\n // mint per-event ids and build transport headers. Regenerate inputs are\n // wire-only (no optimistic fold); other inputs fold into the projection\n // optimistically.\n for (const entry of input) {\n const inputEventId = crypto.randomUUID();\n // Use the input's `codecMessageId` when set (e.g. tool resolution\n // targeting the prior assistant); otherwise mint a fresh id.\n const codecMessageId = entry.codecMessageId ?? crypto.randomUUID();\n codecMessageIds.add(codecMessageId);\n\n // Inputs that reference an existing message (regenerate, tool\n // resolutions targeting an assistant) are wire-only — no optimistic\n // fold needed because either the receiving content doesn't\n // materialise on this side (regenerate) or the target already exists\n // and will be amended when the wire echoes back.\n //\n // A fresh `user-message` is never wire-only, even on the rare path\n // where it carries an explicit `codecMessageId`: it is new content that\n // must fold into the local projection immediately. Excluding it here\n // keeps the optimistic user bubble from depending on the channel\n // round-trip. (The session mints the codec-message-id for fresh user\n // messages; the caller's `message.id` is preserved but never used as\n // the correlation key.)\n const isWireOnly =\n entry.kind !== 'user-message' && (entry.kind === 'regenerate' || entry.codecMessageId !== undefined);\n\n // The input's own routing fields override the auto-parent /\n // sendOptions defaults. For regenerate inputs, `target` becomes the\n // `msg-regenerate` wire header. The fork anchor comes from\n // `sendOptions.forkOf` (set by `View.edit`). The transport reads\n // these directly without runtime classification.\n const parent = entry.parent ?? (sendOptions?.parent === undefined ? autoParent : sendOptions.parent);\n const forkOf = sendOptions?.forkOf;\n const regenerates = entry.kind === 'regenerate' ? entry.target : undefined;\n\n const headers = buildTransportHeaders({\n role: 'user',\n runId,\n codecMessageId,\n runClientId: this._clientId,\n ...(parent !== undefined && { parent }),\n ...(forkOf !== undefined && { forkOf }),\n ...(regenerates !== undefined && { regenerates }),\n inputEventId,\n });\n\n // Spec: AIT-CT3c — optimistic fold for non-wire-only inputs.\n if (!isWireOnly) {\n this._tree.applyMessage({ inputs: [entry], outputs: [] }, headers);\n }\n\n items.push({ input: entry, codecMessageId, inputEventId, headers, isWireOnly });\n\n // Spec: AIT-CT3e — chain subsequent inputs off the previous one when\n // auto-parenting is in effect.\n if (!isWireOnly && sendOptions?.parent === undefined && !sendOptions?.forkOf && entry.parent === undefined) {\n autoParent = codecMessageId;\n }\n }\n\n // The trigger event is the last input — the one the agent looks up on the\n // channel via `event-id`, surfaced on `ActiveRun` (and via `toInvocation()`)\n // so the application can point an invocation at it. Its codec-message-id is\n // the handle the client owns at send time; the agent echoes it back on\n // run-start as `input-codec-message-id`, and it keys the run-start tracker.\n const triggerItem = items.at(-1);\n if (triggerItem === undefined) {\n // Every send must carry at least one input — only new input starts or\n // continues a run. The loop above produced no items, so nothing was\n // published or folded optimistically.\n throw new Ably.ErrorInfo(\n 'unable to send; inputs array is empty (include at least one input)',\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const triggerInputEventId = triggerItem.inputEventId;\n const startedKey = triggerItem.codecMessageId;\n\n // Arm the run-start tracker backing the returned `ActiveRun.runId` promise.\n // The run-start handler resolves it with the agent-minted run-id when this\n // send's `ai-run-start` is observed; close() rejects it on teardown. No\n // deadline — `send()` resolves on publish; callers bound the wait by racing\n // `run.runId` against their own timeout.\n //\n // Key on the arming side mirrors the resolve side — see `_pendingRunStarts`\n // for the full keying invariant. The executor runs synchronously, so the\n // tracker entry is registered before `new Promise` returns.\n const runIdPromise = new Promise<string>((resolve, reject) => {\n this._pendingRunStarts.set(startedKey, { resolve, reject });\n });\n // Suppress unhandled-rejection warnings for callers that never await\n // `run.runId`; the caller still observes the rejection if it does await.\n runIdPromise.catch(() => {\n /* observed via run.runId, if at all */\n });\n\n // Publish each input in original order via the shared encoder. The\n // codec routes user-message inputs into a per-part discrete batch and\n // tool-resolution / regenerate inputs into a single discrete write —\n // all on the `ai-input` wire.\n const publishPromise = (async () => {\n try {\n for (const item of items) {\n await this._encoder.publishInput(item.input, {\n extras: { headers: item.headers },\n messageId: item.codecMessageId,\n ...(this._clientId !== undefined && { clientId: this._clientId }),\n });\n }\n } catch (error) {\n const cause = error instanceof Ably.ErrorInfo ? error : undefined;\n const isPermission = cause?.statusCode === 401 || cause?.statusCode === 403;\n const err = new Ably.ErrorInfo(\n isPermission\n ? `unable to publish events; missing publish capability on the channel`\n : `unable to publish events; ${error instanceof Error ? error.message : String(error)}`,\n isPermission ? ErrorCode.InsufficientCapability : ErrorCode.SessionSendFailed,\n isPermission ? 401 : 500,\n cause,\n );\n this._emitter.emit('error', err);\n // The input never reached the channel — there is no run to wait on.\n // Drop the run-start tracker so close() doesn't later reject an orphan.\n this._pendingRunStarts.delete(startedKey);\n // Continuations didn't insert optimistic nodes, so there is nothing to\n // clear for them — only a fresh send's optimistic input nodes need\n // removing, keyed by their codec-message-ids (the client mints no runId).\n if (!isContinuation) this._cleanupFailedSend([...codecMessageIds]);\n throw err;\n }\n })();\n\n // `send()` resolves once the input is published. The core never sends\n // HTTP — waking an agent is the application's concern. Callers POST\n // `run.toInvocation().toJSON()` to their endpoint if they want one woken,\n // and await `run.runId` if they need to know it was picked up.\n await publishPromise;\n\n return {\n inputCodecMessageId: startedKey,\n runId: runIdPromise,\n inputEventId: triggerInputEventId,\n // The agent mints the run-id, so a fresh run has none until run-start.\n // Cancel synchronously by the triggering input's codec-message-id (the\n // handle the client owns at send time, = `inputCodecMessageId`): the\n // agent resolves it to the run once its input-event lookup completes, and\n // buffers a cancel that arrives before then so an early cancel is honoured\n // rather than dropped. A continuation additionally carries its known\n // run-id so the agent can match the run directly.\n cancel: async () => {\n await this._publishCancel({\n inputCodecMessageId: startedKey,\n ...(runId !== undefined && { runId }),\n });\n },\n optimisticCodecMessageIds: [...codecMessageIds],\n toInvocation: () =>\n // The invocation body carries no run-id: run identity lives on the\n // channel (the agent mints a fresh run-id, or reads a continuation's\n // from the triggering input event, which carries the reused run-id).\n Invocation.fromJSON({\n inputEventId: triggerInputEventId,\n sessionName: this._channel.name,\n }),\n };\n }\n\n // Spec: AIT-CT7, AIT-CT7a\n async cancel(runId: string): Promise<void> {\n return this._publishCancel({ runId });\n }\n\n /**\n * Publish an `ai-cancel` signal. The agent resolves the target run by\n * whichever identifier is present:\n *\n * - `runId` — a continuation, whose run-id the caller already knows.\n * - `inputCodecMessageId` — a fresh send, whose run-id the agent mints at\n * run-start. The client can only key the cancel by the triggering input's\n * codec-message-id (the `ActiveRun.inputCodecMessageId`) it owns at send\n * time; the agent resolves it to the run once its input-event lookup\n * completes, buffering a cancel that arrives before then.\n *\n * Both may be present (a continuation knows its run-id AND published an\n * input). An `event-id` is always stamped so channel rewind redelivers the\n * cancel to a per-request / serverless agent that attaches after it was\n * published.\n *\n * Publishing the cancel signal is all the core does. The consumer-facing\n * stream (if any) lives in the layer that built it — e.g. the Vercel\n * ChatTransport closes its stream on cancel — and the Tree's RunNode is left\n * intact so late agent events (a cancel append, a trailing\n * `status: cancelled`) still fold into the Run's projection.\n * @param target - The run identifier(s) to cancel. At least one of `runId` /\n * `inputCodecMessageId` must be set.\n * @param target.runId - The run-id to cancel (continuations).\n * @param target.inputCodecMessageId - The triggering input's\n * codec-message-id to cancel (fresh sends, before run-start).\n */\n private async _publishCancel(target: { runId?: string; inputCodecMessageId?: string }): Promise<void> {\n if (this._state === ClientSessionState.CLOSED) return;\n await this._requireConnected('cancel');\n // CAST: re-check after await — close() may have been called while waiting for connect.\n if ((this._state as ClientSessionState) === ClientSessionState.CLOSED) return;\n this._logger.debug('ClientSession._publishCancel();', {\n runId: target.runId,\n inputCodecMessageId: target.inputCodecMessageId,\n });\n\n const headers: Record<string, string> = {\n // Stamp a per-cancel event-id so channel rewind redelivers this cancel\n // to an agent that attaches after it was published.\n [HEADER_EVENT_ID]: crypto.randomUUID(),\n };\n if (target.runId !== undefined) headers[HEADER_RUN_ID] = target.runId;\n if (target.inputCodecMessageId !== undefined) headers[HEADER_INPUT_CODEC_MESSAGE_ID] = target.inputCodecMessageId;\n\n await this._channel.publish({\n name: EVENT_CANCEL,\n extras: { ai: { transport: headers } },\n });\n }\n\n // Spec: AIT-CT8, AIT-CT8c, AIT-CT8d\n on(event: 'error', handler: (error: Ably.ErrorInfo) => void): () => void {\n if (this._state === ClientSessionState.CLOSED) return noopUnsubscribe;\n // CAST: the overload signature enforces the correct handler type.\n const cb = handler;\n this._emitter.on(event, cb);\n return () => {\n this._emitter.off(event, cb);\n };\n }\n\n // Spec: AIT-CT12, AIT-CT12b, AIT-CT10c\n async close(): Promise<void> {\n if (this._state === ClientSessionState.CLOSED) return;\n this._state = ClientSessionState.CLOSED;\n this._logger.info('ClientSession.close();');\n\n if (this._connectPromise) {\n this._channel.unsubscribe(this._onMessage);\n }\n this._channel.off(this._onChannelStateChange);\n\n this._emitter.off();\n for (const v of this._views) v.close();\n this._views.clear();\n // Reject any in-flight `run.runId` promises so callers awaiting run-start\n // settle rather than hang.\n if (this._pendingRunStarts.size > 0) {\n const closedErr = new Ably.ErrorInfo('unable to await run-start; session closed', ErrorCode.SessionClosed, 400);\n for (const pending of this._pendingRunStarts.values()) {\n pending.reject(closedErr);\n }\n this._pendingRunStarts.clear();\n }\n\n // Best-effort encoder close — flushes any pending stream operations.\n // The client only uses the discrete input path (publishInput), so this is\n // typically a no-op, but it releases any internal resources cleanly.\n try {\n await this._encoder.close();\n } catch {\n // Swallow: encoder close is best-effort during teardown\n }\n\n // Detach the channel this session attached. connect() subscribes (which\n // implicitly attaches), so we only detach when connect() ran. Best-effort:\n // a detach failure (e.g. the channel is already FAILED) must not throw out\n // of close().\n if (this._connectPromise) {\n try {\n await this._channel.detach();\n } catch (error) {\n // Swallowed (see above): a detach failure must not throw out of\n // close(). Logged at debug for observability.\n this._logger.debug('ClientSession.close(); channel detach failed', { error });\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a client-side session that manages conversation state over an Ably channel.\n *\n * The caller owns the client's lifecycle; the session owns its channel.\n * The session is created in a not-yet-connected state — callers must\n * `await session.connect()` before `send`, `regenerate`, `edit`, `update`,\n * or `cancel`.\n * @param options - Configuration for the client session.\n * @returns A new {@link ClientSession} instance.\n */\nexport const createClientSession = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n options: ClientSessionOptions<TInput, TOutput, TProjection, TMessage>,\n): ClientSession<TInput, TOutput, TProjection, TMessage> => new DefaultClientSession(options);\n","/**\n * Vercel AI SDK transport wrappers that pre-bind the UIMessageCodec.\n *\n * These are convenience factories so consumers don't need to pass the codec\n * explicitly when using the Vercel AI SDK integration.\n *\n * ```ts\n * import { createClientSession } from '@ably/ai-transport/vercel';\n *\n * const session = createClientSession({ client, channelName: 'ai:demo' });\n * await session.connect();\n * ```\n */\n\n// Chat transport adapter\nexport type { ChatTransport, ChatTransportOptions, SendMessagesRequestContext } from './chat-transport.js';\nexport { createChatTransport, DEFAULT_VERCEL_API } from './chat-transport.js';\n\nimport type * as AI from 'ai';\n\nimport { createAgentSession as createCoreAgentSession } from '../../core/transport/agent-session.js';\nimport { createClientSession as createCoreClientSession } from '../../core/transport/client-session.js';\nimport type {\n AgentSession,\n AgentSessionOptions,\n ClientSession,\n ClientSessionOptions,\n} from '../../core/transport/types.js';\nimport { UIMessageCodec, type VercelInput, type VercelOutput, type VercelProjection } from '../codec/index.js';\n\n/** Core client session options with Vercel AI SDK types pre-applied. */\ntype CoreClientOpts = ClientSessionOptions<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;\n\n/** Options for creating a Vercel client session. Same as core options but without the codec field. */\nexport type VercelClientSessionOptions = Omit<CoreClientOpts, 'codec'>;\n\n/** Options for creating a Vercel agent session. Same as core options but without the codec field. */\nexport type VercelAgentSessionOptions = Omit<\n AgentSessionOptions<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>,\n 'codec'\n>;\n\n/**\n * Create a client-side session pre-configured with the Vercel AI SDK codec.\n *\n * Equivalent to calling the core `createClientSession` with `codec: UIMessageCodec`.\n * The core session is a pure Ably-channel transport — it never sends HTTP.\n * To wake a serverless agent over HTTP, POST `run.toInvocation().toJSON()`\n * yourself, or use `createChatTransport` (which does it for useChat parity).\n * @param options - Configuration for the client session (codec is provided automatically).\n * @returns A new {@link ClientSession} for Vercel AI SDK UIMessage/UIMessageChunk types.\n */\nexport const createClientSession = (\n options: VercelClientSessionOptions,\n): ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage> =>\n createCoreClientSession({ ...options, codec: UIMessageCodec });\n\n/**\n * Create an agent (server-side) session pre-configured with the Vercel AI SDK codec.\n *\n * Equivalent to calling the core `createAgentSession` with `codec: UIMessageCodec`.\n * @param options - Configuration for the agent session (codec is provided automatically).\n * @returns A new {@link AgentSession} for Vercel AI SDK UIMessage/UIMessageChunk types.\n */\nexport const createAgentSession = (\n options: VercelAgentSessionOptions,\n): AgentSession<VercelOutput, VercelProjection, AI.UIMessage> =>\n createCoreAgentSession({ ...options, codec: UIMessageCodec });\n","import type * as AI from 'ai';\n\nimport type { RunEndReason, StreamResult } from '../core/transport/types.js';\n\n/**\n * Derive the outcome for a Vercel `streamText` response that was piped through\n * `Run.pipe`: either a terminal {@link RunEndReason} the caller passes to\n * `Run.end`, or the sentinel `'suspend'` telling the caller to call\n * `Run.suspend` instead. Preserves transport-level outcomes (`'cancelled'`,\n * `'error'`) from the pipe result; when the pipe completed naturally, awaits\n * Vercel's `finishReason` and returns `'suspend'` for `'tool-calls'` (the LLM\n * requested tools the SDK did not auto-execute, so the run should suspend\n * rather than end), or `'complete'` otherwise.\n *\n * Tolerates `finishReason` rejection. Vercel AI SDK v6 rejects\n * `streamText().finishReason` with the abort signal's reason when the stream\n * is aborted before any step completes, and rejects with\n * `NoOutputGeneratedError` when the model produced nothing at all. Without\n * this guard the rejection would bubble out of the route handler's `after()`\n * block, skip the developer's `Run.end(...)` call, and leave the run with no\n * `ai-run-end` event on the channel — so observers' UIs stay stuck on\n * `streaming` indefinitely.\n *\n * Saves callers from interpreting Vercel domain semantics inline at the end\n * of every route handler.\n * @param pipeResult - The result returned by `Run.pipe`.\n * @param finishReason - The `finishReason` promise from a `streamText` result.\n * @returns `'suspend'` when the run should suspend awaiting tool input, or the\n * {@link RunEndReason} to pass to `Run.end` otherwise.\n */\nexport const vercelRunOutcome = async (\n pipeResult: StreamResult,\n finishReason: PromiseLike<AI.FinishReason>,\n): Promise<RunEndReason | 'suspend'> => {\n if (pipeResult.reason !== 'complete') {\n // Vercel's `result.finishReason` getter creates the underlying Promise\n // eagerly, before the caller hands it to us. When `streamText` is\n // aborted before any step completes, Vercel rejects that Promise with\n // the abort signal's reason — typically a DOMException whose\n // `.message` is a read-only getter. Returning early without ever\n // attaching a handler lets Node report it as an unhandled rejection;\n // Next.js' dev bundler then tries to mutate `.message` for logging\n // and crashes with a confusing TypeError. Attach a silent handler so\n // the rejection is observed and discarded — the transport-level\n // `pipeResult.reason` is already what we return.\n Promise.resolve(finishReason).catch(() => {\n /* intentionally discarded; reason already known from pipeResult */\n });\n return pipeResult.reason;\n }\n try {\n const finish = await finishReason;\n return finish === 'tool-calls' ? 'suspend' : 'complete';\n } catch (error) {\n // Abort-shaped rejections are surfaced from streamText when the run was\n // cancelled before any step finished — treat the run as cancelled so the\n // observable lifecycle matches the cancel that triggered it. Everything\n // else is a real error (e.g. NoOutputGeneratedError, network blow-ups);\n // surface it as such so the developer sees the failure rather than a\n // silent cancel.\n return _isAbortLikeError(error) ? 'cancelled' : 'error';\n }\n};\n\n/**\n * Heuristic for \"this error came from an AbortSignal aborting\".\n * Covers `DOMException` aborts (browser / Node 20+ `streamText`),\n * plain `Error` objects whose `name` is `'AbortError'`, and anything\n * else carrying that conventional name. Avoids importing\n * `@ai-sdk/provider-utils` just for `isAbortError`.\n * @param error - The error to test.\n * @returns `true` if the error looks like an abort.\n */\nconst _isAbortLikeError = (error: unknown): boolean => {\n if (typeof error !== 'object' || error === null) return false;\n const name = (error as { name?: unknown }).name;\n return name === 'AbortError';\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ably-ai-transport-vercel.js","names":[],"sources":["../../src/constants.ts","../../src/errors.ts","../../src/utils.ts","../../src/core/codec/encoder.ts","../../src/core/codec/decoder.ts","../../src/core/codec/lifecycle-tracker.ts","../../src/core/codec/fields.ts","../../src/core/codec/field-bag.ts","../../src/core/codec/input-descriptor-decoder.ts","../../src/core/codec/input-descriptor-encoder.ts","../../src/core/codec/input-descriptors.ts","../../src/core/codec/output-descriptor-decoder.ts","../../src/core/codec/output-descriptor-encoder.ts","../../src/core/codec/output-descriptors.ts","../../src/core/codec/well-known-inputs.ts","../../src/core/codec/define-codec.ts","../../src/vercel/codec/fields.ts","../../src/vercel/codec/decode-lifecycle.ts","../../src/vercel/codec/wire-data.ts","../../src/vercel/codec/inputs.ts","../../src/vercel/codec/outputs.ts","../../src/vercel/codec/reducer-state.ts","../../src/vercel/codec/fold-content.ts","../../src/vercel/codec/fold-data.ts","../../src/vercel/codec/tool-transitions.ts","../../src/vercel/codec/fold-input.ts","../../src/vercel/codec/fold-lifecycle.ts","../../src/vercel/codec/fold-text.ts","../../src/vercel/codec/fold-tool-input.ts","../../src/vercel/codec/fold-tool-output.ts","../../src/vercel/codec/reducer.ts","../../src/vercel/codec/index.ts","../../src/event-emitter.ts","../../src/logger.ts","../../src/vercel/tool-part.ts","../../src/vercel/transport/run-output-stream.ts","../../src/vercel/transport/chat-transport.ts","../../src/version.ts","../../src/core/agent.ts","../../src/core/channel-options.ts","../../src/core/transport/load-history-pages.ts","../../src/core/transport/agent-view.ts","../../src/core/transport/headers.ts","../../src/core/transport/decode-fold.ts","../../src/core/transport/internal/bounded-map.ts","../../src/core/transport/pipe-stream.ts","../../src/core/transport/run-manager.ts","../../src/core/transport/session-support.ts","../../src/core/codec/codec-event.ts","../../src/core/transport/wire-log.ts","../../src/core/transport/tree.ts","../../src/core/transport/agent-session.ts","../../src/core/transport/invocation.ts","../../src/core/transport/load-history.ts","../../src/core/transport/view.ts","../../src/core/transport/client-session.ts","../../src/vercel/transport/index.ts","../../src/vercel/run-end-reason.ts"],"sourcesContent":["/**\n * Shared constants used by both codec and transport layers.\n *\n * Header constants define the transport wire header names. Message and event\n * name constants define the session lifecycle signals on the channel.\n *\n * These live at the top level (not in codec/ or transport/) because both\n * layers need them — the codec core reads/writes stream and status headers,\n * while the transport layer reads/writes run, cancel, and role headers.\n */\n\n// ---------------------------------------------------------------------------\n// Stream headers (used by codec encoder/decoder core)\n// ---------------------------------------------------------------------------\n\n/** Header: whether this Ably message uses streaming (message appends) or is discrete. Always \"true\" or \"false\". */\nexport const HEADER_STREAM = 'stream';\n\n/** Header: lifecycle status of a streamed message. Only set when stream is \"true\". One of \"streaming\", \"complete\", or \"cancelled\". */\nexport const HEADER_STATUS = 'status';\n\n/** Header: stream identity. Set by the encoder on every streamed message; read by the decoder to correlate streams. */\nexport const HEADER_STREAM_ID = 'stream-id';\n\n/** Header: marks a message as a discrete message part (from writeMessages). Set by publishDiscreteBatch; not set on lifecycle events from publishDiscrete. */\nexport const HEADER_DISCRETE = 'discrete';\n\n// ---------------------------------------------------------------------------\n// Identity headers (used by transport for run correlation)\n// ---------------------------------------------------------------------------\n\n/** Header: run correlation ID. Set on every agent-published message and on continuation client inputs, but omitted from the originating fresh client input (the agent mints the run-id at run-start). */\nexport const HEADER_RUN_ID = 'run-id';\n\n/** Header: invocation correlation ID; identifies a specific invocation under a run. Agent-minted and stamped by the agent on every event it publishes for the invocation — run lifecycle (run-start/resume/suspend/end) and assistant outputs. Never set by the client on its input. */\nexport const HEADER_INVOCATION_ID = 'invocation-id';\n\n/**\n * Header: per-event identifier stamped by the client on every\n * client-published event in a send — user-message events AND amend\n * events (tool-approval responses, client tool outputs). Distinct from\n * `codec-message-id` so it survives edits/retries that reuse the same\n * codec-message-id, and so amend events that target an existing message can\n * carry their own per-send identity. The invocation body lists every\n * inputEventId the agent must observe on the channel before starting LLM\n * work — see `Run.start()`'s input-event lookup.\n */\nexport const HEADER_EVENT_ID = 'event-id';\n\n/** Header: message identity. Assigned per message (user or assistant). Used for optimistic reconciliation on the client. */\nexport const HEADER_CODEC_MESSAGE_ID = 'codec-message-id';\n\n/** Header: clientId of the user who initiated the run. Stamped by the client on its user input and re-stamped by the agent on the run's lifecycle and stream messages. */\nexport const HEADER_RUN_CLIENT_ID = 'run-client-id';\n\n/**\n * Header: clientId of the input event (the `ai-input`) that drove the\n * current invocation. The agent reads the publisher's Ably-level `clientId`\n * from the triggering input event on the channel and re-stamps it as\n * `input-client-id` on every event it publishes for that invocation\n * (run lifecycle and assistant outputs). May differ from\n * `run-client-id` on continuation invocations driven by an input\n * from a non-owner (e.g. a tool-result publish from a different client).\n * Not stamped on `ai-input` events themselves — the wire publisher's\n * Ably `clientId` already conveys that.\n */\nexport const HEADER_INPUT_CLIENT_ID = 'input-client-id';\n\n/** Header: message role (e.g. \"user\", \"assistant\"). */\nexport const HEADER_ROLE = 'role';\n\n// ---------------------------------------------------------------------------\n// Fork / branching headers\n// ---------------------------------------------------------------------------\n\n/** Header: the codec-message-id of the immediately preceding message in this branch. */\nexport const HEADER_PARENT = 'parent';\n\n/** Header: the codec-message-id of the message this one replaces (creates a fork). */\nexport const HEADER_FORK_OF = 'fork-of';\n\n/**\n * Header: the codec-message-id of the assistant message this run regenerates.\n *\n * Stamped on the regenerate wire (and echoed on `run-start`) when the\n * client requested a regeneration. A regenerate run parents at the SAME input\n * node as the reply it regenerates, so it joins that input's reply runs as a\n * same-parent sibling (no fork-of). The View consults this header to resolve\n * the message-level sibling group and to drop the regenerated message from\n * earlier Runs in the visible chain (Spec: AIT-CT13d).\n */\nexport const HEADER_MSG_REGENERATE = 'msg-regenerate';\n\n// ---------------------------------------------------------------------------\n// Run lifecycle headers\n// ---------------------------------------------------------------------------\n\n/** Header: reason a run ended (on ai-run-end messages). */\nexport const HEADER_RUN_REASON = 'run-reason';\n\n/**\n * Header: the `codec-message-id` of the input event that triggered the run.\n * The triggering input is the one whose `event-id` matches the invocation's\n * `inputEventId` (the last input of the originating send). The agent\n * re-stamps it on every event it publishes for the invocation (run\n * lifecycle + assistant outputs), mirroring `input-client-id`. This is the\n * codec-message-id the client owns at send time, so it lets the client\n * correlate any of those events back to the originating input without\n * depending on a client-minted run-id or invocation-id.\n */\nexport const HEADER_INPUT_CODEC_MESSAGE_ID = 'input-codec-message-id';\n\n// ---------------------------------------------------------------------------\n// Run-end error headers (set on `ai-run-end` when `run-reason: error`)\n// ---------------------------------------------------------------------------\n\n/** Header: numeric error code accompanying an `ai-run-end` with reason `error`. */\nexport const HEADER_ERROR_CODE = 'error-code';\n\n/** Header: human-readable error message accompanying an `ai-run-end` with reason `error`. */\nexport const HEADER_ERROR_MESSAGE = 'error-message';\n\n// ---------------------------------------------------------------------------\n// Message / event names\n// ---------------------------------------------------------------------------\n\n/**\n * Message name: client->agent cancel intent. Targets a run by `run-id` (a\n * continuation, whose run-id the client already knows) and/or by\n * `input-codec-message-id` (a fresh send, whose run-id the agent mints at\n * run-start — so the client can only key the cancel by the triggering input's\n * codec-message-id it owns at send time). The agent resolves whichever is\n * present to the registered run; a cancel that arrives before the run is known\n * (the input-event lookup hasn't resolved the input id to a run yet) is\n * buffered by `input-codec-message-id` and honoured when the run resolves it.\n * Also carries an `event-id` so channel rewind redelivers it to a per-request /\n * serverless agent that attaches after the cancel was published.\n */\nexport const EVENT_CANCEL = 'ai-cancel';\n\n/** Message name: server publishes this to signal a run has started. */\nexport const EVENT_RUN_START = 'ai-run-start';\n\n/**\n * Message name: server publishes this to signal a run has suspended — paused\n * awaiting participant input (e.g. a client tool result or approval) without\n * ending. The run stays live and may be resumed under the same `runId`.\n * Distinct from `ai-run-end`, which is terminal.\n */\nexport const EVENT_RUN_SUSPEND = 'ai-run-suspend';\n\n/**\n * Message name: server publishes this when a subsequent invocation re-enters an\n * already-started run (e.g. a tool-result follow-up under the same `runId`).\n * A pure re-entry signal: unlike `ai-run-start` it carries no `parent` / `fork-of`\n * (the original `ai-run-start` already established the run's structure).\n */\nexport const EVENT_RUN_RESUME = 'ai-run-resume';\n\n/** Message name: server publishes this to signal a run has ended. */\nexport const EVENT_RUN_END = 'ai-run-end';\n\n/**\n * Message name: every agent-published codec event (text, reasoning, tool calls,\n * tool outputs, lifecycle helpers, file / source parts, data-* chunks) rides\n * this single wire name. The codec event's own `type` is carried in the\n * SDK-controlled codec-level `kind` header so the decoder can dispatch.\n */\nexport const EVENT_AI_OUTPUT = 'ai-output';\n\n/**\n * Message name: every client-published codec event (user-message parts,\n * tool-approval responses, regenerate signals) rides this single wire\n * name. The codec event's own kind is carried in the codec-level `kind`\n * header so the decoder can dispatch.\n */\nexport const EVENT_AI_INPUT = 'ai-input';\n","import * as Ably from 'ably';\n\n/**\n * Error codes for the AI Transport SDK.\n */\nexport enum ErrorCode {\n /**\n * The request was invalid.\n */\n BadRequest = 40000,\n\n /**\n * Invalid argument provided.\n */\n InvalidArgument = 40003,\n\n /**\n * Operation not permitted with the provided capability (Ably 40160).\n * Used when the Ably channel rejects a publish for a capability reason.\n */\n InsufficientCapability = 40160,\n\n // 104000 - 104999 are reserved for AI Transport SDK errors\n\n /**\n * Encoder recovery failed during flush — one or more updateMessage calls\n * could not recover a failed append pipeline.\n */\n EncoderRecoveryFailed = 104000,\n\n /**\n * A session-level channel subscription callback threw unexpectedly.\n */\n SessionSubscriptionError = 104001,\n\n /**\n * Cancel listener or onCancel hook threw while processing a cancel message.\n */\n CancelListenerError = 104002,\n\n /**\n * A publish within a run failed (lifecycle event, message, or event).\n */\n RunLifecycleError = 104003,\n\n /**\n * An operation was attempted on a session that has already been closed.\n */\n SessionClosed = 104004,\n\n /**\n * The HTTP POST to the agent endpoint failed (network error or non-2xx response).\n */\n SessionSendFailed = 104005,\n\n /**\n * The Ably channel lost message continuity — the channel entered FAILED,\n * SUSPENDED, or DETACHED, or re-attached with `resumed: false`. Active\n * streams can no longer be guaranteed to receive all events.\n */\n ChannelContinuityLost = 104006,\n\n /**\n * An operation was attempted but the channel is not in a usable state\n * (not ATTACHED or ATTACHING).\n */\n ChannelNotReady = 104007,\n\n /**\n * An error occurred while piping a response stream to the channel — either\n * the source event stream threw (e.g. LLM provider rate limit, model error,\n * network failure) or an underlying publish failed mid-stream.\n */\n StreamError = 104008,\n\n /**\n * The agent waited for the input event(s) the invocation points at —\n * across the bounded history scan and the live subscription — but\n * `inputEventLookupTimeoutMs` lapsed without seeing them.\n */\n InputEventNotFound = 104010,\n\n /**\n * Channel history pagination failed after bounded retry — either the initial\n * `channel.history()` call or a subsequent `page.next()` exhausted its\n * retry budget. The original failure is preserved as `cause`.\n */\n HistoryFetchFailed = 104011,\n}\n\n/**\n * Returns true if the {@link Ably.ErrorInfo} code matches the provided ErrorCode value.\n * @param errorInfo The error info to check.\n * @param error The error code to compare against.\n * @returns true if the error code matches, false otherwise.\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison\nexport const errorInfoIs = (errorInfo: Ably.ErrorInfo, error: ErrorCode): boolean => errorInfo.code === error;\n","/**\n * Shared utilities for working with Ably messages.\n *\n * These are general-purpose helpers used by both the codec and transport\n * layers. They live at the top level to avoid either layer depending on\n * the other.\n */\n\nimport * as Ably from 'ably';\n\n/**\n * Extract a human-readable message from an unknown thrown value.\n * @param error - The thrown value.\n * @returns The error's `message` when it is an `Error`, otherwise its string form.\n */\nexport const errorMessage = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/**\n * Narrow an unknown thrown value to an `Ably.ErrorInfo` for use as a wrapping\n * `cause`, returning `undefined` when it is not one. Pass the result as the\n * fourth argument to the `Ably.ErrorInfo` constructor to preserve the error\n * chain without asserting a type the value may not have.\n * @param error - The thrown value.\n * @returns The value when it is an `Ably.ErrorInfo`, otherwise `undefined`.\n */\nexport const errorCause = (error: unknown): Ably.ErrorInfo | undefined =>\n error instanceof Ably.ErrorInfo ? error : undefined;\n\n/**\n * Read one tier of the SDK's `extras.ai` namespace from an Ably message.\n * `extras.ai` is the SDK's reserved corner of the message envelope, split into\n * a `transport` tier (generic transport headers) and a `codec` tier (codec\n * headers). The application's own `extras.headers` is deliberately left\n * untouched.\n * @param message - The Ably message to read from.\n * @param tier - Which `extras.ai` sub-namespace to read.\n * @returns The tier's headers record, or an empty object if absent.\n */\nconst getAiTier = (message: Ably.InboundMessage, tier: 'transport' | 'codec'): Record<string, string> => {\n // CAST: Ably SDK types `extras` as `any`; runtime checks below guard access.\n const extras = message.extras as unknown;\n if (!extras || typeof extras !== 'object') return {};\n const ai = (extras as { ai?: unknown }).ai;\n if (!ai || typeof ai !== 'object') return {};\n const sub = (ai as Record<string, unknown>)[tier];\n if (!sub || typeof sub !== 'object') return {};\n // CAST: Ably wire protocol guarantees the tier is Record<string, string>\n // when present, verified by the runtime guards above.\n return sub as Record<string, string>;\n};\n\n/**\n * Extract the transport-tier headers (`extras.ai.transport`) from an Ably\n * InboundMessage. These are the generic transport headers (run/stream/identity/\n * branching), set and read by the transport layer.\n * @param message - The Ably message to extract headers from.\n * @returns The transport headers record, or an empty object if absent.\n */\nexport const getTransportHeaders = (message: Ably.InboundMessage): Record<string, string> =>\n getAiTier(message, 'transport');\n\n/**\n * Extract the codec-tier headers (`extras.ai.codec`) from an Ably\n * InboundMessage. These are the codec's own headers, with no prefix — the\n * tier isolates them from transport headers.\n * @param message - The Ably message to extract headers from.\n * @returns The codec headers record, or an empty object if absent.\n */\nexport const getCodecHeaders = (message: Ably.InboundMessage): Record<string, string> => getAiTier(message, 'codec');\n\n/**\n * Parse a JSON string, returning undefined on failure.\n * @param value - The JSON string to parse.\n * @returns The parsed value, or undefined if parsing fails.\n */\nexport const parseJson = (value: string | undefined): unknown => {\n if (value === undefined) return undefined;\n try {\n return JSON.parse(value) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Parse a string as JSON, falling back to the raw string when it isn't valid\n * JSON. An empty string yields `undefined`. Used for accumulated stream text\n * whose payload may be JSON or a plain string.\n * @param value - The string to parse.\n * @returns The parsed value, the raw string on parse failure, or undefined if empty.\n */\nexport const parseJsonOrString = (value: string): unknown => {\n if (!value) return undefined;\n try {\n // CAST: JSON.parse returns any; unknown is the safe trust-boundary type.\n return JSON.parse(value) as unknown;\n } catch {\n return value;\n }\n};\n\n/**\n * Merge two header records into a new object. Later values override earlier ones.\n * Undefined inputs are treated as empty.\n * @param base - Base headers (lower priority).\n * @param overrides - Override headers (higher priority).\n * @returns A new merged headers object.\n */\nexport const mergeHeaders = (\n base: Record<string, string> | undefined,\n overrides: Record<string, string> | undefined,\n): Record<string, string> => ({\n ...base,\n ...overrides,\n});\n\n/**\n * Parse a boolean header (\"true\"/\"false\"), returning undefined if absent.\n * @param value - The header string to parse.\n * @returns True if \"true\", false for any other string, or undefined if absent.\n */\nexport const parseBool = (value: string | undefined): boolean | undefined => {\n if (value === undefined) return undefined;\n return value === 'true';\n};\n\n/** A record carrying an optional Ably `serial`, orderable by {@link compareBySerial}. */\ninterface HasSerial {\n /** Ably serial, or undefined if the server has not yet assigned one. */\n readonly serial?: string;\n}\n\n/**\n * Comparator that orders records by their Ably `serial` ascending\n * (chronological). Serials are lexicographically comparable; records whose\n * serial is undefined sort last. Pass directly to `Array.prototype.sort`.\n * @param a - First record to compare.\n * @param b - Second record to compare.\n * @returns Negative if `a` precedes `b`, positive if `a` follows `b`, 0 if equal.\n */\nexport const compareBySerial = (a: HasSerial, b: HasSerial): number => {\n if (a.serial === undefined && b.serial === undefined) return 0;\n if (a.serial === undefined) return 1;\n if (b.serial === undefined) return -1;\n if (a.serial < b.serial) return -1;\n if (a.serial > b.serial) return 1;\n return 0;\n};\n\n/**\n * Mapped type that converts properties whose type includes `undefined`\n * into optional properties with `undefined` excluded from the value.\n * Properties typed as `unknown` are kept required (since `undefined extends unknown`\n * is always true, but `unknown` fields are intentionally broad, not optional).\n */\nexport type Stripped<T> = {\n [K in keyof T as undefined extends T[K] ? (unknown extends T[K] ? K : never) : K]: T[K];\n} & {\n [K in keyof T as undefined extends T[K] ? (unknown extends T[K] ? never : K) : never]?: Exclude<T[K], undefined>;\n};\n\n/**\n * Remove all keys whose value is `undefined` from a shallow object.\n * Returns a new object — the input is not mutated. Useful for building\n * chunk literals with optional fields without conditional spread noise.\n *\n * The return type converts `{ foo: T | undefined }` to `{ foo?: T }`,\n * matching the optional-field pattern used by the AI SDK chunk types.\n * @param obj - The object to strip undefined values from.\n * @returns A shallow copy with undefined-valued keys removed.\n */\nexport const stripUndefined = <T extends Record<string, unknown>>(obj: T): Stripped<T> => {\n const result = {} as Record<string, unknown>;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n // CAST: The runtime strip guarantees the Stripped<T> contract —\n // required keys are always present, optional keys are absent when undefined.\n return result as Stripped<T>;\n};\n","/**\n * Encoder core — message append lifecycle machinery.\n *\n * Provides Ably primitives (publish, append, close, cancel, flush) that\n * domain-specific encoders wire their event types to.\n *\n * Domain encoders call `createEncoderCore(writer, options)` and use the\n * returned core to map domain events to Ably operations without\n * reimplementing the message append lifecycle.\n */\n\nimport * as Ably from 'ably';\n\nimport {\n HEADER_CODEC_MESSAGE_ID,\n HEADER_DISCRETE,\n HEADER_STATUS,\n HEADER_STREAM,\n HEADER_STREAM_ID,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport { mergeHeaders } from '../../utils.js';\nimport type { ChannelWriter, EncoderOptions, Extras, MessagePayload, StreamPayload, WriteOptions } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for creating an encoder core. Extends {@link EncoderOptions} with a logger. */\nexport interface EncoderCoreOptions extends EncoderOptions {\n /** Logger instance for diagnostic output. */\n logger?: Logger;\n}\n\n// ---------------------------------------------------------------------------\n// Stream tracker (internal)\n// ---------------------------------------------------------------------------\n\ninterface StreamState {\n serial: string;\n name: string;\n streamId: string;\n accumulated: string;\n /** Transport-tier headers repeated on every append (`extras.ai.transport`). */\n persistentTransport: Record<string, string>;\n /** Codec-tier headers repeated on every append (`extras.ai.codec`). */\n persistentCodec: Record<string, string>;\n cancelled: boolean;\n /** Set by `closeStream` — a completed stream must never receive a cancelled terminal. */\n completed: boolean;\n}\n\n/**\n * The SDK's `extras.ai` namespace as written to the wire: a `transport` tier\n * (always present on SDK-published messages) and an optional `codec` tier.\n */\ninterface AiExtras {\n transport: Record<string, string>;\n codec?: Record<string, string>;\n}\n\ninterface PendingAppend {\n promise: Promise<Ably.UpdateDeleteResult>;\n streamId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Encoder core interface\n// ---------------------------------------------------------------------------\n\n/** The core encoder primitives that domain codec encoders delegate to. */\nexport interface EncoderCore {\n /** Publish a single discrete (non-streaming) message described by a payload. */\n publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult>;\n\n /** Publish multiple discrete messages atomically in a single channel publish. */\n publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult>;\n\n /** Start a streamed message with status:streaming. */\n startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void>;\n\n /**\n * Append data to an in-flight streamed message. Fire-and-forget: errors are\n * collected internally and surfaced by {@link closeStream},\n * {@link cancelAllStreams} or {@link close}.\n * @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId` or the core is closed.\n */\n appendStream(streamId: string, data: string): void;\n\n /**\n * Close a streamed message with status:complete. Flushes all pending\n * appends for recovery before returning. Repeats persistent and payload headers.\n * @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId`, or the encoder has been closed; EncoderRecoveryFailed if a failed append cannot be recovered during the flush.\n */\n closeStream(streamId: string, payload: StreamPayload): Promise<void>;\n\n /**\n * Cancel all in-progress streams (status:cancelled) and flush all\n * pending appends for recovery before returning.\n */\n cancelAllStreams(opts?: WriteOptions): Promise<void>;\n\n /** Flush + clear trackers. Idempotent. */\n close(): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD1\nclass DefaultEncoderCore implements EncoderCore {\n private readonly _writer: ChannelWriter;\n private readonly _defaultExtras: Extras | undefined;\n private readonly _onMessageHook: (message: Ably.Message) => void;\n private readonly _logger: Logger | undefined;\n private readonly _trackers = new Map<string, StreamState>();\n private _pending: PendingAppend[] = [];\n private _flushPromise: Promise<void> | undefined;\n private _closed = false;\n\n constructor(writer: ChannelWriter, options: EncoderCoreOptions = {}) {\n this._writer = writer;\n this._defaultExtras = options.extras;\n this._onMessageHook =\n options.onMessage ??\n (() => {\n /* noop */\n });\n this._logger = options.logger?.withContext({ component: 'EncoderCore' });\n }\n\n // Spec: AIT-CD11\n async publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.publishDiscrete();', { name: payload.name });\n const msg = this._buildDiscreteMessage(payload, opts);\n return this._writer.publish(msg);\n }\n\n // Spec: AIT-CD11a\n async publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.publishDiscreteBatch();', { count: payloads.length });\n const msgs = payloads.map((p) => this._buildDiscreteMessage(p, opts, true));\n return this._writer.publish(msgs);\n }\n\n // Spec: AIT-CD2\n async startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.startStream();', { name: payload.name, streamId });\n\n const transport = this._buildTransport(payload.transportHeaders, opts);\n transport[HEADER_STREAM] = 'true';\n transport[HEADER_STATUS] = 'streaming';\n transport[HEADER_STREAM_ID] = streamId;\n const codec = payload.codecHeaders ?? {};\n\n const msg: Ably.Message = {\n name: payload.name,\n data: payload.data,\n extras: { ai: this._aiExtras(transport, codec) },\n };\n\n this._invokeOnMessage(msg);\n const result = await this._writer.publish(msg);\n const serial = result.serials[0];\n\n // Spec: AIT-CD2a\n if (!serial) {\n throw new Ably.ErrorInfo(\n `unable to start stream; no serial returned for stream '${payload.name}' (streamId: ${streamId})`,\n ErrorCode.BadRequest,\n 400,\n );\n }\n\n this._trackers.set(streamId, {\n serial,\n name: payload.name,\n streamId,\n accumulated: payload.data,\n persistentTransport: transport,\n persistentCodec: codec,\n cancelled: false,\n completed: false,\n });\n\n this._logger?.debug('DefaultEncoderCore.startStream(); stream started', {\n name: payload.name,\n streamId,\n serial,\n });\n }\n\n // Spec: AIT-CD3\n appendStream(streamId: string, data: string): void {\n this._assertNotClosed();\n // Spec: AIT-CD3a\n const tracker = this._trackers.get(streamId);\n if (!tracker) {\n throw new Ably.ErrorInfo(\n `unable to append to stream; no active stream for streamId '${streamId}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n tracker.accumulated += data;\n\n const appendMsg: Ably.Message = {\n serial: tracker.serial,\n data,\n extras: { ai: this._aiExtras({ ...tracker.persistentTransport }, { ...tracker.persistentCodec }) },\n };\n\n this._invokeOnMessage(appendMsg);\n const p = this._writer.appendMessage(appendMsg);\n this._pending.push({ promise: p, streamId });\n }\n\n // Spec: AIT-CD4, AIT-CD4a\n async closeStream(streamId: string, payload: StreamPayload): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.closeStream();', { streamId });\n\n const tracker = this._trackers.get(streamId);\n if (!tracker) {\n throw new Ably.ErrorInfo(\n `unable to close stream; no active stream for streamId '${streamId}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n // Accumulate closing data so recovery has the full content\n tracker.accumulated += payload.data;\n // Mark completed so a later cancelAllStreams (e.g. pipeStream terminating\n // streams left open by an agent self-abort) skips this stream.\n tracker.completed = true;\n\n const { transport, codec } = this._buildClosing(tracker, payload);\n transport[HEADER_STATUS] = 'complete';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: payload.data,\n extras: { ai: this._aiExtras(transport, codec) },\n };\n\n this._invokeOnMessage(msg);\n const p = this._writer.appendMessage(msg);\n this._pending.push({ promise: p, streamId });\n\n await this._flushPending();\n\n this._logger?.debug('DefaultEncoderCore.closeStream(); stream closed', { streamId });\n }\n\n // Spec: AIT-CD5, AIT-CD5a\n async cancelAllStreams(opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.cancelAllStreams();', { streamCount: this._trackers.size });\n\n for (const tracker of this._trackers.values()) {\n // Idempotent and complete-safe: a stream already cancelled must not be\n // re-appended on a repeat call, and a stream that closed with\n // status:complete must never receive a cancelled terminal.\n if (tracker.cancelled || tracker.completed) continue;\n tracker.cancelled = true;\n\n const { transport, codec } = this._buildClosing(tracker, undefined, opts);\n transport[HEADER_STATUS] = 'cancelled';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: '',\n extras: { ai: this._aiExtras(transport, codec) },\n };\n\n this._invokeOnMessage(msg);\n const p = this._writer.appendMessage(msg);\n this._pending.push({ promise: p, streamId: tracker.streamId });\n }\n\n await this._flushPending();\n }\n\n // Spec: AIT-CD6\n private async _flushPending(): Promise<void> {\n // Re-entrancy guard: if a flush is already in progress, await it instead of starting a new one.\n if (this._flushPromise) {\n return this._flushPromise;\n }\n\n const snapshot = this._pending;\n this._pending = [];\n\n if (snapshot.length === 0) return;\n\n this._logger?.trace('DefaultEncoderCore._flushPending();', { count: snapshot.length });\n\n this._flushPromise = this._doFlush(snapshot);\n try {\n await this._flushPromise;\n } finally {\n this._flushPromise = undefined;\n }\n }\n\n private async _doFlush(snapshot: PendingAppend[]): Promise<void> {\n const results = await Promise.allSettled(snapshot.map(async (p) => p.promise));\n const failures = new Set<string>();\n\n for (const [i, result] of results.entries()) {\n const entry = snapshot[i];\n if (entry && result.status === 'rejected') {\n failures.add(entry.streamId);\n }\n }\n\n if (failures.size === 0) {\n this._logger?.debug('DefaultEncoderCore._flushPending(); all appends succeeded');\n return;\n }\n\n this._logger?.warn('DefaultEncoderCore._flushPending(); recovering failed appends', {\n failedStreams: [...failures],\n });\n\n const recoveryErrors: { streamId: string; error: unknown }[] = [];\n\n for (const streamId of failures) {\n const tracker = this._trackers.get(streamId);\n if (!tracker) continue;\n\n const recoveryStatus = tracker.cancelled ? 'cancelled' : 'complete';\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: tracker.accumulated,\n extras: {\n ai: this._aiExtras(\n { ...tracker.persistentTransport, [HEADER_STATUS]: recoveryStatus },\n { ...tracker.persistentCodec },\n ),\n },\n };\n\n try {\n await this._writer.updateMessage(msg);\n } catch (error) {\n recoveryErrors.push({ streamId, error });\n }\n }\n\n if (recoveryErrors.length > 0) {\n const ids = recoveryErrors.map((e) => e.streamId).join(', ');\n this._logger?.error('DefaultEncoderCore._flushPending(); recovery failed', { failedStreams: ids });\n throw new Ably.ErrorInfo(\n `unable to flush pending appends; recovery failed for stream(s): ${ids}`,\n ErrorCode.EncoderRecoveryFailed,\n 500,\n );\n }\n }\n\n // Spec: AIT-CD12\n async close(): Promise<void> {\n if (this._closed) return;\n this._logger?.trace('DefaultEncoderCore.close();');\n this._closed = true;\n try {\n await this._flushPending();\n } finally {\n this._trackers.clear();\n }\n this._logger?.debug('DefaultEncoderCore.close(); encoder closed');\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD14\n private _invokeOnMessage(msg: Ably.Message): void {\n try {\n this._onMessageHook(msg);\n } catch (error) {\n this._logger?.error('DefaultEncoderCore._invokeOnMessage(); hook threw', { error });\n }\n }\n\n private _assertNotClosed(): void {\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to write to encoder; encoder has been closed', ErrorCode.InvalidArgument, 400);\n }\n }\n\n /**\n * Build the transport-tier header record for a message: caller-configured\n * transport headers (default extras + per-write overrides) layered with any\n * transport headers the codec payload stamps directly, plus the message-id.\n * @param payloadTransport - Transport headers carried on the codec payload.\n * @param opts - Optional per-write overrides.\n * @returns The transport-tier headers record (`extras.ai.transport`).\n */\n private _buildTransport(\n payloadTransport: Record<string, string> | undefined,\n opts?: WriteOptions,\n ): Record<string, string> {\n const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);\n const transport = { ...callerHeaders, ...payloadTransport };\n if (opts?.messageId !== undefined) {\n transport[HEADER_CODEC_MESSAGE_ID] = opts.messageId;\n }\n return transport;\n }\n\n /**\n * Assemble the `extras.ai` namespace from its two tiers, omitting the codec\n * tier when empty.\n * @param transport - Transport-tier headers (always present on SDK messages).\n * @param codec - Codec-tier headers; omitted from the wire when empty.\n * @returns The `extras.ai` object.\n */\n private _aiExtras(transport: Record<string, string>, codec: Record<string, string>): AiExtras {\n return Object.keys(codec).length > 0 ? { transport, codec } : { transport };\n }\n\n private _buildDiscreteMessage(payload: MessagePayload, opts?: WriteOptions, discrete = false): Ably.Message {\n const transport = this._buildTransport(payload.transportHeaders, opts);\n transport[HEADER_STREAM] = 'false';\n if (discrete) {\n // Mark batch-published payloads as discrete message parts (from writeMessages).\n // The decoder relies on this header to distinguish message parts from lifecycle\n // events that also happen to be discrete (stream: false).\n transport[HEADER_DISCRETE] = 'true';\n }\n const msg: Ably.Message = {\n name: payload.name,\n data: payload.data,\n extras: {\n ai: this._aiExtras(transport, payload.codecHeaders ?? {}),\n ...(payload.ephemeral ? { ephemeral: true } : {}),\n },\n };\n\n this._invokeOnMessage(msg);\n return msg;\n }\n\n /**\n * Build both header tiers for a closing append. Closing appends must repeat\n * ALL persistent headers (Ably replaces the entire extras object on append).\n * Then layer caller and codec overrides.\n * @param tracker - The stream tracker with persistent headers.\n * @param payload - The closing stream payload (codec + transport headers).\n * @param opts - Optional per-write overrides.\n * @returns The two tiers for the closing append.\n */\n private _buildClosing(\n tracker: StreamState,\n payload: StreamPayload | undefined,\n opts?: WriteOptions,\n ): { transport: Record<string, string>; codec: Record<string, string> } {\n const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);\n const transport = { ...tracker.persistentTransport, ...callerHeaders, ...payload?.transportHeaders };\n const codec = { ...tracker.persistentCodec, ...payload?.codecHeaders };\n return { transport, codec };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an encoder core bound to the given channel writer.\n * @param writer - The channel writer to publish messages through.\n * @param options - Encoder configuration (extras, hooks, logger).\n * @returns A new {@link EncoderCore} instance.\n */\nexport const createEncoderCore = (writer: ChannelWriter, options: EncoderCoreOptions = {}): EncoderCore =>\n new DefaultEncoderCore(writer, options);\n","/**\n * Decoder core — action dispatch and serial tracking machinery.\n *\n * Handles the Ably message action patterns (create, append, update, delete)\n * and delegates to domain-specific hooks for event building and discrete\n * event decoding. Stream trackers are version-guarded: a delivery whose\n * `Message.version.serial` the tracker has already incorporated decodes to\n * nothing, so the same decoder instance can serve both the live\n * subscription and history hydration without double-decoding.\n *\n * Domain decoders call `createDecoderCore(hooks, options)` and provide hooks\n * for stream classification, event building, and discrete decoding. Hooks\n * return a flat `TEvent[]` — no event-vs-message union. Per-message routing\n * concerns (`codec-message-id`) are handled by the SDK via `ReducerMeta`, not\n * here.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { getCodecHeaders, getTransportHeaders } from '../../utils.js';\nimport type { MessagePayload, StreamTrackerState } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for creating a decoder core. */\nexport interface DecoderCoreOptions {\n /** Called when a tracked stream is replaced (non-prefix update). Receives the tracker with updated state. */\n onStreamUpdate?: (tracker: StreamTrackerState) => void;\n /** Called when a message is deleted. Receives the serial and tracker (if one exists). */\n onStreamDelete?: (serial: string, tracker: StreamTrackerState | undefined) => void;\n /** Logger instance for diagnostic output. */\n logger?: Logger;\n}\n\n// ---------------------------------------------------------------------------\n// Domain hooks\n// ---------------------------------------------------------------------------\n\n/** Hooks that a domain codec provides to the decoder core for stream classification and event building. */\nexport interface DecoderCoreHooks<TEvent> {\n /**\n * Build domain events emitted when a new stream starts. May return multiple\n * events (e.g. a start event and a start-step event).\n */\n buildStartEvents(tracker: StreamTrackerState): TEvent[];\n\n /** Build domain events for a text delta received on a stream. */\n buildDeltaEvents(tracker: StreamTrackerState, delta: string): TEvent[];\n\n /**\n * Build domain events emitted when a stream completes (status:complete).\n * Not called for cancelled streams. The closing codec headers may differ\n * from tracker.codecHeaders if the closing append carried updated headers.\n */\n buildEndEvents(tracker: StreamTrackerState, closingCodecHeaders: Record<string, string>): TEvent[];\n\n /**\n * Decode a discrete message (a `message.create` whose stream header is not\n * \"true\", or a non-streamable first-contact update). Handles user messages,\n * tool lifecycle, data-*, etc.\n */\n decodeDiscrete(input: MessagePayload): TEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\n/** The decoder core returned by {@link createDecoderCore}. */\nexport interface DecoderCore<TEvent> {\n /** Decode a single Ably message into zero or more domain TEvents. */\n decode(message: Ably.InboundMessage): TEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD7\nclass DefaultDecoderCore<TEvent> implements DecoderCore<TEvent> {\n private readonly _hooks: DecoderCoreHooks<TEvent>;\n private readonly _logger: Logger | undefined;\n private readonly _onStreamUpdate: ((tracker: StreamTrackerState) => void) | undefined;\n private readonly _onStreamDelete: ((serial: string, tracker: StreamTrackerState | undefined) => void) | undefined;\n private readonly _serialState = new Map<string, StreamTrackerState>();\n\n constructor(hooks: DecoderCoreHooks<TEvent>, options: DecoderCoreOptions = {}) {\n this._hooks = hooks;\n this._onStreamUpdate = options.onStreamUpdate;\n this._onStreamDelete = options.onStreamDelete;\n this._logger = options.logger?.withContext({ component: 'DecoderCore' });\n }\n\n decode(message: Ably.InboundMessage): TEvent[] {\n const action = message.action;\n\n this._logger?.trace('DefaultDecoderCore.decode();', { action, serial: message.serial, name: message.name });\n\n switch (action) {\n // Spec: AIT-CD7a\n case 'message.create': {\n const payload = this._toPayload(message);\n return payload.transportHeaders?.[HEADER_STREAM] === 'true'\n ? this._decodeStreamedCreate(payload, message.serial, message.version.serial)\n : this._hooks.decodeDiscrete(payload);\n }\n\n case 'message.append': {\n return this._decodeAppend(message);\n }\n\n case 'message.update': {\n return this._decodeUpdate(message);\n }\n\n case 'message.delete': {\n return this._decodeDelete(message);\n }\n\n default: {\n return [];\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: extract MessagePayload\n // -------------------------------------------------------------------------\n\n private _toPayload(message: Ably.InboundMessage): MessagePayload {\n return {\n name: message.name ?? '',\n // CAST: Ably SDK types `data` as `any`; cast to unknown is the safe boundary type.\n data: message.data as unknown,\n transportHeaders: getTransportHeaders(message),\n codecHeaders: getCodecHeaders(message),\n };\n }\n\n /**\n * Extract string data from an Ably message, for stream accumulation paths.\n * @param message - The Ably message to extract string data from.\n * @returns The string data, or empty string if data is not a string.\n */\n private _stringData(message: Ably.InboundMessage): string {\n return typeof message.data === 'string' ? message.data : '';\n }\n\n // -------------------------------------------------------------------------\n // Private: safe callback invocation\n // -------------------------------------------------------------------------\n\n private _invokeOnStreamUpdate(tracker: StreamTrackerState): void {\n if (!this._onStreamUpdate) return;\n try {\n this._onStreamUpdate(tracker);\n } catch (error) {\n this._logger?.error('DefaultDecoderCore._invokeOnStreamUpdate(); callback threw', { error });\n }\n }\n\n private _invokeOnStreamDelete(serial: string, tracker: StreamTrackerState | undefined): void {\n if (!this._onStreamDelete) return;\n try {\n this._onStreamDelete(serial, tracker);\n } catch (error) {\n this._logger?.error('DefaultDecoderCore._invokeOnStreamDelete(); callback threw', { error });\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: version guard\n // -------------------------------------------------------------------------\n\n /**\n * Whether a delivery is already incorporated into (or out of contract for)\n * an existing tracker, and so must decode to nothing. Covers two cases:\n *\n * - The delivery carries a `version.serial` at or below the tracker's —\n * the mutation it describes is already incorporated (a history aggregate\n * covered by live deltas, a resume retransmission, a whole-wire replay).\n * - The tracker is closed — the stream has ended and its accumulated text\n * has been dropped, so nothing further can fold into it. In-contract\n * replays are already covered by the version check; this catches\n * out-of-contract version-less deliveries for an ended stream.\n *\n * A version-bearing delivery that passes advances the tracker's version.\n * @param method - Calling method name, for log messages.\n * @param serial - The message serial (the tracker's key).\n * @param tracker - The existing tracker for the serial.\n * @param version - The delivery's `Message.version.serial`, if present.\n * @returns True when the delivery must decode to nothing.\n */\n private _alreadyIncorporated(\n method: string,\n serial: string,\n tracker: StreamTrackerState,\n version: string | undefined,\n ): boolean {\n if (version !== undefined && version <= tracker.version) {\n this._logger?.debug(`DefaultDecoderCore.${method}(); delivery already incorporated`, {\n serial,\n version,\n trackerVersion: tracker.version,\n });\n return true;\n }\n if (tracker.closed) {\n this._logger?.debug(`DefaultDecoderCore.${method}(); stream closed, dropping delivery`, { serial, version });\n return true;\n }\n if (version !== undefined) tracker.version = version;\n return false;\n }\n\n /**\n * Close a tracker, dropping its accumulated text. What remains is a\n * `{version, closed}` tombstone: enough to recognise covered replays and\n * out-of-contract post-close deliveries, without retaining the stream's\n * full content for the decoder's lifetime.\n * @param tracker - The tracker to close.\n */\n private _closeTracker(tracker: StreamTrackerState): void {\n tracker.closed = true;\n tracker.accumulated = '';\n }\n\n // -------------------------------------------------------------------------\n // Private: terminal-status transition\n // -------------------------------------------------------------------------\n\n /**\n * Apply a stream's terminal status (complete / cancelled) to a tracker. On\n * `complete` it emits end events (read before the tracker is closed) and\n * then closes the tracker; on `cancelled` it closes silently. Both the\n * append and prefix-match update paths funnel through here so they can't\n * diverge. Covered replays and post-close deliveries are filtered upstream\n * by `_alreadyIncorporated`, so no closed-once guard is needed here.\n * Returns whether a terminal transition fired (so callers can log it).\n * @param tracker - The stream tracker to close.\n * @param status - The status header value from the message (may be undefined).\n * @param closingCodecHeaders - Codec headers from the closing message, passed to buildEndEvents.\n * @param outputs - The output array end events are pushed into.\n * @returns True when this call closed the tracker; false otherwise.\n */\n private _applyTerminalStatus(\n tracker: StreamTrackerState,\n status: string | undefined,\n closingCodecHeaders: Record<string, string>,\n outputs: TEvent[],\n ): boolean {\n if (status === 'complete') {\n outputs.push(...this._hooks.buildEndEvents(tracker, closingCodecHeaders));\n this._closeTracker(tracker);\n return true;\n }\n if (status === 'cancelled') {\n this._closeTracker(tracker);\n return true;\n }\n return false;\n }\n\n // -------------------------------------------------------------------------\n // Private: streamed message create\n // -------------------------------------------------------------------------\n\n private _decodeStreamedCreate(\n payload: MessagePayload,\n serial: string | undefined,\n version: string | undefined,\n ): TEvent[] {\n if (!serial) return [];\n\n const existing = this._serialState.get(serial);\n if (existing) {\n // A create is the message's first version, so a tracker for this serial\n // has already incorporated it (resume retransmission, whole-wire replay).\n this._logger?.debug('DefaultDecoderCore._decodeStreamedCreate(); duplicate create for tracked stream', {\n serial,\n });\n return [];\n }\n\n const streamId = payload.transportHeaders?.[HEADER_STREAM_ID] ?? '';\n\n const tracker: StreamTrackerState = {\n name: payload.name,\n streamId,\n accumulated: '',\n codecHeaders: { ...payload.codecHeaders },\n transportHeaders: { ...payload.transportHeaders },\n version: version ?? serial,\n closed: false,\n };\n this._serialState.set(serial, tracker);\n\n this._logger?.debug('DefaultDecoderCore._decodeStreamedCreate(); new stream', {\n name: payload.name,\n streamId,\n serial,\n });\n\n return this._hooks.buildStartEvents(tracker);\n }\n\n // -------------------------------------------------------------------------\n // Private: append handling\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD8\n private _decodeAppend(message: Ably.InboundMessage): TEvent[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const tracker = this._serialState.get(serial);\n if (!tracker) {\n // Out of contract: the platform converts the first post-attach append\n // of an in-flight message into a full-contents update, so an append\n // should never be a stream's first contact. Keep the first-contact\n // heuristic as a defensive fallback.\n this._logger?.warn('DefaultDecoderCore._decodeAppend(); append with no tracker, treating as first contact', {\n serial,\n });\n return this._decodeUpdate(message);\n }\n\n if (this._alreadyIncorporated('_decodeAppend', serial, tracker, message.version.serial)) return [];\n\n const transport = getTransportHeaders(message);\n const closingCodec = getCodecHeaders(message);\n const delta = typeof message.data === 'string' ? message.data : '';\n const status = transport[HEADER_STATUS];\n const outputs: TEvent[] = [];\n\n if (delta.length > 0) {\n tracker.accumulated += delta;\n outputs.push(...this._hooks.buildDeltaEvents(tracker, delta));\n }\n\n if (this._applyTerminalStatus(tracker, status, closingCodec, outputs)) {\n this._logger?.debug(\n `DefaultDecoderCore._decodeAppend(); stream ${status === 'complete' ? 'complete' : 'cancelled'}`,\n {\n streamId: tracker.streamId,\n },\n );\n }\n\n return outputs;\n }\n\n // -------------------------------------------------------------------------\n // Private: update handling (first-contact, prefix-match, replacement)\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD9\n private _decodeUpdate(message: Ably.InboundMessage): TEvent[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const payload = this._toPayload(message);\n const transport = payload.transportHeaders ?? {};\n const codec = payload.codecHeaders ?? {};\n const isStreamed = transport[HEADER_STREAM] === 'true';\n const status = transport[HEADER_STATUS];\n\n const tracker = this._serialState.get(serial);\n\n if (!tracker) {\n return this._decodeFirstContact(payload, isStreamed, status, serial, message.version.serial);\n }\n\n if (this._alreadyIncorporated('_decodeUpdate', serial, tracker, message.version.serial)) return [];\n\n // Updates to tracked streams use string data for prefix-match accumulation\n const data = this._stringData(message);\n\n // --- Tracker exists: prefix-match or replacement ---\n if (data.startsWith(tracker.accumulated)) {\n const delta = data.slice(tracker.accumulated.length);\n const outputs: TEvent[] = [];\n\n if (delta.length > 0) {\n tracker.accumulated = data;\n outputs.push(...this._hooks.buildDeltaEvents(tracker, delta));\n }\n\n this._applyTerminalStatus(tracker, status, codec, outputs);\n\n return outputs;\n }\n\n // --- Replacement (NOT a prefix match) ---\n tracker.accumulated = data;\n tracker.codecHeaders = { ...codec };\n tracker.transportHeaders = { ...transport };\n\n this._invokeOnStreamUpdate(tracker);\n\n return [];\n }\n\n private _decodeFirstContact(\n payload: MessagePayload,\n isStreamed: boolean,\n status: string | undefined,\n serial: string,\n version: string | undefined,\n ): TEvent[] {\n // Non-streamed messages are discrete\n if (!isStreamed) {\n return this._hooks.decodeDiscrete(payload);\n }\n\n const streamId = payload.transportHeaders?.[HEADER_STREAM_ID] ?? '';\n const codec = payload.codecHeaders ?? {};\n const data = typeof payload.data === 'string' ? payload.data : '';\n\n this._logger?.debug('DefaultDecoderCore._decodeFirstContact(); first-contact stream', {\n name: payload.name,\n streamId,\n serial,\n });\n\n // Create tracker\n const newTracker: StreamTrackerState = {\n name: payload.name,\n streamId,\n accumulated: data,\n codecHeaders: { ...codec },\n transportHeaders: { ...payload.transportHeaders },\n version: version ?? serial,\n closed: false,\n };\n this._serialState.set(serial, newTracker);\n\n // Emit start + delta (if any) + end (if complete)\n const outputs = this._hooks.buildStartEvents(newTracker);\n\n if (data.length > 0) {\n outputs.push(...this._hooks.buildDeltaEvents(newTracker, data));\n }\n\n if (status === 'complete') {\n outputs.push(...this._hooks.buildEndEvents(newTracker, codec));\n }\n\n if (status === 'complete' || status === 'cancelled') {\n this._closeTracker(newTracker);\n }\n\n return outputs;\n }\n\n // -------------------------------------------------------------------------\n // Private: delete handling\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD10\n private _decodeDelete(message: Ably.InboundMessage): TEvent[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const tracker = this._serialState.get(serial);\n\n this._invokeOnStreamDelete(serial, tracker);\n\n if (tracker) {\n // No need to advance the tracker's version here: `_closeTracker` leaves a\n // closed tombstone, and `_alreadyIncorporated`'s closed check drops every\n // later delivery regardless of version.\n this._closeTracker(tracker);\n }\n\n this._logger?.debug('DefaultDecoderCore._decodeDelete();', { serial });\n\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a decoder core with the given domain hooks.\n * @param hooks - Domain-specific hooks for stream classification, event building, and discrete decoding.\n * @param options - Decoder configuration (callbacks, logger).\n * @returns A new {@link DecoderCore} instance.\n */\nexport const createDecoderCore = <TEvent>(\n hooks: DecoderCoreHooks<TEvent>,\n options: DecoderCoreOptions = {},\n): DecoderCore<TEvent> => new DefaultDecoderCore(hooks, options);\n","/**\n * Generic lifecycle tracker for codec decoders.\n *\n * Manages per-scope (typically per-run) tracking of lifecycle phases that\n * must be emitted before content events. When a phase has not been emitted\n * (e.g. mid-stream join), the tracker synthesizes the missing events using\n * codec-provided build functions.\n *\n * Codecs configure the tracker with an ordered list of phases, then compose\n * it into their decoder hooks. The tracker is independent of any specific\n * codec or event type.\n */\n\n// ---------------------------------------------------------------------------\n// Phase configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for a single lifecycle phase that may need to be\n * synthesized when missing from the wire stream.\n */\nexport interface PhaseConfig<TEvent> {\n /** Unique key identifying this phase (e.g. \"start\", \"start-step\"). */\n key: string;\n /**\n * Build the synthetic event(s) for this phase. Called with a context\n * record that codecs populate at the call site — the tracker passes\n * it through without interpreting it.\n * @param context - Key-value pairs from the call site (e.g. headers).\n * @returns One or more synthetic events to emit for this phase.\n */\n build(context: Record<string, string | undefined>): TEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Tracker interface\n// ---------------------------------------------------------------------------\n\n/**\n * Per-scope lifecycle tracker that ensures required phases are emitted\n * before content events, synthesizing missing ones for mid-stream joins.\n *\n * Scoped by an arbitrary string key (typically a run ID). Each scope\n * tracks independently which phases have been emitted.\n */\nexport interface LifecycleTracker<TEvent> {\n /**\n * Ensure all configured phases have been emitted for the given scope.\n * Synthesizes and returns the events for any phases not yet marked as\n * emitted, marking each as emitted so it is not synthesized again.\n * Returns an empty array if all phases are already emitted.\n * @param scopeId - The scope to check (e.g. run ID).\n * @param context - Key-value pairs passed through to phase build functions.\n * @returns Synthetic events for missing phases, in configuration order.\n */\n ensurePhases(scopeId: string, context: Record<string, string | undefined>): TEvent[];\n\n /**\n * Mark a phase as emitted from the wire (not synthetic). Call this\n * when the real event arrives so the tracker does not re-synthesize it.\n * @param scopeId - The scope (e.g. run ID).\n * @param phaseKey - The phase key to mark.\n */\n markEmitted(scopeId: string, phaseKey: string): void;\n\n /**\n * Reset a phase so it will be re-synthesized on the next\n * {@link ensurePhases} call. Used for repeating phases (e.g. \"start-step\"\n * resets after \"finish-step\").\n * @param scopeId - The scope (e.g. run ID).\n * @param phaseKey - The phase key to reset.\n */\n resetPhase(scopeId: string, phaseKey: string): void;\n\n /**\n * Remove all tracking state for a scope. Call on run completion\n * (finish, cancel) to free memory.\n * @param scopeId - The scope to clear.\n */\n clearScope(scopeId: string): void;\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD13\nclass DefaultLifecycleTracker<TEvent> implements LifecycleTracker<TEvent> {\n private readonly _phases: PhaseConfig<TEvent>[];\n private readonly _emitted = new Map<string, Set<string>>();\n\n constructor(phases: PhaseConfig<TEvent>[]) {\n this._phases = phases;\n }\n\n ensurePhases(scopeId: string, context: Record<string, string | undefined>): TEvent[] {\n const emitted = this._getOrCreate(scopeId);\n const events: TEvent[] = [];\n for (const phase of this._phases) {\n if (!emitted.has(phase.key)) {\n emitted.add(phase.key);\n events.push(...phase.build(context));\n }\n }\n return events;\n }\n\n markEmitted(scopeId: string, phaseKey: string): void {\n this._getOrCreate(scopeId).add(phaseKey);\n }\n\n resetPhase(scopeId: string, phaseKey: string): void {\n this._emitted.get(scopeId)?.delete(phaseKey);\n }\n\n clearScope(scopeId: string): void {\n this._emitted.delete(scopeId);\n }\n\n private _getOrCreate(scopeId: string): Set<string> {\n let set = this._emitted.get(scopeId);\n if (!set) {\n set = new Set();\n this._emitted.set(scopeId, set);\n }\n return set;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a lifecycle tracker configured with the given phases.\n * Phases are checked and synthesized in array order.\n * @param phases - Ordered phase configurations.\n * @returns A new {@link LifecycleTracker} instance.\n */\nexport const createLifecycleTracker = <TEvent>(phases: PhaseConfig<TEvent>[]): LifecycleTracker<TEvent> =>\n new DefaultLifecycleTracker(phases);\n","/**\n * Typed header-field bindings.\n *\n * A {@link HeaderField} binds a codec header key to its value type **once**,\n * and exposes a symmetric {@link HeaderField.read | read} / {@link\n * HeaderField.write | write} pair over a raw `Record<string, string>` headers\n * record. Because a single binding drives both the encode and decode side, the\n * header key and its value type stay in lockstep across directions — a key\n * cannot be misspelled on one side and silently read as absent on the other.\n *\n * This is deliberately **not** a schema library: it is a thin bidirectional\n * string (de)serializer over the headers record. The SDK ships no hard runtime\n * dependencies, and a schema approach would force re-declaring peer-SDK-owned\n * types. The four constructors cover every header value shape the codecs use:\n *\n * - {@link strField} — string values, optional default.\n * - {@link boolField} — `\"true\"`/`\"false\"` booleans, optional default.\n * - {@link jsonField} — JSON-serialized structured values.\n * - {@link enumField} — string values validated against an allow-list with a\n * fallback (e.g. a finish reason).\n *\n * Passing a default to `strField`/`boolField` makes the field **total**: its\n * `read` returns `V` rather than `V | undefined`, for required headers that\n * should always decode to a concrete value.\n */\n\nimport { parseBool, parseJson } from '../../utils.js';\n\n/**\n * A header key bound to its value type, with symmetric read/write over a raw\n * headers record. Created via {@link strField}, {@link boolField}, {@link\n * jsonField}, or {@link enumField}.\n *\n * The `key` plays a dual role in descriptor `fields` tables: it is the wire\n * header key AND the property name the drivers read off the source object on\n * encode and write back into the rebuilt object on decode. {@link FieldFor}\n * enforces this — a declared field's key must name a real property of the\n * member it lenses onto.\n * @template V - The decoded value type this field reads and writes.\n * @template K - The header key literal (preserved so {@link FieldFor} can match it against the member's property names).\n */\nexport interface HeaderField<V, K extends string = string> {\n /** The raw header key this field reads from and writes to — also the source/rebuilt property name in descriptor tables. */\n readonly key: K;\n /**\n * Read and decode this field's value from a headers record.\n * @param headers - The raw codec headers record to read from.\n * @returns The decoded value. For defaulted/validated fields this is total\n * (the default/fallback is returned when the header is absent or invalid);\n * otherwise `undefined` when the header is absent.\n */\n read(headers: Record<string, string>): V;\n /**\n * Encode and write this field's value into a headers record, mutating it in\n * place. `undefined` (and `null`, for JSON values), and values whose runtime\n * type doesn't match the field, are skipped — the key is left unset rather\n * than written. The parameter is `unknown` (not `V`) so a field keeps `V` in\n * a single covariant position (`read`); this lets heterogeneous fields share a\n * `HeaderField<unknown>[]` array, which the descriptor drivers rely on. At a\n * typed call site the caller still passes a `V`.\n * @param headers - The headers record to mutate.\n * @param value - The value to encode and set.\n */\n write(headers: Record<string, string>, value: unknown): void;\n}\n\n/**\n * Symmetric codec for a descriptor's wire `data`. Many wire payloads are object\n * envelopes a decode reads several chunk props out of (e.g. `{ errorText, input }`),\n * so a single field can't model them. `encode` produces the wire data from the\n * chunk; `decode` returns the chunk props the envelope contributes, merged into\n * the rebuilt chunk by the driver.\n * @template C - The narrowed chunk member.\n */\nexport interface DataCodec<C> {\n /** Produce the wire `data` from the chunk. */\n encode: (chunk: C) => unknown;\n /**\n * Extract the chunk props this envelope contributes from the wire `data`.\n * Undefined-valued props are stripped when the driver rebuilds the object —\n * every rebuild seam (output chunk, input payload, batch part) applies the\n * same rule, since absent and undefined are indistinguishable on the wire.\n */\n decode: (data: unknown) => Partial<C>;\n}\n\n/**\n * The header fields a descriptor may declare against member `C`. For each\n * string-keyed property of `C`, a field is acceptable when its key IS that\n * property name and its value type can hold the property. A mistyped key or a\n * wrong-typed field (e.g. a `boolField` on a string property) is a compile\n * error instead of a silently absent header.\n * @template C - The member (chunk, payload, or part) the fields lens onto.\n */\nexport type FieldFor<C> = {\n [K in keyof C & string]-?: HeaderField<C[K] | undefined, K>;\n}[keyof C & string];\n\n/**\n * Bind a string-valued header field.\n * @param key - The header key (and source property name in descriptor tables).\n * @returns A field whose `read` yields `string | undefined` (absent → `undefined`).\n */\nexport function strField<K extends string>(key: K): HeaderField<string | undefined, K>;\n/**\n * Bind a string-valued header field with a default, making it total.\n * @param key - The header key (and source property name in descriptor tables).\n * @param fallback - Value returned by `read` when the header is absent.\n * @returns A field whose `read` yields `string` (absent → `fallback`).\n */\nexport function strField<K extends string>(key: K, fallback: string): HeaderField<string, K>;\nexport function strField<K extends string>(key: K, fallback?: string): HeaderField<string | undefined, K> {\n return {\n key,\n read: (headers) => headers[key] ?? fallback,\n write: (headers, value) => {\n if (typeof value === 'string') headers[key] = value;\n },\n };\n}\n\n/**\n * Bind a boolean-valued header field, serialized as `\"true\"`/`\"false\"`.\n * @param key - The header key (and source property name in descriptor tables).\n * @returns A field whose `read` yields `boolean | undefined` (absent → `undefined`).\n */\nexport function boolField<K extends string>(key: K): HeaderField<boolean | undefined, K>;\n/**\n * Bind a boolean-valued header field with a default, making it total.\n * @param key - The header key (and source property name in descriptor tables).\n * @param fallback - Value returned by `read` when the header is absent.\n * @returns A field whose `read` yields `boolean` (absent → `fallback`).\n */\nexport function boolField<K extends string>(key: K, fallback: boolean): HeaderField<boolean, K>;\nexport function boolField<K extends string>(key: K, fallback?: boolean): HeaderField<boolean | undefined, K> {\n return {\n key,\n read: (headers) => parseBool(headers[key]) ?? fallback,\n write: (headers, value) => {\n if (typeof value === 'boolean') headers[key] = String(value);\n },\n };\n}\n\n/**\n * Bind a JSON-serialized header field. The value is written with\n * `JSON.stringify` and read back with `JSON.parse`; malformed JSON reads as\n * `undefined`. The decoded shape is a trust boundary — the caller asserts it\n * via the `V` type parameter.\n * @template V - The expected decoded shape of the JSON value.\n * @template K - The header key literal. Inferred when `V` is omitted; pass it explicitly alongside `V` when the field participates in a typed descriptor `fields` table.\n * @param key - The header key (and source property name in descriptor tables).\n * @returns A field whose `read` yields `V | undefined` (absent or malformed → `undefined`).\n */\nexport const jsonField = <V, K extends string = string>(key: K): HeaderField<V | undefined, K> => ({\n key,\n // CAST: header values are wire data parsed via JSON.parse — a trust\n // boundary. The caller declares the expected shape through `V`; malformed\n // JSON reads back as `undefined` (parseJson swallows the parse error).\n read: (headers) => parseJson(headers[key]) as V | undefined,\n write: (headers, value) => {\n // Skip undefined and null so an absent value leaves the key unset rather\n // than serializing to \"null\".\n if (value !== undefined && value !== null) headers[key] = JSON.stringify(value);\n },\n});\n\n/**\n * Bind a string-valued header field validated against a fixed allow-list,\n * falling back to a given value when the header is absent or unrecognized. Use\n * for headers with a small closed set of valid values (e.g. a finish reason).\n * @template T - The union of allowed string literals, inferred from `allowed`.\n * @template K - The header key literal, inferred from `key`.\n * @param key - The header key (and source property name in descriptor tables).\n * @param allowed - The exhaustive list of valid values.\n * @param fallback - Value returned by `read` when the header is absent or not in `allowed`.\n * @returns A total field whose `read` yields one of the allowed literals.\n */\nexport const enumField = <const T extends string, K extends string>(\n key: K,\n allowed: readonly T[],\n fallback: NoInfer<T>,\n): HeaderField<T, K> => ({\n key,\n read: (headers) => {\n const raw = headers[key];\n // find returns the matched literal (typed T) or undefined — no cast needed.\n return allowed.find((candidate) => candidate === raw) ?? fallback;\n },\n write: (headers, value) => {\n if (typeof value === 'string') headers[key] = value;\n },\n});\n","/**\n * Shared header-field bag helpers.\n *\n * The wire dispatch discriminator (`kind`) plus the symmetric field↔headers\n * write and read used by the descriptor drivers. Centralised so encode and\n * decode operate on the same record shape and the dispatch key has one home —\n * and so the input drivers can reuse the same primitives as the output drivers.\n */\n\nimport type { HeaderField } from './fields.js';\nimport type { OutputDescriptor, OutputEventDescriptor } from './output-descriptors.js';\n\n/** The codec header carrying the SDK-controlled dispatch kind / stream family id. */\nexport const KIND_HEADER = 'kind';\n\n/** The sentinel suffix marking a descriptor literal as a wildcard family. */\nconst WILDCARD_SUFFIX = '-*';\n\n/**\n * Derive a wildcard dispatch predicate from a descriptor literal: a literal\n * ending in `-*` matches any value sharing its prefix, so the literal and its\n * predicate can never disagree. Returns `undefined` for an exact literal.\n * Shared by the output event builder and the input part builder so the `-*`\n * sentinel rule lives in one place, next to the {@link partFor} that consumes it.\n * @param literal - The declared descriptor literal (`type` / `partType`).\n * @returns A prefix-match predicate for a wildcard literal, else `undefined`.\n */\nexport const wildcardMatcher = (literal: string): ((value: string) => boolean) | undefined =>\n literal.endsWith(WILDCARD_SUFFIX) ? (value: string): boolean => value.startsWith(literal.slice(0, -1)) : undefined;\n\n/**\n * The codec header carrying a batch part's sub-discriminator. A batch stamps it\n * on every exploded part on encode; the decoder reads it back to resolve the\n * matching part descriptor. Centralised so the key has one home across the\n * input encode and decode drivers and cannot drift between them.\n */\nexport const PART_TYPE_HEADER = 'partType';\n\n/**\n * Read the value at a declared field key off a source object.\n * @param source - The object to index (a chunk, or a lensed sub-object such as a payload).\n * @param key - The declared field key.\n * @returns The value at `key`, typed `unknown`.\n */\n// CAST: a descriptor indexes a source object's props by a declared key. The\n// source's indexed type isn't statically known here, but a descriptor only ever\n// runs against the member it matches, so the value has the field's type at runtime.\nexport const prop = (source: object, key: string): unknown => (source as Record<string, unknown>)[key];\n\n/**\n * Build a codec-headers record from a source object through declared fields,\n * seeded with the dispatch `kind`. Each field writes the value at its key on\n * `source`; an optional `keys` subset restricts which fields are written.\n * @param fields - The declared header fields.\n * @param kindValue - The dispatch kind / stream family id to seed under {@link KIND_HEADER}.\n * @param source - The object to read field values from (a chunk, or a lensed payload).\n * @param keys - Optional subset of field keys to write; omit to write all.\n * @returns The codec-headers record.\n */\nexport const writeFields = (\n fields: readonly HeaderField<unknown>[],\n kindValue: string,\n source: object,\n keys?: readonly string[],\n): Record<string, string> => {\n const rec: Record<string, string> = { [KIND_HEADER]: kindValue };\n for (const field of fields) {\n if (keys && !keys.includes(field.key)) continue;\n field.write(rec, prop(source, field.key));\n }\n return rec;\n};\n\n/**\n * Read declared fields out of a codec-headers record into a bag keyed by field key.\n * A field that reads `undefined` (absent, with no default) contributes no key — the\n * bag carries only the values that are actually present.\n * @param fields - The declared header fields.\n * @param headers - The inbound codec-tier headers.\n * @returns A bag of the present field values, keyed by each field's key.\n */\n/** The structural slice of a part descriptor {@link partFor} dispatches on. */\ninterface PartDispatch {\n /** The exact `partType` literal, or the `-*` wildcard literal for a family. */\n partType: string;\n /** Wildcard dispatch predicate; absent for an exact part. */\n match?: (partType: string) => boolean;\n}\n\n/**\n * Resolve the part descriptor for a `partType`: an exact non-wildcard match,\n * else a wildcard whose derived predicate accepts it. Wildcards are excluded\n * from the exact pass — only their predicate may route to them. Shared by the\n * input encode and decode drivers.\n * @param parts - The batch's part descriptor sub-table.\n * @param partType - The `partType` to resolve (from `partTypeOf` on encode, the wire header on decode).\n * @returns The matching part descriptor, or undefined when none matches.\n */\nexport const partFor = <P extends PartDispatch>(parts: readonly P[], partType: string): P | undefined =>\n parts.find((part) => !part.match && part.partType === partType) ?? parts.find((part) => part.match?.(partType));\n\n/** An output descriptor set's event descriptors, split for dispatch. */\nexport interface OutputEventDispatch<U> {\n /** Exact (non-wildcard) event descriptors, keyed by `type`. */\n discreteByType: Map<string, OutputEventDescriptor<U>>;\n /** Wildcard event descriptors, dispatched by their `match` predicate. */\n wildcards: OutputEventDescriptor<U>[];\n}\n\n/**\n * Partition an output descriptor set's `event` descriptors into an exact-type\n * map and a wildcard list. Stream descriptors are skipped — each driver indexes\n * those by its own key (phase on encode, kind on decode). Shared by the output\n * encode and decode drivers so the exact-vs-wildcard split has one home.\n * @template U - The codec's event union.\n * @param descriptors - The full descriptor set (events + streamed families).\n * @returns The event descriptors split into {@link OutputEventDispatch}.\n */\nexport const partitionOutputEvents = <U extends { type: string }>(\n descriptors: readonly OutputDescriptor<U>[],\n): OutputEventDispatch<U> => {\n const discreteByType = new Map<string, OutputEventDescriptor<U>>();\n const wildcards: OutputEventDescriptor<U>[] = [];\n for (const descriptor of descriptors) {\n if (descriptor.construct !== 'event') continue;\n if (descriptor.match) wildcards.push(descriptor);\n else discreteByType.set(descriptor.type, descriptor);\n }\n return { discreteByType, wildcards };\n};\n\nexport const readFields = (\n fields: readonly HeaderField<unknown>[],\n headers: Record<string, string>,\n): Record<string, unknown> => {\n const bag: Record<string, unknown> = {};\n for (const field of fields) {\n const value = field.read(headers);\n if (value !== undefined) bag[field.key] = value;\n }\n return bag;\n};\n","/**\n * Generic input decode driver over an input descriptor set — the input-side\n * sibling of {@link import('./output-descriptor-decoder.js')}.\n *\n * Rebuilds inputs from one inbound `ai-input` message, dispatching on the codec\n * `kind` header. A single `event` rebuilds its field bag (and `data`) and wraps\n * it into the `{ kind, codecMessageId, payload }` envelope; `wireOnly` events\n * decode to `[]`. A\n * `batch` reads the `partType` sub-discriminator, rebuilds the part via its\n * sub-table, `assemble`s it into a one-part input, and the driver stamps the\n * `kind` plus the codec-message-id reconstructed from the transport header.\n *\n * Returns bare `TInput[]`, never `CodecEvent[]` — direction tagging is\n * core-owned, downstream at the decode→fold seam.\n */\n\nimport { HEADER_CODEC_MESSAGE_ID } from '../../constants.js';\nimport { stripUndefined } from '../../utils.js';\nimport { PART_TYPE_HEADER, partFor, readFields } from './field-bag.js';\nimport type {\n BatchDescriptor,\n InputDecodeContext,\n InputDescriptor,\n InputEventDescriptor,\n} from './input-descriptors.js';\n\n/** Decodes inbound `ai-input` messages of union `U` from an input descriptor set. */\nexport interface InputDescriptorDecoder<U> {\n /**\n * Rebuild zero or more inputs from one inbound `ai-input` message.\n * @param ctx - The inbound message context (codec kind, data, header tiers).\n * @returns The decoded inputs (empty when no descriptor matches or the input is wire-only).\n */\n decode(ctx: InputDecodeContext): U[];\n}\n\n/**\n * Build an input decode driver for an input descriptor set.\n * @template U - The codec's input union.\n * @param descriptors - The input descriptor set (events + batches).\n * @returns An {@link InputDescriptorDecoder} that reconstructs inputs from the wire.\n */\nexport const createInputDescriptorDecoder = <U extends { kind: string }>(\n descriptors: readonly InputDescriptor<U>[],\n): InputDescriptorDecoder<U> => {\n const byKind = new Map<string, InputDescriptor<U>>();\n for (const descriptor of descriptors) byKind.set(descriptor.kind, descriptor);\n\n const decodeEvent = (descriptor: InputEventDescriptor, ctx: InputDecodeContext): U[] => {\n if (descriptor.wireOnly) return [];\n\n const bag = readFields(descriptor.fields, ctx.codecHeaders);\n if (descriptor.data) Object.assign(bag, descriptor.data.decode(ctx.data));\n\n const codecMessageId = ctx.transportHeaders[HEADER_CODEC_MESSAGE_ID] ?? '';\n // The payload bag is stripped of undefined-valued props — the same rule\n // every rebuild seam applies to its innermost bag (absent and undefined\n // are indistinguishable on the wire). The envelope keys are always defined.\n // CAST: the rebuild seam — `bag` is assembled from the descriptor's declared\n // fields and data codec onto the payload, so the `{ kind, codecMessageId, payload }`\n // envelope conforms to the matched member by construction.\n return [{ kind: descriptor.kind, codecMessageId, payload: stripUndefined(bag) } as unknown as U];\n };\n\n const decodeBatch = (descriptor: BatchDescriptor<U>, ctx: InputDecodeContext): U[] => {\n const partType = ctx.codecHeaders[PART_TYPE_HEADER] ?? '';\n const partDesc = partFor(descriptor.parts, partType);\n if (!partDesc) return [];\n\n const bag = readFields(partDesc.fields, ctx.codecHeaders);\n if (partDesc.data) Object.assign(bag, partDesc.data.decode(ctx.data));\n bag.type = partType;\n\n // `assemble` takes the erased part (`unknown`); `bag` is the part rebuilt from\n // its declared fields/data plus the `partType` written to the domain `type`\n // field. The header tiers carry the per-message metadata (id, role, …) the\n // batch stamped on every part, so `assemble` can rebuild the message envelope.\n const partial = descriptor.assemble(stripUndefined(bag), {\n codecHeaders: ctx.codecHeaders,\n transportHeaders: ctx.transportHeaders,\n });\n // CAST: the driver stamps the shared `kind` onto the assembled one-part input; together\n // they complete the matched member. A batch creates a new message (not addressed by a\n // codec-message-id, unlike single `event`s), so none is stamped — the per-message\n // identity rides the transport header and is recovered by `assemble` when needed.\n return [{ kind: descriptor.kind, ...partial } as unknown as U];\n };\n\n return {\n decode: (ctx) => {\n const descriptor = byKind.get(ctx.codecKind);\n if (!descriptor) return [];\n if (descriptor.construct === 'event') return decodeEvent(descriptor, ctx);\n return decodeBatch(descriptor, ctx);\n },\n };\n};\n","/**\n * Generic input encode driver over an input descriptor set — the input-side\n * sibling of {@link import('./output-descriptor-encoder.js')}.\n *\n * Builds a `kind`→descriptor registry once, then routes each input: a single\n * `event` publishes one discrete message (fields/data lensed onto the member's\n * `payload`, or kind-only when `wireOnly`); a `batch` explodes the domain\n * message into one wire event per part and publishes them atomically, with a\n * built-in ≥1-event guarantee so the codec-message-id and role survive an empty\n * decomposition. Headers are always built through the descriptor's declared\n * fields ({@link writeFields}), so the imperative paths can't drift.\n */\n\nimport * as Ably from 'ably';\n\nimport { ErrorCode } from '../../errors.js';\nimport { KIND_HEADER, PART_TYPE_HEADER, partFor, prop, writeFields } from './field-bag.js';\nimport type {\n BatchDescriptor,\n BatchMessageHeaders,\n InputDescriptor,\n InputEncodeContext,\n InputEncoderCore,\n InputEventDescriptor,\n} from './input-descriptors.js';\nimport type { MessagePayload } from './types.js';\n\n/** Encodes inputs of union `U` to channel operations via an input descriptor set. */\nexport interface InputDescriptorEncoder<U> {\n /**\n * Encode one input through its descriptor.\n * @param input - The input to encode.\n * @param core - The input encoder core to publish through.\n * @param ctx - Per-write context (write options, carrying the codec-message-id).\n * @returns A promise resolving when the publish operation completes.\n */\n encode(input: U, core: InputEncoderCore, ctx: InputEncodeContext): Promise<void>;\n}\n\n// Layer the batch's per-message transport headers onto a part payload, if any.\nconst withMessageTransport = (payload: MessagePayload, message: BatchMessageHeaders | undefined): MessagePayload =>\n message?.transportHeaders === undefined\n ? payload\n : { ...payload, transportHeaders: { ...payload.transportHeaders, ...message.transportHeaders } };\n\n/**\n * Build an input encode driver for an input descriptor set bound to a wire name.\n * @template U - The codec's input union.\n * @param descriptors - The input descriptor set (events + batches).\n * @param wireName - The Ably message name for the input direction (`ai-input`).\n * @returns An {@link InputDescriptorEncoder} routing each input through its descriptor.\n */\nexport const createInputDescriptorEncoder = <U extends { kind: string }>(\n descriptors: readonly InputDescriptor<U>[],\n wireName: string,\n): InputDescriptorEncoder<U> => {\n const byKind = new Map<string, InputDescriptor<U>>();\n for (const descriptor of descriptors) byKind.set(descriptor.kind, descriptor);\n\n const encodeEvent = async (\n descriptor: InputEventDescriptor,\n input: U,\n core: InputEncoderCore,\n ctx: InputEncodeContext,\n ): Promise<void> => {\n if (descriptor.wireOnly) {\n // Kind only: no fields, no data — the parent/target ride transport headers.\n await core.publishDiscrete(\n { name: wireName, data: '', codecHeaders: { [KIND_HEADER]: descriptor.kind } },\n ctx.opts,\n );\n return;\n }\n // A non-wireOnly input nests its domain data under `payload` — fields and\n // data are authored against it. Fail fast on a payload-less member instead\n // of silently publishing empty data the decoder can't rebuild from.\n const source = prop(input, 'payload');\n if (typeof source !== 'object' || source === null) {\n throw new Ably.ErrorInfo(\n `unable to encode input; event '${descriptor.kind}' carries no payload object — declare it wireOnly or use an encode escape hatch`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const codecHeaders = writeFields(descriptor.fields, descriptor.kind, source);\n const data = descriptor.data ? descriptor.data.encode(source) : '';\n await core.publishDiscrete({ name: wireName, data, codecHeaders }, ctx.opts);\n };\n\n const encodeBatch = async (\n descriptor: BatchDescriptor<U>,\n input: U,\n core: InputEncoderCore,\n ctx: InputEncodeContext,\n ): Promise<void> => {\n // Per-message headers (e.g. message id, role) are stamped on every part so\n // the decode side can reconstruct the shared message envelope from any one.\n const message = descriptor.messageHeaders?.(input);\n const payloads: MessagePayload[] = [];\n for (const part of descriptor.explode(input)) {\n const partType = descriptor.partTypeOf(part);\n const partDesc = partFor(descriptor.parts, partType);\n if (!partDesc) continue;\n // CAST: a part is indexed by its declared fields; the part descriptor only\n // runs against the part its predicate/literal matched, so the source has the\n // field's type at runtime. The wire `partType` is the resolved part type.\n const source = part as object;\n const codecHeaders = {\n ...writeFields(partDesc.fields, descriptor.kind, source),\n ...message?.codecHeaders,\n [PART_TYPE_HEADER]: partType,\n };\n const data = partDesc.data ? partDesc.data.encode(part) : '';\n payloads.push(withMessageTransport({ name: wireName, data, codecHeaders }, message));\n }\n\n if (payloads.length === 0) {\n // ≥1-event guarantee: emit one bare part so the per-message headers (e.g.\n // the message id and role) reach the wire even when no exploded part\n // matched a descriptor. This fallback carries no partType, so the batch\n // decode path yields no input for it — a codec that needs an empty\n // message to round-trip must guarantee ≥1 encodable part in `explode`\n // (e.g. by substituting a canonical empty part).\n payloads.push(\n withMessageTransport(\n { name: wireName, data: '', codecHeaders: { [KIND_HEADER]: descriptor.kind, ...message?.codecHeaders } },\n message,\n ),\n );\n }\n\n await core.publishDiscreteBatch(payloads, ctx.opts);\n };\n\n return {\n encode: async (input, core, ctx) => {\n const descriptor = byKind.get(input.kind);\n if (!descriptor) {\n throw new Ably.ErrorInfo(\n `unable to publish; unsupported input kind '${input.kind}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n await (descriptor.construct === 'event'\n ? encodeEvent(descriptor, input, core, ctx)\n : encodeBatch(descriptor, input, core, ctx));\n },\n };\n};\n","/**\n * Declarative input descriptors — the single source of truth for a codec's\n * `ai-input` wire mapping, the input-side sibling of {@link import('./output-descriptors.js')}.\n *\n * Inputs come in two cardinalities: a single domain input ↔ one wire message\n * (the `event` construct), and a single domain message ↔ many atomic wire events\n * (the `batch` construct — the input sibling of the output `stream`). Both are\n * declared once per codec and consumed by the generic input encode/decode\n * drivers, so adding an input is one descriptor entry rather than a pair of\n * hand-synchronised switch arms.\n *\n * Authoring is cast-free: the {@link inputBuilder} factory hands the codec a\n * `{ event, batch }` pair curried on the codec's input union, so every `data` /\n * `fields` / `parts` callback receives the exact narrowed member. The descriptors\n * are then erased to a heterogeneous {@link InputDescriptor} via a single\n * documented cast at each constructor boundary — never in author code.\n */\n\nimport type * as Ably from 'ably';\n\nimport { wildcardMatcher } from './field-bag.js';\nimport type { DataCodec, FieldFor, HeaderField } from './fields.js';\nimport type { MessagePayload, WriteOptions } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Type helpers\n// ---------------------------------------------------------------------------\n\n/** Resolve the input union member a `kind` literal selects. */\nexport type ResolveInput<U extends { kind: string }, K extends U['kind']> = Extract<U, { kind: K }>;\n\n/**\n * The payload an input `event`'s `fields` / `data` operate on. Inputs nest their\n * domain data under `payload` (the `{ kind, codecMessageId, payload }` envelope of\n * the well-known input variants), so a single event's spec is authored against the\n * payload, and the driver wraps/unwraps the envelope. A member with no `payload`\n * (a `wireOnly` signal) resolves to `never` — such an event declares no `fields` /\n * `data`, so the payload type is never used.\n * @template C - The narrowed input member.\n */\nexport type PayloadOf<C> = C extends { payload: infer P } ? P : never;\n\n/**\n * Resolve the part union member a `partType` literal selects, mirroring the\n * output {@link import('./output-descriptors.js').ResolveType} curry one level down.\n * An exact match wins; a wildcard literal (`data-*`) resolves to the template\n * member (`data-${string}`).\n * @template P - The part union.\n * @template T - The selected `partType` literal (or a `*-*` wildcard).\n */\nexport type ResolvePart<P extends { type: string }, T extends string> =\n Extract<P, { type: T }> extends never\n ? T extends `${infer Pre}-*`\n ? Extract<P, { type: `${Pre}-${string}` }>\n : never\n : Extract<P, { type: T }>;\n\n// ---------------------------------------------------------------------------\n// Author-facing specs (narrowed)\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Input driver core surface + contexts\n// ---------------------------------------------------------------------------\n\n/**\n * The encoder-core view the input encode driver receives: discrete publishes\n * only — inputs never stream. The concrete {@link EncoderCore} satisfies this\n * structurally.\n */\nexport interface InputEncoderCore {\n /** Publish a single discrete message. */\n publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult>;\n /** Publish multiple discrete messages atomically (the batch fan-out). */\n publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult>;\n}\n\n/** Per-write context passed to the input encode driver. */\nexport interface InputEncodeContext {\n /** Per-write overrides (the wire codec-message-id is stamped here by the client session). */\n opts: WriteOptions | undefined;\n}\n\n/** Per-message context the input decode driver receives for one inbound `ai-input` message. */\nexport interface InputDecodeContext {\n /** The codec `kind` header value (the input descriptor's dispatch key). */\n codecKind: string;\n /** The inbound message data. */\n data: unknown;\n /** The inbound codec-tier headers. */\n codecHeaders: Record<string, string>;\n /** The inbound transport-tier headers (role, codec-message-id, discrete marker). */\n transportHeaders: Record<string, string>;\n}\n\n/**\n * The spec the input `event` construct accepts for member `C`. A member with\n * no `payload` has nothing for `fields` / `data` to lens onto, so it may only\n * be declared `wireOnly` or escape-hatched; the driver also rejects a\n * payload-less encode at runtime.\n * @template C - The narrowed input member.\n */\nexport type InputEventSpecFor<C> = [PayloadOf<C>] extends [never]\n ? Pick<InputEventSpec<C>, 'wireOnly'>\n : InputEventSpec<C>;\n\n/**\n * A single-event input descriptor spec, narrowed to input member `C`. `fields`\n * and `data` operate on the member's {@link PayloadOf payload}; the driver wraps\n * the `{ kind, codecMessageId, payload }` envelope on decode and unwraps it on\n * encode. A `wireOnly` event carries no payload (kind only).\n * @template C - The narrowed input member.\n */\nexport interface InputEventSpec<C> {\n /**\n * Declared header fields over the member's payload, written on encode and\n * read on decode. Each field's key names both the wire header and the\n * payload property it carries (see {@link FieldFor}). Omit for none.\n */\n fields?: readonly FieldFor<PayloadOf<C>>[];\n /** Wire `data` codec over the payload. Omit when the input carries no data (`data: ''`). */\n data?: DataCodec<PayloadOf<C>>;\n /** Wire-only signal: encode stamps only the `kind` header (empty data, no fields); decode yields `[]`. */\n wireOnly?: boolean;\n}\n\n/**\n * A per-part wire mapping inside a {@link BatchSpec}, narrowed to part member `Q`.\n * `fields` and `data` operate on the selected part; the batch driver fans the\n * domain message out into one wire event per part and reassembles them in the\n * reducer (merge by codec-message-id).\n * @template Q - The narrowed part member.\n */\nexport interface PartSpec<Q> {\n /**\n * Declared header fields for this part, written on encode and read on\n * decode. Each field's key names both the wire header and the part property\n * it carries (see {@link FieldFor}). Omit for none.\n */\n fields?: readonly FieldFor<Q>[];\n /** Wire `data` codec over the part. Omit when the part carries no data. */\n data?: DataCodec<Q>;\n}\n\n/**\n * The curried part sub-builder a {@link BatchSpec.parts} function receives.\n * Mirrors the {@link inputBuilder} `event` curry one level down — and the\n * output builder's wildcard idiom: `p(partType, spec)` narrows `spec` to the\n * part member the literal selects, and a `-*` literal (e.g. `data-*`) declares\n * a wildcard family whose dispatch predicate is derived from the literal's\n * prefix, narrowing `spec` to the template member. Both forms are cast-free in\n * author code.\n * @template P - The part union.\n */\nexport type PartBuilder<P extends { type: string }> = <T extends P['type'] | `${string}-*`>(\n partType: T,\n spec: PartSpec<ResolvePart<P, T>>,\n) => PartDescriptor;\n\n/**\n * Per-message wire headers a {@link BatchSpec.messageHeaders} stamps on every\n * part of one batch. These carry metadata that belongs to the whole message\n * rather than an individual part (e.g. the message id as a codec header, the\n * sender role as a transport header), so the decode side can reconstruct the\n * shared message envelope from any single part. Both tiers are optional.\n */\nexport interface BatchMessageHeaders {\n /** Codec-tier headers stamped on every part (e.g. a per-message id). */\n codecHeaders?: Record<string, string>;\n /** Transport-tier headers stamped on every part (e.g. the sender role). */\n transportHeaders?: Record<string, string>;\n}\n\n/**\n * The context a {@link BatchSpec.assemble} receives alongside one decoded part:\n * the inbound header tiers of the wire event the part was decoded from. A batch\n * fans out into N independent wire events, so each part arrives carrying the\n * shared per-message headers ({@link BatchMessageHeaders}); `assemble` reads them\n * to rebuild the message envelope (id, role, …) around its one part.\n */\nexport interface BatchAssembleContext {\n /** The inbound codec-tier headers (carries the per-message codec headers). */\n codecHeaders: Record<string, string>;\n /** The inbound transport-tier headers (carries the per-message transport headers). */\n transportHeaders: Record<string, string>;\n}\n\n/**\n * A multi-part input descriptor spec: one domain message decomposed into many\n * atomic wire events sharing the input member's `kind` and codec-message-id, each\n * carrying a `partType` sub-discriminator. The part union `P` is inferred from\n * `explode`'s return type and threaded into `parts`'s curried `p` and `assemble`,\n * so all three are cast-free in author code.\n * @template C - The narrowed input member (the message-bearing input).\n * @template P - The part union the message explodes into.\n */\nexport interface BatchSpec<C, P extends { type: string }> {\n /** ENCODE: decompose the domain message into its parts. */\n explode: (input: C) => readonly P[];\n /** The `partType` sub-discriminator read off each part on encode. */\n partTypeOf: (part: P) => string;\n /** Declarative per-part wire mapping (a sub-table built via the curried `p`). */\n parts: (p: PartBuilder<P>) => readonly PartDescriptor[];\n /**\n * ENCODE: per-message headers stamped on every part (the driver merges them\n * onto each part's headers, including the ≥1-event fallback). Use for metadata\n * shared by the whole message — e.g. a message-id codec header and a role\n * transport header. Omit when parts carry no shared per-message metadata.\n */\n messageHeaders?: (input: C) => BatchMessageHeaders;\n /**\n * DECODE: shape one decoded wire part into a one-part input (the reducer merges\n * parts by codec-message-id). `ctx` exposes the inbound header tiers so the\n * shared per-message metadata stamped by `messageHeaders` can be read back.\n * The driver stamps only `kind`; the per-message identity rides the\n * transport header and is recovered through `ctx` when needed.\n */\n assemble: (part: P, ctx: BatchAssembleContext) => Omit<C, 'kind'>;\n}\n\n// ---------------------------------------------------------------------------\n// Erased descriptors (heterogeneous array elements)\n// ---------------------------------------------------------------------------\n\n/** A single-event input descriptor erased to the codec's input union `U`. */\nexport interface InputEventDescriptor {\n /** Discriminator. */\n construct: 'event';\n /** The wire `kind` this input dispatches on. */\n kind: string;\n /** Declared header fields (read/written against the member's payload). */\n fields: readonly HeaderField<unknown>[];\n /** Wire `data` codec, if any. */\n data?: DataCodec<unknown>;\n /** Wire-only signal flag. */\n wireOnly: boolean;\n}\n\n/** An erased per-part wire mapping within a {@link BatchDescriptor}. */\nexport interface PartDescriptor {\n /** The exact `partType` this part encodes as (the wildcard sentinel for a family). */\n partType: string;\n /** Decode-dispatch predicate for a wildcard part; absent for an exact part. */\n match?: (partType: string) => boolean;\n /** Declared header fields for this part. */\n fields: readonly HeaderField<unknown>[];\n /** Wire `data` codec over the part, if any. */\n data?: DataCodec<unknown>;\n}\n\n/** A multi-part (batch) input descriptor erased to the codec's input union `U`. */\nexport interface BatchDescriptor<U> {\n /** Discriminator. */\n construct: 'batch';\n /** The wire `kind` every part of this batch shares. */\n kind: string;\n /** Decompose the domain input into its parts. */\n explode: (input: U) => readonly unknown[];\n /** Read the `partType` sub-discriminator off a part. */\n partTypeOf: (part: unknown) => string;\n /** The per-part wire mappings. */\n parts: readonly PartDescriptor[];\n /** Build the per-message headers stamped on every part, if any. */\n messageHeaders?: (input: U) => BatchMessageHeaders;\n /** Shape one decoded part into a one-part input (sans the driver-stamped `kind`). */\n assemble: (part: unknown, ctx: BatchAssembleContext) => Omit<U, 'kind'>;\n}\n\n/** An erased input descriptor — a single event or a multi-part batch. */\nexport type InputDescriptor<U> = InputEventDescriptor | BatchDescriptor<U>;\n\n// ---------------------------------------------------------------------------\n// Builder factory\n// ---------------------------------------------------------------------------\n\n/**\n * The direction-scoped input builder `defineCodec` injects into the `input`\n * config function — `event` and `batch`, both curried on the codec's input union\n * so author entries narrow cast-free.\n * @template U - The codec's input union.\n */\nexport interface InputBuilder<U extends { kind: string }> {\n /**\n * Declare a single-event input. Narrows `spec` to the member `kind` selects;\n * `fields` / `data` operate on that member's payload.\n * @param kind - The input member's `kind` literal (the wire dispatch key).\n * @param spec - The narrowed input spec. Omit for a bare-`kind` input.\n * @returns An erased {@link InputDescriptor}.\n */\n event: <K extends U['kind']>(kind: K, spec?: InputEventSpecFor<ResolveInput<U, K>>) => InputDescriptor<U>;\n /**\n * Declare a multi-part (batch) input. Narrows the spec to the message-bearing\n * member `kind` selects; `explode`'s return type fixes the part union `P`, which\n * threads into `parts`'s curried `p` and `assemble` cast-free.\n * @param kind - The input member's `kind` literal (the shared wire dispatch key).\n * @param spec - The narrowed batch spec.\n * @returns An erased {@link InputDescriptor}.\n */\n batch: <K extends U['kind'], P extends { type: string }>(\n kind: K,\n spec: BatchSpec<ResolveInput<U, K>, P>,\n ) => InputDescriptor<U>;\n}\n\n/**\n * Build the curried `{ event, batch }` input builder for a codec's input union.\n * `defineCodec` calls this once and hands the result to the `input` config\n * function; mirrors the output side's {@link import('./output-descriptors.js').outputBuilder}.\n * @template U - The codec's input union.\n * @returns The direction-scoped {@link InputBuilder}.\n */\nexport const inputBuilder = <U extends { kind: string }>(): InputBuilder<U> => {\n // The internal part sub-builder reads only the structural `fields`/`data` off the\n // spec; the narrowed part type is an author-facing concern, erased here.\n interface ErasedPartSpec {\n fields?: readonly HeaderField<unknown>[];\n data?: DataCodec<unknown>;\n }\n const part = (partType: string, spec: ErasedPartSpec): PartDescriptor => {\n // A `-*` literal declares a wildcard family; the dispatch predicate is\n // derived from the literal so the two can never disagree (see wildcardMatcher).\n const match = wildcardMatcher(partType);\n return {\n partType,\n ...(match ? { match } : {}),\n fields: spec.fields ?? [],\n data: spec.data,\n };\n };\n // CAST: the part sub-builder is exposed to authors narrowed (PartBuilder<P>) so\n // each `p(partType, spec)` narrows its spec to the selected part. Internally it\n // reads only the structural `fields`/`data`, so the narrowed specs erase to the\n // structural `ErasedPartSpec` at this boundary; a descriptor's part callbacks\n // only ever run against the part their literal/predicate matched.\n const p = part as unknown as PartBuilder<{ type: string }>;\n\n return {\n event: (kind, spec) => {\n // CAST: the author-facing spec is conditional (a payload-less member may\n // only declare wireOnly / escape hatches); both branches erase to one\n // structural bag here, and the impl only reads optional properties off it.\n const bag = spec as InputEventSpec<{ kind: string; payload: unknown }> | undefined;\n // CAST: `spec` is narrowed to the member `kind` selects; the descriptor erases\n // that to the codec's union `U` so heterogeneous descriptors share one array\n // type. The drivers only ever invoke a descriptor's callbacks with the matching\n // member, so the erasure is sound.\n return {\n construct: 'event',\n kind,\n fields: bag?.fields ?? [],\n data: bag?.data,\n wireOnly: bag?.wireOnly ?? false,\n } as unknown as InputDescriptor<U>;\n },\n batch: (kind, spec) => {\n // CAST: `p` is the single structural sub-builder; the author's `parts`\n // function is typed to the narrowed `PartBuilder<P>`, so we hand it `p`\n // through the same erasure the part specs already cross.\n const parts = (spec.parts as (b: PartBuilder<{ type: string }>) => readonly PartDescriptor[])(p);\n // CAST: see `event` — the narrowed batch spec (with its part-union-typed\n // `explode`/`partTypeOf`/`assemble`) erases to `BatchDescriptor<U>`.\n return {\n construct: 'batch',\n kind,\n explode: spec.explode,\n partTypeOf: spec.partTypeOf,\n parts,\n messageHeaders: spec.messageHeaders,\n assemble: spec.assemble,\n } as unknown as InputDescriptor<U>;\n },\n };\n};\n","/**\n * Generic output decode driver over a descriptor set.\n *\n * Rebuilds events from the wire: discrete messages dispatch on the codec `kind`\n * header, streamed families rebuild start/delta/end chunks from the stream\n * tracker. Escape-hatch `decode`/`decodeEnd`/`decodeDiscrete` functions take\n * over where a pure field rebuild can't express the mapping.\n *\n * This driver is pure chunk reconstruction — it carries no decode-time side\n * effects (e.g. a codec's stream-join lifecycle repair). Those belong in the\n * codec's hook layer wrapping this driver.\n */\n\nimport { stripUndefined } from '../../utils.js';\nimport { KIND_HEADER, partitionOutputEvents, readFields } from './field-bag.js';\nimport type {\n OutputDecodeContext,\n OutputDescriptor,\n OutputEventDescriptor,\n OutputStreamDescriptor,\n} from './output-descriptors.js';\nimport type { StreamTrackerState } from './types.js';\n\n/**\n * The reconstructed chunk's domain discriminator field — the codec model's own\n * `type` discriminator, per `CodecOutputEvent.type`. Distinct\n * from {@link KIND_HEADER}: this is the rebuilt object's property, never the\n * wire dispatch key.\n */\nconst TYPE_FIELD = 'type';\n\n/** Decodes wire messages of union `U` from a descriptor set. */\nexport interface OutputDescriptorDecoder<U> {\n /** Rebuild the chunk(s) emitted when a stream starts. */\n buildStart(tracker: StreamTrackerState): U[];\n /** Rebuild the chunk(s) for a stream delta. */\n buildDelta(tracker: StreamTrackerState, delta: string): U[];\n /** Rebuild the chunk(s) emitted when a stream completes. */\n buildEnd(tracker: StreamTrackerState, closingCodecHeaders: Record<string, string>): U[];\n /**\n * Decode a discrete message by its codec `kind`.\n * @param codecKind - The codec `kind` header value (the dispatch key).\n * @param codecHeaders - The inbound codec-tier headers.\n * @param transportHeaders - The inbound transport-tier headers.\n * @param data - The inbound message data.\n * @returns The decoded events (empty if no descriptor matches).\n */\n decodeDiscrete(\n codecKind: string,\n codecHeaders: Record<string, string>,\n transportHeaders: Record<string, string>,\n data: unknown,\n ): U[];\n}\n\n/**\n * Build an output decode driver for a descriptor set.\n * @template U - The codec's event union.\n * @param descriptors - The descriptor set (events + streamed families).\n * @returns An {@link OutputDescriptorDecoder} that reconstructs events from the wire.\n */\nexport const createOutputDescriptorDecoder = <U extends { type: string }>(\n descriptors: readonly OutputDescriptor<U>[],\n): OutputDescriptorDecoder<U> => {\n const { discreteByType, wildcards } = partitionOutputEvents(descriptors);\n const streamByKind = new Map<string, OutputStreamDescriptor<U>>();\n\n for (const descriptor of descriptors) {\n if (descriptor.construct === 'stream') {\n streamByKind.set(descriptor.kind, descriptor);\n }\n }\n\n // CAST: the rebuild seam — `bag` is assembled from the descriptor's declared\n // fields and data codec, so it conforms to the matched member by construction.\n // `typeValue` is the descriptor identity, written to the chunk's domain `type`\n // field (not the wire `kind` header).\n const rebuild = (typeValue: string, bag: Record<string, unknown>): U => {\n bag[TYPE_FIELD] = typeValue;\n return stripUndefined(bag) as unknown as U;\n };\n\n const decodeEvent = (descriptor: OutputEventDescriptor<U>, codecKind: string, ctx: OutputDecodeContext): U[] => {\n const bag = readFields(descriptor.fields, ctx.codecHeaders);\n if (descriptor.data) Object.assign(bag, descriptor.data.decode(ctx.data));\n return [rebuild(codecKind, bag)];\n };\n\n // Resolve the stream family from the tracker's `kind` header. An unrecognized\n // family yields no descriptor, and the build* hooks return no events —\n // unreachable in practice, since a tracker only exists because the encoder\n // started a stream stamping a known family id.\n const familyOf = (tracker: StreamTrackerState): OutputStreamDescriptor<U> | undefined =>\n streamByKind.get(tracker.codecHeaders[KIND_HEADER] ?? '');\n\n return {\n buildStart: (tracker) => {\n const desc = familyOf(tracker);\n if (!desc) return [];\n const bag = readFields(desc.fields, tracker.codecHeaders);\n bag[desc.idField] = tracker.streamId;\n return [rebuild(desc.start, bag)];\n },\n\n buildDelta: (tracker, delta) => {\n const desc = familyOf(tracker);\n if (!desc) return [];\n const bag: Record<string, unknown> = { [desc.idField]: tracker.streamId, [desc.deltaField]: delta };\n return [rebuild(desc.delta, bag)];\n },\n\n buildEnd: (tracker, closingCodecHeaders) => {\n const desc = familyOf(tracker);\n if (!desc) return [];\n if (desc.decodeEnd) {\n return desc.decodeEnd({\n streamId: tracker.streamId,\n accumulated: tracker.accumulated,\n codecHeaders: tracker.codecHeaders,\n closingCodecHeaders,\n });\n }\n const bag = readFields(desc.fields, closingCodecHeaders);\n bag[desc.idField] = tracker.streamId;\n return [rebuild(desc.end, bag)];\n },\n\n decodeDiscrete: (codecKind, codecHeaders, transportHeaders, data) => {\n const ctx: OutputDecodeContext = { codecKind, codecHeaders, transportHeaders, data };\n const evt = discreteByType.get(codecKind);\n if (evt) return decodeEvent(evt, codecKind, ctx);\n const streamDesc = streamByKind.get(codecKind);\n if (streamDesc?.decodeDiscrete) return streamDesc.decodeDiscrete(ctx);\n const wildcard = wildcards.find((w) => w.match?.(codecKind));\n if (wildcard) return decodeEvent(wildcard, codecKind, ctx);\n return [];\n },\n };\n};\n","/**\n * Generic output encode driver over a descriptor set.\n *\n * Builds a chunk→descriptor registry once, then routes each event: discrete\n * descriptors publish a single message, streamed families drive\n * start/append/close, and escape-hatch `encode` functions take over entirely.\n * Headers are always built through the descriptor's declared fields (the `h`\n * builder), so the imperative paths can't drift from the declarative ones.\n */\n\nimport * as Ably from 'ably';\n\nimport { ErrorCode } from '../../errors.js';\nimport type { EncoderCore } from './encoder.js';\nimport { partitionOutputEvents, prop, writeFields } from './field-bag.js';\nimport type { HeaderBuilder, OutputDescriptor, OutputStreamDescriptor } from './output-descriptors.js';\nimport type { WriteOptions } from './types.js';\n\n/** Per-write output encode context threaded from the encoder. */\nexport interface OutputEncodeContext {\n /** The encoder's configured fallback message id, if any. */\n messageId: string | undefined;\n /** Per-write overrides. */\n opts: WriteOptions | undefined;\n}\n\n/** Encodes events of union `U` to channel operations via a descriptor set. */\nexport interface OutputDescriptorEncoder<U> {\n /**\n * Encode one event through its descriptor.\n * @param chunk - The event to encode.\n * @param core - The encoder core to publish/stream through.\n * @param ctx - Per-write context (fallback message id, write options).\n * @returns A promise resolving when the publish/stream operation completes.\n */\n encode(chunk: U, core: EncoderCore, ctx: OutputEncodeContext): Promise<void>;\n}\n\n/**\n * Build an output encode driver for a descriptor set bound to a wire message name.\n * @template U - The codec's event union.\n * @param descriptors - The descriptor set (events + streamed families).\n * @param wireName - The Ably message name for this direction (`ai-output` / `ai-input`).\n * @returns An {@link OutputDescriptorEncoder} routing each event through its descriptor.\n */\nexport const createOutputDescriptorEncoder = <U extends { type: string }>(\n descriptors: readonly OutputDescriptor<U>[],\n wireName: string,\n): OutputDescriptorEncoder<U> => {\n const { discreteByType, wildcards } = partitionOutputEvents(descriptors);\n const streamByPhase = new Map<string, { descriptor: OutputStreamDescriptor<U>; phase: 'start' | 'delta' | 'end' }>();\n\n for (const descriptor of descriptors) {\n if (descriptor.construct === 'stream') {\n streamByPhase.set(descriptor.start, { descriptor, phase: 'start' });\n streamByPhase.set(descriptor.delta, { descriptor, phase: 'delta' });\n streamByPhase.set(descriptor.end, { descriptor, phase: 'end' });\n }\n }\n\n return {\n encode: async (chunk, core, ctx) => {\n const { type } = chunk;\n\n const streamEntry = streamByPhase.get(type);\n if (streamEntry) {\n const { descriptor, phase } = streamEntry;\n const h: HeaderBuilder<U> = (c, keys) => writeFields(descriptor.fields, descriptor.kind, c, keys);\n // CAST: idField/deltaField are string-valued chunk keys by construction.\n const streamId = prop(chunk, descriptor.idField) as string;\n if (phase === 'start') {\n await core.startStream(streamId, { name: wireName, data: '', codecHeaders: h(chunk) }, ctx.opts);\n } else if (phase === 'delta') {\n core.appendStream(streamId, prop(chunk, descriptor.deltaField) as string);\n } else if (descriptor.onEnd) {\n await descriptor.onEnd(chunk, core, { h, name: wireName, messageId: ctx.messageId, opts: ctx.opts });\n } else {\n await core.closeStream(streamId, { name: wireName, data: '', codecHeaders: h(chunk) });\n }\n return;\n }\n\n const descriptor = discreteByType.get(type) ?? wildcards.find((w) => w.match?.(type));\n if (!descriptor) {\n throw new Ably.ErrorInfo(`unable to publish; unsupported event type '${type}'`, ErrorCode.InvalidArgument, 400);\n }\n\n const h: HeaderBuilder<U> = (c, keys) => writeFields(descriptor.fields, c.type, c, keys);\n if (descriptor.encode) {\n await descriptor.encode(chunk, core, { h, name: wireName, messageId: ctx.messageId, opts: ctx.opts });\n return;\n }\n\n const data = descriptor.data ? descriptor.data.encode(chunk) : '';\n await core.publishDiscrete(\n { name: wireName, data, codecHeaders: h(chunk), ephemeral: descriptor.ephemeral?.(chunk) },\n ctx.opts,\n );\n },\n };\n};\n","/**\n * Declarative output descriptors — the single source of truth for a codec's\n * `ai-output` wire mapping, the output-side sibling of {@link import('./input-descriptors.js')}.\n *\n * A codec declares each ordinary output event once, as a descriptor built on the\n * typed header-field bindings ({@link HeaderField}). The generic encode/decode\n * drivers consume the descriptor set, so adding an ordinary event is one\n * descriptor entry instead of three hand-synchronised switch arms (encoder,\n * decoder, stream reconstruction).\n *\n * Authoring is cast-free: the {@link outputBuilder} factory hands the codec an\n * `{ event, stream }` pair curried on the codec's output union, so every `data` /\n * `encode` / `decode` callback receives the exact narrowed member. The descriptors\n * are then erased to a heterogeneous {@link OutputDescriptor} via a single\n * documented cast at each constructor boundary — never in author code.\n */\n\nimport type * as Ably from 'ably';\n\nimport { wildcardMatcher } from './field-bag.js';\nimport type { DataCodec, FieldFor, HeaderField } from './fields.js';\nimport type { MessagePayload, StreamPayload, WriteOptions } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Type helpers\n// ---------------------------------------------------------------------------\n\n/** The string-valued keys of `C` — the only keys `idField`/`deltaField` may name. */\nexport type StringKeyOf<C> = { [K in keyof C]-?: C[K] extends string ? K : never }[keyof C];\n\n/**\n * Resolve the union member a descriptor `type` literal selects. An exact match\n * wins; a wildcard literal (`'data-*'`) resolves to the template member\n * (`data-${string}`), so wildcard descriptors still narrow to the real member.\n */\nexport type ResolveType<U extends { type: string }, T extends string> =\n Extract<U, { type: T }> extends never\n ? T extends `${infer P}-*`\n ? Extract<U, { type: `${P}-${string}` }>\n : never\n : Extract<U, { type: T }>;\n\n// ---------------------------------------------------------------------------\n// Escape-hatch core surface\n// ---------------------------------------------------------------------------\n\n/**\n * The narrowed view of the encoder core that escape-hatch `encode` functions\n * receive — only the publish/stream operations a hatch legitimately needs. The\n * full internal `EncoderCore` satisfies this structurally.\n */\nexport interface EscapeHatchCore {\n /** Publish a single discrete message. */\n publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult>;\n /** Start a streamed message. */\n startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void>;\n /** Append a fragment to an in-flight stream (fire-and-forget). */\n appendStream(streamId: string, data: string): void;\n /** Close a streamed message. */\n closeStream(streamId: string, payload: StreamPayload): Promise<void>;\n /** Cancel all in-progress streams. */\n cancelAllStreams(opts?: WriteOptions): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Header builder + contexts\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a codec headers record from a chunk through the descriptor's declared\n * fields, stamping the dispatch `type` plus each field read off `chunk`. An\n * optional `keys` subset restricts which declared fields are written; the keys\n * are checked against the chunk so the imperative path can't drift.\n * @template C - The narrowed chunk member.\n */\nexport type HeaderBuilder<C> = <K extends keyof C & string>(chunk: C, keys?: readonly K[]) => Record<string, string>;\n\n/**\n * Context passed to an escape-hatch `encode` function.\n * @template C - The narrowed chunk member.\n */\nexport interface OutputEncodeHatchContext<C> {\n /** Header builder bound to the descriptor's declared fields. */\n h: HeaderBuilder<C>;\n /** The wire message name for this direction (`ai-output` / `ai-input`). */\n name: string;\n /** The encoder's configured fallback message id, if any. */\n messageId: string | undefined;\n /** Per-write overrides to thread into the hatch's publish/cancel calls. */\n opts: WriteOptions | undefined;\n}\n\n/** Context passed to a discrete escape-hatch `decode` function. */\nexport interface OutputDecodeContext {\n /** The codec `kind` header value the message dispatched on (mirrors the input context's `codecKind`). */\n codecKind: string;\n /** The inbound codec-tier headers. */\n codecHeaders: Record<string, string>;\n /** The inbound transport-tier headers. */\n transportHeaders: Record<string, string>;\n /** The inbound message data. */\n data: unknown;\n}\n\n/** Context passed to a stream descriptor's `decodeEnd` escape hatch. */\nexport interface OutputStreamEndContext {\n /** The stream identifier (e.g. chunk id, toolCallId). */\n streamId: string;\n /** The full accumulated stream text. */\n accumulated: string;\n /** The stream's persistent (start) codec headers. */\n codecHeaders: Record<string, string>;\n /** The codec headers carried on close (may differ from the start headers). */\n closingCodecHeaders: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Data codec\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Author-facing specs (narrowed)\n// ---------------------------------------------------------------------------\n\n/**\n * A discrete (non-streaming) output event descriptor spec, narrowed to chunk member `C`.\n * @template C - The narrowed chunk member.\n */\nexport interface OutputEventSpec<C> {\n /**\n * Declared header fields, written on encode and read on decode. Each field's\n * key names both the wire header and the chunk property it carries (see\n * {@link FieldFor}). Omit for a header-less event.\n */\n fields?: readonly FieldFor<C>[];\n /** Wire `data` codec. Omit when the event carries no data (`data: ''`). */\n data?: DataCodec<C>;\n /** Whether the publish is ephemeral (not persisted). Default false. */\n ephemeral?: (chunk: C) => boolean;\n /** Escape-hatch encode — overrides the default discrete publish. */\n encode?: (chunk: C, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<C>) => Promise<void>;\n}\n\n/**\n * A streamed-family descriptor spec. `start`/`delta`/`end` are the domain chunk\n * `type` literals; the family id (the {@link OutputBuilder.stream} first argument)\n * is the wire `kind` header all three phases stamp.\n * @template U - The codec's event union.\n * @template S - The start chunk `type` literal.\n * @template D - The delta chunk `type` literal.\n * @template E - The end chunk `type` literal.\n */\nexport interface OutputStreamSpec<\n U extends { type: string },\n S extends U['type'],\n D extends U['type'],\n E extends U['type'],\n> {\n /** The start chunk `type` literal. */\n start: S;\n /** The delta chunk `type` literal. */\n delta: D;\n /** The end chunk `type` literal. */\n end: E;\n /** The string-valued chunk key carrying the stream id (e.g. `id`, `toolCallId`). */\n idField: StringKeyOf<ResolveType<U, S>> & StringKeyOf<ResolveType<U, D>> & StringKeyOf<ResolveType<U, E>>;\n /** The string-valued delta chunk key carrying the appended fragment. */\n deltaField: StringKeyOf<ResolveType<U, D>>;\n /**\n * Declared header fields written/read on the start and end chunks. Each\n * field's key names both the wire header and the chunk property (see\n * {@link FieldFor}); a field may bind a property carried by either phase.\n */\n fields: readonly (FieldFor<ResolveType<U, S>> | FieldFor<ResolveType<U, E>>)[];\n /** Escape-hatch override for the stream-close step only (e.g. close-or-discrete fallback). */\n onEnd?: (\n chunk: ResolveType<U, E>,\n core: EscapeHatchCore,\n ctx: OutputEncodeHatchContext<ResolveType<U, E>>,\n ) => Promise<void>;\n /** Escape-hatch override for the end-chunk rebuild (e.g. input from accumulated text). */\n decodeEnd?: (ctx: OutputStreamEndContext) => ResolveType<U, E>[];\n /**\n * Escape-hatch decode for when the family arrives as a discrete (non-streamed)\n * message — the wire `kind` equals the family id but the wire wasn't streamed\n * (e.g. history compaction). Reconstructs the start/end chunk pair.\n */\n decodeDiscrete?: (ctx: OutputDecodeContext) => ResolveType<U, S | E>[];\n}\n\n// ---------------------------------------------------------------------------\n// Erased descriptor (heterogeneous array element)\n// ---------------------------------------------------------------------------\n\n/** A discrete output event descriptor erased to the codec's union `U`. */\nexport interface OutputEventDescriptor<U> {\n /** Discriminator — the construct this descriptor was built with. */\n construct: 'event';\n /** The dispatch `type` literal (or wildcard sentinel), stamped as the wire `kind` header. */\n type: string;\n /** Declared header fields. */\n fields: readonly HeaderField<unknown>[];\n /** Wire `data` codec, if any. */\n data?: DataCodec<U>;\n /** Ephemeral predicate, if any. */\n ephemeral?: (chunk: U) => boolean;\n /** Wildcard dispatch predicate (both directions), derived by the builder from a `-*` type literal. */\n match?: (type: string) => boolean;\n /** Escape-hatch encode, if any. */\n encode?: (chunk: U, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<U>) => Promise<void>;\n}\n\n/** A streamed-family descriptor erased to the codec's union `U`. */\nexport interface OutputStreamDescriptor<U> {\n /** Discriminator — the construct this descriptor was built with. */\n construct: 'stream';\n /** The stream family id, stamped as the wire `kind` header on every phase. */\n kind: string;\n /** The start chunk `type`. */\n start: string;\n /** The delta chunk `type`. */\n delta: string;\n /** The end chunk `type`. */\n end: string;\n /** The chunk key carrying the stream id. */\n idField: string;\n /** The delta chunk key carrying the appended fragment. */\n deltaField: string;\n /** Declared header fields. */\n fields: readonly HeaderField<unknown>[];\n /** Escape-hatch close override, if any. */\n onEnd?: (chunk: U, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<U>) => Promise<void>;\n /** Escape-hatch end-rebuild override, if any. */\n decodeEnd?: (ctx: OutputStreamEndContext) => U[];\n /** Escape-hatch non-streamed decode, if any. */\n decodeDiscrete?: (ctx: OutputDecodeContext) => U[];\n}\n\n/** An erased output descriptor — a discrete event or a streamed family. */\nexport type OutputDescriptor<U> = OutputEventDescriptor<U> | OutputStreamDescriptor<U>;\n\n// ---------------------------------------------------------------------------\n// Builder factory\n// ---------------------------------------------------------------------------\n\n/**\n * The direction-scoped output builder `defineCodec` injects into the `output`\n * config function — `event` (single discrete) and `stream` (streamed family),\n * both curried on the codec's output union so author entries narrow cast-free.\n * @template U - The codec's output union.\n */\nexport interface OutputBuilder<U extends { type: string }> {\n /**\n * Declare a single discrete output event. Curried on the output union; narrows\n * `spec` to the member the `type` literal selects. The `type` literal is stamped\n * as the wire `kind` dispatch header.\n * @param type - The event's `type` literal (or a `*-*` wildcard); stamped as the wire `kind` header.\n * @param spec - The narrowed output event spec. Omit for a header-less event with no data.\n * @returns An erased {@link OutputDescriptor}.\n */\n event: <T extends U['type'] | `${string}-*`>(\n type: T,\n spec?: OutputEventSpec<ResolveType<U, T>>,\n ) => OutputDescriptor<U>;\n /**\n * Declare a streamed output family (start / delta / end). `start`/`delta`/`end`\n * are domain chunk `type` literals; the first argument is the family id, stamped\n * as the wire `kind` dispatch header on every phase.\n * @param kind - The stream family id, stamped as the wire `kind` header on every phase.\n * @param spec - The narrowed stream spec.\n * @returns An erased {@link OutputDescriptor}.\n */\n stream: <S extends U['type'], D extends U['type'], E extends U['type']>(\n kind: string,\n spec: OutputStreamSpec<U, S, D, E>,\n ) => OutputDescriptor<U>;\n}\n\n/**\n * Build the curried `{ event, stream }` output builder for a codec's output union.\n * `defineCodec` calls this once and hands the result to the `output` config\n * function; mirrors the input side's {@link import('./input-descriptors.js').inputBuilder}.\n * @template U - The codec's output union.\n * @returns The direction-scoped {@link OutputBuilder}.\n */\nexport const outputBuilder = <U extends { type: string }>(): OutputBuilder<U> => ({\n event: (type, spec) =>\n // CAST: `spec` is narrowed to the member selected by `type`; the descriptor\n // erases that to the codec's union `U` so heterogeneous descriptors share one\n // array type. The drivers only ever invoke a descriptor's callbacks with the\n // matching member, so the erasure is sound by construction. `fields` defaults\n // to `[]` so a header-less event needs no spec (mirrors the input `event` builder).\n ({\n construct: 'event',\n type,\n fields: spec?.fields ?? [],\n data: spec?.data,\n ephemeral: spec?.ephemeral,\n // A `-*` literal declares a wildcard family; the dispatch predicate is\n // derived from the literal so the two can never disagree (see wildcardMatcher).\n match: wildcardMatcher(type),\n encode: spec?.encode,\n }) as unknown as OutputDescriptor<U>,\n stream: (kind, spec) =>\n // CAST: see `event` — the narrowed stream spec erases to `OutputDescriptor<U>`.\n ({ construct: 'stream', kind, ...spec }) as unknown as OutputDescriptor<U>,\n});\n","/**\n * Core-provided factories for the well-known input variants.\n *\n * Every codec's `TInput` union is built from the same well-known input shapes\n * ({@link UserMessage}, {@link Regenerate}, {@link ToolResult}, {@link\n * ToolResultError}, {@link ToolApprovalResponse}). The factory bodies that wrap\n * a domain value into one of these variants are fully determined by those\n * shapes, so they live here once rather than being re-implemented per codec.\n * {@link wellKnownInputs} returns them payload-typed to a given `TInput`, ready\n * to spread into a codec definition.\n */\n\nimport type {\n CodecInputEvent,\n Regenerate,\n ToolApprovalResponse,\n ToolApprovalResponsePayloadOf,\n ToolResult,\n ToolResultError,\n ToolResultErrorPayloadOf,\n ToolResultPayloadOf,\n UserMessage,\n UserMessageOf,\n} from './types.js';\n\n/**\n * The well-known input factory functions, payload-typed for a codec's `TInput`\n * union. A codec spreads these into its definition rather than re-implementing\n * the variant-wrapping boilerplate. Each factory returns the specific variant\n * it builds — a member of the codec's `TInput` union.\n * @template TInput - The codec's input union.\n */\nexport interface WellKnownInputFactories<TInput extends CodecInputEvent> {\n /**\n * Wrap a domain message as the codec's {@link UserMessage} variant.\n * @param message - The message in the codec's domain representation.\n * @returns The user-message input.\n */\n createUserMessage(message: UserMessageOf<TInput>): UserMessage<UserMessageOf<TInput>>;\n /**\n * Build a {@link Regenerate} input.\n * @param target - The codec-message-id of the assistant message to regenerate.\n * @param parent - The codec-message-id of the parent user message the new assistant threads under.\n * @returns The regenerate input.\n */\n createRegenerate(target: string, parent: string): Regenerate;\n /**\n * Build a {@link ToolResult} input addressing an assistant codec-message.\n * @param codecMessageId - The assistant codec-message carrying the tool call.\n * @param payload - The codec's domain payload describing the tool result.\n * @returns The tool-result input.\n */\n createToolResult(\n codecMessageId: string,\n payload: ToolResultPayloadOf<TInput>,\n ): ToolResult<ToolResultPayloadOf<TInput>>;\n /**\n * Build a {@link ToolResultError} input addressing an assistant codec-message.\n * @param codecMessageId - The assistant codec-message carrying the tool call.\n * @param payload - The codec's domain payload describing the failure.\n * @returns The tool-result-error input.\n */\n createToolResultError(\n codecMessageId: string,\n payload: ToolResultErrorPayloadOf<TInput>,\n ): ToolResultError<ToolResultErrorPayloadOf<TInput>>;\n /**\n * Build a {@link ToolApprovalResponse} input addressing an assistant codec-message.\n * @param codecMessageId - The assistant codec-message carrying the tool call.\n * @param payload - The codec's domain payload describing the approval decision.\n * @returns The tool-approval-response input.\n */\n createToolApprovalResponse(\n codecMessageId: string,\n payload: ToolApprovalResponsePayloadOf<TInput>,\n ): ToolApprovalResponse<ToolApprovalResponsePayloadOf<TInput>>;\n}\n\n/**\n * Build the {@link WellKnownInputFactories} for a codec's `TInput` union. The\n * returned factories wrap domain values into the well-known input variants and\n * are typically spread into a codec definition.\n * @template TInput - The codec's input union.\n * @returns The well-known input factory functions, payload-typed to `TInput`.\n */\nexport const wellKnownInputs = <TInput extends CodecInputEvent>(): WellKnownInputFactories<TInput> => ({\n createUserMessage: (message) => ({ kind: 'user-message', message }),\n createRegenerate: (target, parent) => ({ kind: 'regenerate', target, parent }),\n createToolResult: (codecMessageId, payload) => ({ kind: 'tool-result', codecMessageId, payload }),\n createToolResultError: (codecMessageId, payload) => ({ kind: 'tool-result-error', codecMessageId, payload }),\n createToolApprovalResponse: (codecMessageId, payload) => ({\n kind: 'tool-approval-response',\n codecMessageId,\n payload,\n }),\n});\n","/**\n * `defineCodec` — composition packaging for a codec.\n *\n * A codec author supplies only its **parts** — a reducer, a per-direction\n * descriptor table (the `output` and `input` builder functions), an optional\n * decode lifecycle policy, and an optional agent identifier — and `defineCodec`\n * assembles a fully-formed {@link Codec}: the generic encoder/decoder skeletons\n * (built here, codec-agnostic), the reducer methods, and the well-known input\n * factories (merged internally).\n *\n * Both directions are declarative descriptor tables driven by the generic\n * encode/decode drivers. `defineCodec` hands each table a direction-scoped\n * builder typed to that direction's union — `{ event, stream }` for outputs,\n * `{ event, batch }` for inputs — so each construct's spec stays type-correct\n * per direction under shared construct names, with no per-entry casts. Both\n * sides build/read wire headers through the same shared field bindings, so\n * encode and decode cannot drift.\n */\n\nimport * as Ably from 'ably';\n\nimport { EVENT_AI_INPUT, EVENT_AI_OUTPUT, HEADER_RUN_ID } from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { DecoderCore, DecoderCoreHooks } from './decoder.js';\nimport { createDecoderCore } from './decoder.js';\nimport type { EncoderCore, EncoderCoreOptions } from './encoder.js';\nimport { createEncoderCore } from './encoder.js';\nimport { KIND_HEADER, PART_TYPE_HEADER } from './field-bag.js';\nimport type { HeaderField } from './fields.js';\nimport { createInputDescriptorDecoder, type InputDescriptorDecoder } from './input-descriptor-decoder.js';\nimport { createInputDescriptorEncoder, type InputDescriptorEncoder } from './input-descriptor-encoder.js';\nimport { type InputBuilder, inputBuilder, type InputDescriptor } from './input-descriptors.js';\nimport { createOutputDescriptorDecoder } from './output-descriptor-decoder.js';\nimport { createOutputDescriptorEncoder, type OutputDescriptorEncoder } from './output-descriptor-encoder.js';\nimport { type OutputBuilder, outputBuilder, type OutputDescriptor } from './output-descriptors.js';\nimport type {\n ChannelWriter,\n Codec,\n CodecEvent,\n CodecInputEvent,\n CodecMessage,\n CodecOutputEvent,\n DecodedMessage,\n Decoder,\n Encoder,\n MessagePayload,\n ReducerMeta,\n StreamTrackerState,\n WriteOptions,\n} from './types.js';\nimport { type WellKnownInputFactories, wellKnownInputs } from './well-known-inputs.js';\n\n// Re-exported so codec descriptor tables (e.g. the Vercel `inputs.ts` / `outputs.ts`)\n// can type their builder parameter without reaching into the descriptor modules directly.\nexport type { InputBuilder } from './input-descriptors.js';\nexport type { OutputBuilder } from './output-descriptors.js';\n\n// ---------------------------------------------------------------------------\n// Decode lifecycle policy\n// ---------------------------------------------------------------------------\n\n/** Context passed to a {@link LifecyclePolicy} `onDiscrete` repair function. */\nexport interface LifecycleDiscreteContext {\n /** The inbound codec-tier headers (e.g. to recover a stream's message id). */\n codecHeaders: Record<string, string>;\n}\n\n/**\n * Declarative decode-time lifecycle repair, applied when joining a stream\n * mid-flight (history compaction, rewind miss, partial page). Each function\n * performs its side effect on the codec's lifecycle tracker (captured by the\n * factory that builds the policy) and RETURNS lead-in events to PREPEND; the\n * generic decoder ALWAYS runs the descriptor driver after and appends its\n * output, so the policy never replaces a decode. A codec with no repair\n * supplies no policy.\n * @template TOutput - The codec's output union.\n */\nexport interface LifecyclePolicy<TOutput> {\n /**\n * Keyed on the discrete codec `kind`. Returns lead-in events to prepend\n * (empty array = none) after applying any tracker side effect.\n */\n onDiscrete?: Record<string, (runId: string, ctx: LifecycleDiscreteContext) => TOutput[]>;\n /** Lead-in prepended to a stream's start events (mid-stream-join pre-roll). */\n onStreamStart?: (runId: string, tracker: StreamTrackerState) => TOutput[];\n}\n\n// ---------------------------------------------------------------------------\n// defineCodec config + result\n// ---------------------------------------------------------------------------\n\n/**\n * The reducer parts a codec supplies. `TProjection` and `TMessage` infer from\n * these, so `defineCodec` callers need not spell them out.\n * @template TInput - The codec's input union.\n * @template TOutput - The codec's output union.\n * @template TProjection - The per-node projection the reducer folds into.\n * @template TMessage - The per-message domain type.\n */\nexport interface CodecReducer<TInput, TOutput, TProjection, TMessage> {\n /** Build an empty projection for a node. */\n init: () => TProjection;\n /** Fold one direction-tagged input or output event into the projection. */\n fold: (state: TProjection, event: CodecEvent<TInput, TOutput>, meta: ReducerMeta) => TProjection;\n /** Extract the per-message list (each paired with its codec-message-id). */\n getMessages: (projection: TProjection) => CodecMessage<TMessage>[];\n}\n\n/**\n * The parts a codec supplies to {@link defineCodec}.\n * @template TInput - The codec's input union.\n * @template TOutput - The codec's output union.\n * @template TProjection - The per-node projection the reducer folds into.\n * @template TMessage - The per-message domain type.\n */\nexport interface DefineCodecConfig<\n TInput extends { kind: string },\n TOutput extends { type: string },\n TProjection,\n TMessage,\n> {\n /** Optional Ably-Agent identifier registered on the channel; omit to opt out. */\n adapterTag?: string;\n /** Reducer parts; `TProjection` / `TMessage` infer from here. */\n reducer: CodecReducer<TInput, TOutput, TProjection, TMessage>;\n /**\n * The declarative output (`ai-output`) descriptor table, returned from the\n * injected `{ event, stream }` builder (both curried on `TOutput`).\n */\n output: (b: OutputBuilder<TOutput>) => readonly OutputDescriptor<TOutput>[];\n /**\n * The declarative input (`ai-input`) descriptor table, returned from the\n * injected `{ event, batch }` builder (both curried on `TInput`).\n */\n input: (b: InputBuilder<TInput>) => readonly InputDescriptor<TInput>[];\n /**\n * Factory for a fresh decode lifecycle policy per decoder instance (the\n * policy's closures capture a fresh, per-decoder lifecycle tracker). Omit\n * for a codec with no mid-stream-join repair.\n */\n decodeLifecycle?: () => LifecyclePolicy<TOutput>;\n}\n\n/**\n * A codec assembled by {@link defineCodec}: a conforming {@link Codec} whose\n * well-known input factories are typed concretely by {@link WellKnownInputFactories}\n * (so `createToolResult` etc. are callable without a guard). The factory methods\n * are sourced from `WellKnownInputFactories` rather than `Codec` because the\n * former types them against `UserMessageOf<TInput>` / `ToolResultPayloadOf<TInput>`\n * — equal to the codec's `TMessage` / payloads for every real codec, but not\n * provably so to the generic type system. At a concrete call site a\n * `DefinedCodec` is assignable to the corresponding `Codec`.\n */\nexport type DefinedCodec<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> = Omit<Codec<TInput, TOutput, TProjection, TMessage>, keyof WellKnownInputFactories<TInput>> &\n WellKnownInputFactories<TInput>;\n\n// ---------------------------------------------------------------------------\n// Generic encoder\n// ---------------------------------------------------------------------------\n\nclass DefaultCodecEncoder<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent> implements Encoder<\n TInput,\n TOutput\n> {\n private readonly _core: EncoderCore;\n private readonly _messageId: string | undefined;\n private readonly _outputEncoder: OutputDescriptorEncoder<TOutput>;\n private readonly _inputEncoder: InputDescriptorEncoder<TInput>;\n\n constructor(\n writer: ChannelWriter,\n options: EncoderCoreOptions,\n outputEncoder: OutputDescriptorEncoder<TOutput>,\n inputEncoder: InputDescriptorEncoder<TInput>,\n ) {\n this._core = createEncoderCore(writer, options);\n this._messageId = options.messageId;\n this._outputEncoder = outputEncoder;\n this._inputEncoder = inputEncoder;\n }\n\n async publishInput(input: TInput, options?: WriteOptions): Promise<void> {\n // No `messageId` threads into inputs — user-message parts carry no\n // transport codec-message-id today; inputs rely on opts.messageId stamped\n // by the client session.\n await this._inputEncoder.encode(input, this._core, { opts: options });\n }\n\n async publishOutput(output: TOutput, options?: WriteOptions): Promise<void> {\n await this._outputEncoder.encode(output, this._core, { messageId: this._messageId, opts: options });\n }\n\n async cancelStreams(): Promise<void> {\n await this._core.cancelAllStreams();\n }\n\n async close(): Promise<void> {\n await this._core.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Generic decoder\n// ---------------------------------------------------------------------------\n\nconst decodeDiscretePayload = <TInput extends { kind: string }, TOutput>(\n payload: MessagePayload,\n outputDecoder: ReturnType<typeof createOutputDescriptorDecoder<TOutput & { type: string }>>,\n inputDecoder: InputDescriptorDecoder<TInput>,\n lifecycle: LifecyclePolicy<TOutput> | undefined,\n): (TInput | TOutput)[] => {\n const codecHeaders = payload.codecHeaders ?? {};\n const transportHeaders = payload.transportHeaders ?? {};\n const codecKind = codecHeaders[KIND_HEADER] ?? '';\n\n if (payload.name === EVENT_AI_INPUT) {\n return inputDecoder.decode({ codecKind, data: payload.data, codecHeaders, transportHeaders });\n }\n\n if (payload.name === EVENT_AI_OUTPUT) {\n const runId = transportHeaders[HEADER_RUN_ID] ?? '';\n // Lifecycle repair runs its side effect and returns lead-in events; the\n // descriptor driver always decodes after and its output is appended.\n // The `kind` comes off the wire, so the policy lookup must be own-property\n // only — a crafted kind such as 'valueOf' or 'toString' would otherwise\n // resolve through Object.prototype and corrupt the decode.\n const onDiscrete = lifecycle?.onDiscrete;\n const repair = onDiscrete !== undefined && Object.hasOwn(onDiscrete, codecKind) ? onDiscrete[codecKind] : undefined;\n const pre = repair?.(runId, { codecHeaders }) ?? [];\n return [...pre, ...outputDecoder.decodeDiscrete(codecKind, codecHeaders, transportHeaders, payload.data)];\n }\n\n return [];\n};\n\n// Only outputs stream: a streamed message under any other wire name (a\n// foreign or crafted ai-input stream) must not rebuild through the output\n// stream path — its events would be mislabelled as inputs by the\n// direction-routing decode. Enforces the invariant the decode cast relies on.\nconst isOutputStream = (tracker: StreamTrackerState): boolean => tracker.name === EVENT_AI_OUTPUT;\n\nconst buildHooks = <TInput extends { kind: string }, TOutput extends { type: string }>(\n outputDecoder: ReturnType<typeof createOutputDescriptorDecoder<TOutput>>,\n inputDecoder: InputDescriptorDecoder<TInput>,\n lifecycle: LifecyclePolicy<TOutput> | undefined,\n): DecoderCoreHooks<TInput | TOutput> => ({\n buildStartEvents: (tracker) => {\n if (!isOutputStream(tracker)) return [];\n const runId = tracker.transportHeaders[HEADER_RUN_ID] ?? '';\n const pre = lifecycle?.onStreamStart?.(runId, tracker) ?? [];\n return [...pre, ...outputDecoder.buildStart(tracker)];\n },\n buildDeltaEvents: (tracker, delta) => (isOutputStream(tracker) ? outputDecoder.buildDelta(tracker, delta) : []),\n buildEndEvents: (tracker, closingCodecHeaders) =>\n isOutputStream(tracker) ? outputDecoder.buildEnd(tracker, closingCodecHeaders) : [],\n decodeDiscrete: (payload) => decodeDiscretePayload(payload, outputDecoder, inputDecoder, lifecycle),\n});\n\nclass DefaultCodecDecoder<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent> implements Decoder<\n TInput,\n TOutput\n> {\n private readonly _core: DecoderCore<TInput | TOutput>;\n\n constructor(core: DecoderCore<TInput | TOutput>) {\n this._core = core;\n }\n\n decode(message: Ably.InboundMessage): DecodedMessage<TInput, TOutput> {\n const events = this._core.decode(message);\n // A single inbound message carries one wire name (ai-input XOR ai-output), so the\n // name fixes the direction of every event decoded from it. The wire name is the\n // authoritative direction signal — never the event's in-memory shape.\n if (message.name === EVENT_AI_INPUT) {\n // CAST: an ai-input message decodes only to inputs.\n return { inputs: events as TInput[], outputs: [] };\n }\n // CAST: every other message is ai-output — the only other wire name the core decodes\n // (unrecognised names yield no events) — so its events are all outputs.\n return { inputs: [], outputs: events as TOutput[] };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Table validation\n// ---------------------------------------------------------------------------\n\n/**\n * Reserve `literal` in `seen` under a human-readable owner description,\n * throwing if another descriptor already holds it. Dispatch literals must be\n * unique within their namespace — a duplicate would silently route through\n * whichever descriptor registered last.\n * @param seen - The namespace's literal → owner registry, mutated in place.\n * @param literal - The dispatch literal to reserve.\n * @param owner - Human-readable description of the declaring descriptor (used in the error).\n */\nconst reserve = (seen: Map<string, string>, literal: string, owner: string): void => {\n const holder = seen.get(literal);\n if (holder !== undefined) {\n throw new Ably.ErrorInfo(\n `unable to define codec; dispatch literal '${literal}' is declared by both ${holder} and ${owner}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n seen.set(literal, owner);\n};\n\n/**\n * Throw when a declared field binds one of the driver-reserved header keys.\n * @param fields - The descriptor's declared header fields.\n * @param owner - Human-readable description of the declaring descriptor (used in the error).\n */\nconst rejectReservedFieldKeys = (fields: readonly HeaderField<unknown>[], owner: string): void => {\n for (const field of fields) {\n if (field.key === KIND_HEADER || field.key === PART_TYPE_HEADER) {\n throw new Ably.ErrorInfo(\n `unable to define codec; ${owner} binds the driver-reserved header key '${field.key}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n }\n};\n\n/**\n * Fail-fast validation of the assembled descriptor tables, run once per\n * `defineCodec` call. Catches author mistakes the drivers would otherwise\n * surface as silent last-wins routing or encode/decode asymmetry:\n *\n * - duplicate dispatch literals within a namespace — the domain chunk `type`\n * namespace (discrete event types + stream phase types, which drive encode\n * dispatch) and the wire `kind` namespace (discrete event types + stream\n * family kinds, which drive decode dispatch);\n * - duplicate input `kind`s and duplicate `partType`s within a batch;\n * - field bindings on the driver-reserved `kind` / `partType` header keys.\n * @param outputs - The assembled output descriptor table.\n * @param inputs - The assembled input descriptor table.\n */\nconst validateTables = <TInput, TOutput>(\n outputs: readonly OutputDescriptor<TOutput>[],\n inputs: readonly InputDescriptor<TInput>[],\n): void => {\n const chunkTypes = new Map<string, string>();\n const wireKinds = new Map<string, string>();\n for (const descriptor of outputs) {\n if (descriptor.construct === 'event') {\n const owner = `output event '${descriptor.type}'`;\n reserve(chunkTypes, descriptor.type, owner);\n reserve(wireKinds, descriptor.type, owner);\n rejectReservedFieldKeys(descriptor.fields, owner);\n } else {\n const owner = `output stream '${descriptor.kind}'`;\n reserve(wireKinds, descriptor.kind, owner);\n for (const phase of [descriptor.start, descriptor.delta, descriptor.end]) {\n reserve(chunkTypes, phase, owner);\n }\n rejectReservedFieldKeys(descriptor.fields, owner);\n }\n }\n\n const inputKinds = new Map<string, string>();\n for (const descriptor of inputs) {\n const owner = `input ${descriptor.construct} '${descriptor.kind}'`;\n reserve(inputKinds, descriptor.kind, owner);\n if (descriptor.construct === 'event') {\n rejectReservedFieldKeys(descriptor.fields, owner);\n } else {\n const partTypes = new Map<string, string>();\n for (const part of descriptor.parts) {\n const partOwner = `${owner} part '${part.partType}'`;\n reserve(partTypes, part.partType, partOwner);\n rejectReservedFieldKeys(part.fields, partOwner);\n }\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Assemble a fully-formed {@link Codec} from a codec's parts. Curried on the\n * input/output unions so `TProjection` / `TMessage` infer from `config.reducer`\n * — a caller writes `defineCodec<TInput, TOutput>()({ ... })` and never spells\n * out the projection or message types.\n * @template TInput - The codec's input union.\n * @template TOutput - The codec's output union.\n * @returns A function taking the codec's parts and returning the assembled codec.\n */\nexport const defineCodec =\n <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>() =>\n <TProjection, TMessage>(\n config: DefineCodecConfig<TInput, TOutput, TProjection, TMessage>,\n ): DefinedCodec<TInput, TOutput, TProjection, TMessage> => {\n const { reducer, decodeLifecycle } = config;\n // Build the direction-scoped builders, hand them to the codec's table\n // functions, and collect the descriptor arrays the drivers consume.\n const outputs = config.output(outputBuilder<TOutput>());\n const inputs = config.input(inputBuilder<TInput>());\n validateTables(outputs, inputs);\n // The descriptor drivers are pure functions of the (fixed) tables — build\n // them once here and share them across every encoder/decoder instance.\n const outputEncoder = createOutputDescriptorEncoder(outputs, EVENT_AI_OUTPUT);\n const inputEncoder = createInputDescriptorEncoder(inputs, EVENT_AI_INPUT);\n const outputDecoder = createOutputDescriptorDecoder(outputs);\n const inputDecoder = createInputDescriptorDecoder(inputs);\n return {\n // adapterTag is optional on Codec; only set it when supplied so a codec\n // can opt out of Ably-Agent registration.\n ...(config.adapterTag === undefined ? {} : { adapterTag: config.adapterTag }),\n init: reducer.init,\n fold: reducer.fold,\n getMessages: reducer.getMessages,\n createEncoder: (writer, options = {}) => new DefaultCodecEncoder(writer, options, outputEncoder, inputEncoder),\n createDecoder: () =>\n new DefaultCodecDecoder<TInput, TOutput>(\n // The lifecycle policy (and its tracker) stays per-decoder: each\n // decoder instance gets independent per-run phase state. No options\n // thread through: Codec.createDecoder takes none, so accepting any\n // here would be unreachable surface.\n createDecoderCore(buildHooks(outputDecoder, inputDecoder, decodeLifecycle?.()), {}),\n ),\n ...wellKnownInputs<TInput>(),\n };\n };\n","/**\n * Shared Vercel codec header-field bindings.\n *\n * Each field binds a codec header key to its value type once (see\n * {@link HeaderField}); the output/input descriptors and escape hatches all\n * read and write through these bindings, so a header key cannot drift between\n * the encode and decode side. Domain field names live in the Vercel layer, not\n * core, per the header-discipline rule.\n */\n\nimport type * as AI from 'ai';\n\nimport { boolField, enumField, type HeaderField, jsonField, strField } from '../../core/codec/index.js';\n\n/** Stream / message id (text & reasoning streams). */\nexport const fId = strField('id');\n/**\n * Provider metadata envelope, typed to the AI SDK shape. Annotated explicitly:\n * the inferred type resolves to the AI SDK's internal `SharedV3ProviderMetadata`\n * alias, which isn't portably nameable across the package boundary.\n */\nexport const fMeta: HeaderField<AI.ProviderMetadata | undefined, 'providerMetadata'> = jsonField<\n AI.ProviderMetadata,\n 'providerMetadata'\n>('providerMetadata');\n/** Tool call id — defaulted to total: an absent header reads as `''`. */\nexport const fToolCallId = strField('toolCallId', '');\n/** Tool name — defaulted to total. */\nexport const fToolName = strField('toolName', '');\n/** Whether the tool is a dynamic tool. */\nexport const fDynamic = boolField('dynamic');\n/** Optional human-readable title. */\nexport const fTitle = strField('title');\n/** Whether the provider executed the tool. */\nexport const fProviderExecuted = boolField('providerExecuted');\n/** Media type for file / source-document parts — defaulted to total. */\nexport const fMediaType = strField('mediaType', '');\n/** Source id for source-url / source-document parts — defaulted to total. */\nexport const fSourceId = strField('sourceId', '');\n\n// --- input-side bindings (shared by the input descriptors' encode/decode) ---\n\n/** Domain message id (`message.id`) stamped on every user-message part — distinct from the wire codec-message-id transport header. */\nexport const fMessageId = strField('messageId');\n/** Whether the user approved a tool execution — defaulted to total so an absent header reads `false`. */\nexport const fApproved = boolField('approved', false);\n/** Optional human-readable reason on a tool-approval response. */\nexport const fReason = strField('reason');\n\n/**\n * Validated finish reason. Mirrors the AI SDK's `FinishReason` literals and\n * falls back to `'stop'` for an absent or unrecognized value.\n */\nexport const fFinishReason = enumField(\n 'finishReason',\n ['stop', 'length', 'content-filter', 'tool-calls', 'error', 'other'] as const,\n 'stop',\n);\n","/**\n * Vercel decode lifecycle policy — mid-stream-join repair.\n *\n * When a client joins a stream mid-flight (history compaction, rewind miss,\n * partial page), the reducer must still see a clean `start` / `start-step`\n * pre-roll. This policy keys that repair on the discrete codec `kind` and on\n * stream start; each entry performs its tracker side effect and returns the\n * lead-in chunks the generic decoder prepends before running the descriptor\n * driver. A fresh policy (and tracker) is built per decoder instance.\n */\n\nimport type * as AI from 'ai';\n\nimport { createLifecycleTracker, type LifecyclePolicy, type LifecycleTracker } from '../../core/codec/index.js';\nimport { stripUndefined } from '../../utils.js';\nimport type { VercelOutput } from './events.js';\nimport { fMessageId } from './fields.js';\n\nconst createVercelLifecycleTracker = (): LifecycleTracker<AI.UIMessageChunk> =>\n createLifecycleTracker<AI.UIMessageChunk>([\n {\n key: 'start',\n build: (ctx) => [stripUndefined({ type: 'start' as const, messageId: ctx.messageId })],\n },\n {\n key: 'start-step',\n build: () => [{ type: 'start-step' as const }],\n },\n ]);\n\n/**\n * Build a fresh Vercel decode lifecycle policy (with its own tracker). Passed\n * to `defineCodec` as the `decodeLifecycle` factory so each decoder instance\n * gets independent per-run phase state.\n * @returns A {@link LifecyclePolicy} for the Vercel output union.\n */\nexport const createVercelDecodeLifecycle = (): LifecyclePolicy<VercelOutput> => {\n const tracker = createVercelLifecycleTracker();\n return {\n onDiscrete: {\n start: (runId) => {\n tracker.markEmitted(runId, 'start');\n return [];\n },\n 'start-step': (runId) => {\n tracker.markEmitted(runId, 'start-step');\n return [];\n },\n 'finish-step': (runId) => {\n tracker.resetPhase(runId, 'start-step');\n return [];\n },\n finish: (runId) => {\n tracker.clearScope(runId);\n return [];\n },\n error: (runId) => {\n tracker.clearScope(runId);\n return [];\n },\n abort: (runId) => {\n tracker.clearScope(runId);\n return [];\n },\n 'tool-input': (runId, ctx) => tracker.ensurePhases(runId, { messageId: fMessageId.read(ctx.codecHeaders) }),\n },\n onStreamStart: (runId, trackerState) =>\n tracker.ensurePhases(runId, { messageId: fMessageId.read(trackerState.codecHeaders) }),\n };\n};\n","/**\n * Wire-data shapes and runtime guards for the tool payloads whose `data`\n * envelope is JSON-parsed from the network (a trust boundary). The guards\n * validate the typed envelope fields; tool-defined `output`/`input` stay\n * unconstrained. Shared by the output and input descriptor tables.\n */\n\n/** Wire format for the agent-side `tool-input-error` chunk data payload. */\nexport interface ToolInputErrorWireData {\n errorText?: string;\n input?: unknown;\n}\n\n/** Wire format for the `tool-output-available` (agent) / `tool-result` (client) data payload. */\nexport interface ToolOutputAvailableWireData {\n output?: unknown;\n}\n\n/** Wire format for the agent-side `tool-output-error` chunk data payload. */\nexport interface AgentToolOutputErrorWireData {\n errorText?: string;\n}\n\n/** Wire format for the client-side `tool-result-error` input data payload. */\nexport interface ClientToolResultErrorWireData {\n message?: string;\n}\n\n// Narrow JSON-parsed wire data to a record. The encoder is expected to publish\n// an object for these payloads, but a malformed publish could carry a primitive\n// or null — callers fall back to field defaults when these guards reject.\nconst isRecord = (data: unknown): data is Record<string, unknown> => typeof data === 'object' && data !== null;\n\n// Validate that `data` is a record whose named field is absent or a string. The\n// optional-string check for the typed error fields below lives here once so the\n// guards can't drift. No `as` needed: `isRecord` narrows `data` to a record, so\n// string-key indexing is well-typed.\nconst isRecordWithOptionalString = (data: unknown, key: string): boolean =>\n isRecord(data) && (data[key] === undefined || typeof data[key] === 'string');\n\n// Validates the typed `errorText` field; `input` is tool-defined and\n// intentionally left unconstrained.\n/**\n * Coerce wire `data` to a string, falling back to `''` for any non-string\n * payload — the defensive read for descriptors whose data is plain text.\n * @param data - The inbound wire data.\n * @returns The string payload, or `''` when the data is not a string.\n */\nexport const asString = (data: unknown): string => (typeof data === 'string' ? data : '');\n\nexport const isToolInputErrorWireData = (data: unknown): data is ToolInputErrorWireData =>\n isRecordWithOptionalString(data, 'errorText');\n\n// The sole field `output` is tool-defined and intentionally unconstrained, so\n// this asserts only that the payload is an object envelope.\nexport const isToolOutputAvailableWireData = (data: unknown): data is ToolOutputAvailableWireData => isRecord(data);\n\n// Validates the typed `errorText` field.\nexport const isAgentToolOutputErrorWireData = (data: unknown): data is AgentToolOutputErrorWireData =>\n isRecordWithOptionalString(data, 'errorText');\n\n// Validates the typed `message` field.\nexport const isClientToolResultErrorWireData = (data: unknown): data is ClientToolResultErrorWireData =>\n isRecordWithOptionalString(data, 'message');\n","/**\n * Vercel input (`ai-input`) descriptors — the single source of truth for the\n * `VercelInput` wire mapping, the user-message fan-out included.\n *\n * `defineCodec` injects the direction-scoped `{ event, batch }` builder; the\n * generic input drivers consume the returned array. The tool inputs are single\n * `event`s lensed onto their nested `payload`; `regenerate` is a wire-only\n * signal; the multi-part user message is a `batch` that fans each\n * `UIMessage` part out into one wire event (reassembled by the reducer).\n *\n * Author-facing acceptance gate: the injected `event`/`batch` builders narrow\n * each member, so every `data` / `fields` / `parts` / `assemble` callback is\n * fully typed. The file's single `as` cast is the wire trust boundary on the\n * inbound role header (see `assemble`).\n */\n\nimport type * as AI from 'ai';\n\nimport { HEADER_ROLE } from '../../constants.js';\nimport type { InputBuilder, InputDescriptor } from '../../core/codec/index.js';\nimport type { VercelInput } from './events.js';\nimport { fApproved, fId, fMediaType, fMessageId, fReason, fToolCallId } from './fields.js';\nimport { asString, isClientToolResultErrorWireData, isToolOutputAvailableWireData } from './wire-data.js';\n\n/** Fallback for a message with no encodable parts (see the `user-message` batch). */\nconst EMPTY_MESSAGE_PARTS: AI.UIMessage['parts'] = [{ type: 'text', text: '' }];\n\n/**\n * Part types the `user-message` batch's `parts` sub-table can encode — must\n * stay in step with that table. Parts outside this set (e.g. `step-start`,\n * tool parts) have no wire mapping; `explode` filters them so the batch always\n * yields at least one encodable part and the message round-trips.\n * @param part - The UIMessage part to test.\n * @returns Whether the part has a wire mapping in the batch's part table.\n */\nconst isEncodablePart = (part: AI.UIMessage['parts'][number]): boolean =>\n part.type === 'text' || part.type === 'file' || part.type.startsWith('data-');\n\n/**\n * The Vercel codec's `ai-input` descriptors, built from the injected\n * direction-scoped builder.\n * @param builder - The `{ event, batch }` builder curried on `VercelInput`.\n * @param builder.event - Define a single-event input (payload-nested, or `wireOnly`).\n * @param builder.batch - Define a multi-part (batch) input that fans out into one wire event per part.\n * @returns The input descriptor table the generic input drivers consume.\n */\nexport const inputs = ({ event, batch }: InputBuilder<VercelInput>): readonly InputDescriptor<VercelInput>[] => [\n // --- tool inputs: nested payload, codec-message-id-addressed ----------------\n\n event('tool-result', {\n fields: [fToolCallId],\n data: {\n encode: (p) => ({ output: p.output }),\n // Malformed wire data decodes to undefined, which the rebuild seam strips\n // — the folded payload then has no `output` key (reads as undefined).\n decode: (d) => ({ output: isToolOutputAvailableWireData(d) ? d.output : undefined }),\n },\n }),\n event('tool-result-error', {\n fields: [fToolCallId],\n data: {\n encode: (p) => ({ message: p.message }),\n decode: (d) => ({ message: isClientToolResultErrorWireData(d) ? (d.message ?? '') : '' }),\n },\n }),\n event('tool-approval-response', { fields: [fToolCallId, fApproved, fReason] }),\n\n // --- wire-only signal -------------------------------------------------------\n\n // `regenerate` carries no domain payload; `parent` / `target` ride the\n // transport headers built by the client-session and read by the agent's\n // input-event lookup, so it stamps only the `kind` header and decodes to [].\n event('regenerate', { wireOnly: true }),\n\n // --- multi-part client message ----------------------------------------------\n\n // The user message fans out into one wire event per part, all sharing the\n // `user-message` kind and codec-message-id, each carrying its `partType`. The\n // message id (a codec header) and role (a transport header) are per-message,\n // stamped on every part so the decode side can rebuild the envelope from any\n // one; the reducer merges parts sharing a codec-message-id.\n batch('user-message', {\n // A message with no encodable parts (empty, or only unmapped types like\n // step-start) still publishes one empty text part, so the codec-message-id\n // and role survive and it round-trips to a one-part message. The driver's\n // bare-headers fallback cannot round-trip (it carries no partType), so the\n // ≥1-encodable-part guarantee lives here.\n explode: (input) => {\n const encodable = input.message.parts.filter((part) => isEncodablePart(part));\n return encodable.length > 0 ? encodable : EMPTY_MESSAGE_PARTS;\n },\n partTypeOf: (part) => part.type,\n parts: (p) => [\n p('text', { data: { encode: (x) => x.text, decode: (d) => ({ text: asString(d) }) } }),\n p('file', {\n fields: [fMediaType],\n data: { encode: (x) => x.url, decode: (d) => ({ url: asString(d) }) },\n }),\n p('data-*', {\n fields: [fId],\n data: { encode: (x) => x.data, decode: (d) => ({ data: d }) },\n }),\n ],\n messageHeaders: (input) => {\n const codecHeaders: Record<string, string> = {};\n fMessageId.write(codecHeaders, input.message.id);\n return { codecHeaders, transportHeaders: { [HEADER_ROLE]: input.message.role } };\n },\n assemble: (part, { codecHeaders, transportHeaders }) => {\n // CAST: HEADER_ROLE is wire data; the role string is trusted as a UIMessage role.\n const role = (transportHeaders[HEADER_ROLE] ?? 'user') as AI.UIMessage['role'];\n const id = fMessageId.read(codecHeaders) ?? '';\n return { message: { id, role, parts: [part] } };\n },\n }),\n];\n","/**\n * Vercel output (`ai-output`) event descriptors — the single source of truth for\n * encoding/decoding `UIMessageChunk` outputs. `defineCodec` injects the\n * direction-scoped `{ event, stream }` builder; the generic drivers consume the\n * returned array. Adding an ordinary output event is one entry here.\n *\n * Author-facing acceptance gate: this file contains **zero `as` casts**. The\n * injected `event`/`stream` builders narrow each chunk member, so the\n * `data`/`encode`/`decode` callbacks are fully typed.\n */\n\nimport * as Ably from 'ably';\n\nimport type { OutputBuilder, OutputDescriptor } from '../../core/codec/index.js';\nimport { boolField, jsonField, strField } from '../../core/codec/index.js';\nimport { ErrorCode, errorInfoIs } from '../../errors.js';\nimport { parseJsonOrString, stripUndefined } from '../../utils.js';\nimport type { VercelOutput } from './events.js';\nimport {\n fDynamic,\n fFinishReason,\n fId,\n fMediaType,\n fMessageId,\n fMeta,\n fProviderExecuted,\n fSourceId,\n fTitle,\n fToolCallId,\n fToolName,\n} from './fields.js';\nimport {\n asString,\n isAgentToolOutputErrorWireData,\n isToolInputErrorWireData,\n isToolOutputAvailableWireData,\n} from './wire-data.js';\n\n/**\n * The Vercel codec's `ai-output` descriptors, built from the injected\n * direction-scoped builder.\n * @param builder - The `{ event, stream }` builder curried on `VercelOutput`.\n * @param builder.event - Define a single discrete output event.\n * @param builder.stream - Define a streamed output family (start / delta / end).\n * @returns The output descriptor table the generic output drivers consume.\n */\nexport const outputs = ({ event, stream }: OutputBuilder<VercelOutput>): readonly OutputDescriptor<VercelOutput>[] => [\n // --- streamed families -----------------------------------------------------\n\n stream('text', {\n start: 'text-start',\n delta: 'text-delta',\n end: 'text-end',\n idField: 'id',\n deltaField: 'delta',\n fields: [fId, fMeta],\n }),\n\n stream('reasoning', {\n start: 'reasoning-start',\n delta: 'reasoning-delta',\n end: 'reasoning-end',\n idField: 'id',\n deltaField: 'delta',\n fields: [fId, fMeta],\n }),\n\n // tool-input streams; the close step is a close-or-discrete fallback, the end\n // chunk reconstructs `input` from the accumulated text, and the family also\n // decodes from a non-streamed discrete publish.\n stream('tool-input', {\n start: 'tool-input-start',\n delta: 'tool-input-delta',\n end: 'tool-input-available',\n idField: 'toolCallId',\n deltaField: 'inputTextDelta',\n fields: [fToolCallId, fToolName, fDynamic, fTitle, fProviderExecuted, fMeta],\n onEnd: async (c, core, { h, name }) => {\n try {\n await core.closeStream(c.toolCallId, {\n name,\n data: '',\n codecHeaders: h(c, ['toolCallId', 'toolName', 'providerMetadata']),\n });\n } catch (error: unknown) {\n // closeStream raises InvalidArgument when there is no active stream for\n // this id; fall through to a discrete publish, rethrow anything else.\n if (!(error instanceof Ably.ErrorInfo && errorInfoIs(error, ErrorCode.InvalidArgument))) {\n throw error;\n }\n await core.publishDiscrete({ name, data: c.input, codecHeaders: h(c) });\n }\n },\n decodeEnd: ({ streamId, accumulated, codecHeaders, closingCodecHeaders }) => [\n stripUndefined({\n type: 'tool-input-available' as const,\n toolCallId: streamId,\n toolName: fToolName.read(closingCodecHeaders) || fToolName.read(codecHeaders),\n input: parseJsonOrString(accumulated),\n providerMetadata: fMeta.read(closingCodecHeaders),\n }),\n ],\n decodeDiscrete: ({ codecHeaders, data }) => [\n stripUndefined({\n type: 'tool-input-start' as const,\n toolCallId: fToolCallId.read(codecHeaders),\n toolName: fToolName.read(codecHeaders),\n dynamic: fDynamic.read(codecHeaders),\n title: fTitle.read(codecHeaders),\n providerExecuted: fProviderExecuted.read(codecHeaders),\n providerMetadata: fMeta.read(codecHeaders),\n }),\n stripUndefined({\n type: 'tool-input-available' as const,\n toolCallId: fToolCallId.read(codecHeaders),\n toolName: fToolName.read(codecHeaders),\n input: data,\n providerMetadata: fMeta.read(codecHeaders),\n }),\n ],\n }),\n\n // --- discrete lifecycle events ---------------------------------------------\n\n // `start` injects the encoder's configured messageId as a fallback, so it\n // builds its headers through a hatch rather than a pure descriptor.\n event('start', {\n fields: [fMessageId, jsonField('messageMetadata')],\n encode: async (c, core, { h, name, messageId, opts }) => {\n await core.publishDiscrete(\n { name, data: '', codecHeaders: h({ ...c, messageId: c.messageId ?? messageId }) },\n opts,\n );\n },\n }),\n event('start-step'),\n event('finish-step'),\n event('finish', {\n fields: [fFinishReason, jsonField('messageMetadata')],\n }),\n event('message-metadata', { fields: [jsonField('messageMetadata')] }),\n event('error', {\n data: { encode: (c) => c.errorText, decode: (data) => ({ errorText: asString(data) }) },\n }),\n\n // abort: an ordinary discrete output carrying its reason as wire data. The\n // agent's own stream emits it on abort; run cancellation closes in-flight\n // streams via the encoder's cancelStreams() and terminates via the transport\n // ai-run-end event — this chunk is content, not the run terminator.\n event('abort', {\n data: {\n encode: (c) => c.reason ?? '',\n decode: (data) => (typeof data === 'string' && data ? { reason: data } : {}),\n },\n }),\n\n // --- content parts ---------------------------------------------------------\n\n event('file', {\n fields: [fMediaType, fMeta],\n data: { encode: (c) => c.url, decode: (data) => ({ url: asString(data) }) },\n }),\n event('source-url', {\n fields: [fSourceId, fTitle, fMeta],\n data: { encode: (c) => c.url, decode: (data) => ({ url: asString(data) }) },\n }),\n event('source-document', {\n fields: [fSourceId, fMediaType, strField('title', ''), strField('filename'), fMeta],\n }),\n\n // --- tool lifecycle (discrete) ---------------------------------------------\n\n event('tool-input-error', {\n fields: [fToolCallId, fToolName, fDynamic, fTitle, fProviderExecuted, fMeta],\n data: {\n encode: (c) => ({ errorText: c.errorText, input: c.input }),\n decode: (data) =>\n isToolInputErrorWireData(data) ? { errorText: data.errorText ?? '', input: data.input } : { errorText: '' },\n },\n }),\n event('tool-output-available', {\n fields: [fToolCallId, fDynamic, fProviderExecuted, boolField('preliminary')],\n data: {\n encode: (c) => ({ output: c.output }),\n decode: (data) => (isToolOutputAvailableWireData(data) ? { output: data.output } : {}),\n },\n }),\n event('tool-output-error', {\n fields: [fToolCallId, fDynamic, fProviderExecuted],\n data: {\n encode: (c) => ({ errorText: c.errorText }),\n decode: (data) => ({ errorText: isAgentToolOutputErrorWireData(data) ? (data.errorText ?? '') : '' }),\n },\n }),\n event('tool-approval-request', {\n fields: [fToolCallId, strField('approvalId', '')],\n }),\n event('tool-output-denied', { fields: [fToolCallId] }),\n\n // --- data-* wildcard -------------------------------------------------------\n\n event('data-*', {\n fields: [fId, boolField('transient')],\n ephemeral: (c) => c.transient === true,\n data: { encode: (c) => c.data, decode: (data) => ({ data }) },\n }),\n];\n","/**\n * Shared reducer state: the projection shape, its internal tracker types,\n * `init`, and the message/tracker lookup helpers the per-concern fold modules\n * build on. This module is the base of the reducer's import DAG — the fold\n * modules depend on it; it depends on none of them.\n */\n\nimport type * as AI from 'ai';\n\nimport type { CodecMessage } from '../../core/codec/index.js';\n\n// ---------------------------------------------------------------------------\n// Internal tracker state\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks an in-progress tool part within a UIMessage. Text and reasoning\n * parts don't need this — we write to them directly via partIndex. Tool\n * parts need an extra `inputText` buffer because deltas arrive as raw\n * JSON fragments that must be accumulated before parsing.\n */\nexport interface ToolPartTracker {\n /** Index in the message's parts array. */\n partIndex: number;\n /** Accumulated streaming input text (for JSON parsing on completion). */\n inputText: string;\n}\n\n/** Per-codecMessageId tracking state for in-progress streams within a UIMessage. */\nexport interface MessageTrackers {\n /** Text stream id → partIndex. */\n text: Map<string, number>;\n /** Reasoning stream id → partIndex. */\n reasoning: Map<string, number>;\n /** Tool call id → tracker. */\n tools: Map<string, ToolPartTracker>;\n}\n\n// ---------------------------------------------------------------------------\n// Projection\n// ---------------------------------------------------------------------------\n\n/**\n * The per-Run state produced by the Vercel codec's reducer.\n *\n * The SDK reads only `messages` (via `Codec.getMessages`). The remaining\n * fields are internal to the reducer; they happen to live on the\n * projection because the projection is the only thing the reducer can\n * carry from fold to fold (it has no instance state).\n */\nexport interface VercelProjection {\n /**\n * UIMessages produced or modified in this Run, in publication order,\n * each paired with its codec-message-id. The reducer correlates strictly\n * on `codecMessageId`; `message.id` is preserved verbatim from the source\n * (the AI SDK stream's `start.messageId` for assistants, the caller's id\n * for user messages) and is never used as an identity key.\n */\n messages: CodecMessage<AI.UIMessage>[];\n /** Per-codecMessageId tracker state for streamed parts. Internal — do not access. */\n trackers: Map<string, MessageTrackers>;\n /**\n * Tool-resolution events that arrived before any assistant in this\n * projection had a matching `toolCallId`. Re-evaluated on every\n * subsequent fold so that an out-of-order tool output is folded as\n * soon as the corresponding assistant lands.\n */\n pendingToolResolutions: PendingToolResolution[];\n}\n\n/**\n * A buffered tool resolution waiting for its assistant message to arrive.\n * The reducer scans pending entries after every successful fold so an\n * out-of-order tool output is promoted as soon as the matching assistant\n * is added to the projection.\n */\nexport interface PendingToolResolution {\n /** The codec-message-id of the assistant the resolution targets. */\n targetCodecMessageId: string;\n /** Tool call this resolution targets. */\n toolCallId: string;\n /** Variant of the tool-resolution used to transition the assistant's tool part. */\n resolution:\n | { kind: 'tool-result'; output: unknown }\n | { kind: 'tool-result-error'; message: string }\n | { kind: 'tool-approval-response'; approved: boolean; reason?: string };\n}\n\n/** A located `dynamic-tool` part with its owning message and tracker. */\nexport interface OwnerLookup {\n /** The message owning the tool part. */\n message: AI.UIMessage;\n /** The tracker pointing at the part's index. */\n tracker: ToolPartTracker;\n /** The resolved `dynamic-tool` part itself. */\n part: AI.DynamicToolUIPart;\n}\n\n// ---------------------------------------------------------------------------\n// init\n// ---------------------------------------------------------------------------\n\n/**\n * Build an empty initial projection.\n * @returns A fresh VercelProjection with no messages and no tracker state.\n */\nexport const init = (): VercelProjection => ({\n messages: [],\n trackers: new Map(),\n pendingToolResolutions: [],\n});\n\n// ---------------------------------------------------------------------------\n// Message + tracker helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the assistant message for a codec-message-id, creating an empty\n * placeholder when none exists yet.\n * @param state - Projection to read or extend.\n * @param codecMessageId - The codec-message-id to resolve.\n * @returns The existing or newly-seeded UIMessage for that id.\n */\nexport const ensureMessage = (state: VercelProjection, codecMessageId: string): AI.UIMessage => {\n let entry = state.messages.find((e) => e.codecMessageId === codecMessageId);\n if (!entry) {\n // No source id seen yet — seed the domain `message.id` with the\n // codec-message-id as a fallback. The `start` chunk overwrites it with\n // the stream's `messageId` when the stream provides one.\n entry = { codecMessageId, message: { id: codecMessageId, role: 'assistant', parts: [] } };\n state.messages.push(entry);\n }\n return entry.message;\n};\n\n/**\n * Resolve the stream trackers for a codec-message-id, creating empty maps\n * when none exist yet.\n * @param state - Projection to read or extend.\n * @param messageId - The codec-message-id whose trackers to resolve.\n * @returns The existing or newly-created tracker maps for that id.\n */\nexport const ensureTrackers = (state: VercelProjection, messageId: string): MessageTrackers => {\n let trackers = state.trackers.get(messageId);\n if (!trackers) {\n trackers = { text: new Map(), reasoning: new Map(), tools: new Map() };\n state.trackers.set(messageId, trackers);\n }\n return trackers;\n};\n\n/**\n * Resolve the `dynamic-tool` part tracked for a toolCallId within a message.\n * @param message - The message whose parts to read.\n * @param trackers - The message's tracker maps.\n * @param toolCallId - The tool call to resolve.\n * @returns The tracker and part, or `undefined` if untracked or the part is not a dynamic-tool.\n */\nexport const getToolPart = (\n message: AI.UIMessage,\n trackers: MessageTrackers,\n toolCallId: string,\n): { tracker: ToolPartTracker; part: AI.DynamicToolUIPart } | undefined => {\n const tracker = trackers.tools.get(toolCallId);\n if (!tracker) return undefined;\n const part = message.parts[tracker.partIndex];\n if (part?.type !== 'dynamic-tool') return undefined;\n return { tracker, part };\n};\n","/**\n * File and source content-part folds: file / source-url / source-document.\n * These are independent attachments — each appends a part, never dedups.\n */\n\nimport type * as AI from 'ai';\n\nimport { stripUndefined } from '../../utils.js';\nimport { ensureMessage, type VercelProjection } from './reducer-state.js';\n\n/**\n * Fold a file or source content chunk into the projection.\n * @param state - Projection to fold into.\n * @param chunk - The file, source-url, or source-document chunk.\n * @param messageId - The target codec-message-id.\n * @returns The same projection reference.\n */\nexport const foldContentPart = (\n state: VercelProjection,\n chunk: Extract<AI.UIMessageChunk, { type: 'file' | 'source-url' | 'source-document' }>,\n messageId: string,\n): VercelProjection => {\n const message = ensureMessage(state, messageId);\n\n switch (chunk.type) {\n case 'file': {\n message.parts.push({ type: 'file', mediaType: chunk.mediaType, url: chunk.url });\n return state;\n }\n case 'source-url': {\n message.parts.push(\n stripUndefined({\n type: 'source-url' as const,\n sourceId: chunk.sourceId,\n url: chunk.url,\n title: chunk.title,\n }),\n );\n return state;\n }\n case 'source-document': {\n message.parts.push(\n stripUndefined({\n type: 'source-document' as const,\n sourceId: chunk.sourceId,\n mediaType: chunk.mediaType,\n title: chunk.title,\n filename: chunk.filename,\n }),\n );\n return state;\n }\n }\n};\n","/**\n * data-* part folds. Transient data parts are dropped; persistent ones are\n * appended, or replaced in place when a matching `id` is already present.\n */\n\nimport type * as AI from 'ai';\n\nimport { stripUndefined } from '../../utils.js';\nimport { ensureMessage, type VercelProjection } from './reducer-state.js';\n\n/**\n * Fold a `data-*` chunk into the projection.\n * @param state - Projection to fold into.\n * @param chunk - The data-* chunk.\n * @param messageId - The target codec-message-id.\n * @returns The same projection reference.\n */\nexport const foldDataPart = (\n state: VercelProjection,\n chunk: Extract<AI.UIMessageChunk, { type: `data-${string}` }>,\n messageId: string,\n): VercelProjection => {\n if (chunk.transient) return state;\n\n const message = ensureMessage(state, messageId);\n\n // CAST: chunk.type is `data-${string}` which satisfies DataUIPart, but\n // TypeScript cannot verify the template literal matches a specific\n // UIMessagePart variant at the type level.\n const dataPart = stripUndefined({\n type: chunk.type,\n id: chunk.id,\n data: chunk.data,\n }) as AI.UIMessage['parts'][number];\n\n if (chunk.id !== undefined) {\n const idx = message.parts.findIndex((p) => p.type === chunk.type && 'id' in p && p.id === chunk.id);\n if (idx !== -1) {\n message.parts[idx] = dataPart;\n return state;\n }\n }\n\n message.parts.push(dataPart);\n return state;\n};\n","/**\n * Shared tool part transition logic for the Vercel AI SDK codec.\n *\n * Keeps the tool output state transition logic in one place, reusable by the\n * Vercel codec reducer and any other callers.\n */\n\nimport type * as AI from 'ai';\n\nimport { stripUndefined } from '../../utils.js';\n\n// ---------------------------------------------------------------------------\n// Tool output chunk type\n// ---------------------------------------------------------------------------\n\n/** The set of UIMessageChunk types that represent tool output transitions. */\nexport type ToolOutputChunk = Extract<\n AI.UIMessageChunk,\n { type: 'tool-output-available' | 'tool-output-error' | 'tool-output-denied' | 'tool-approval-request' }\n>;\n\n// ---------------------------------------------------------------------------\n// Tool base helper\n// ---------------------------------------------------------------------------\n\n/** Fields shared by all DynamicToolUIPart state variants. */\ninterface ToolBaseFields {\n type: 'dynamic-tool';\n toolName: string;\n toolCallId: string;\n title?: string;\n providerExecuted?: boolean;\n}\n\n/**\n * Extract the state-independent base fields for a DynamicToolUIPart.\n * Works with both chunks (tool-input-start, etc.) and existing parts.\n * @param source - Any object containing the required tool identity fields.\n * @param source.toolCallId - The tool call identifier.\n * @param source.toolName - The tool name.\n * @param source.title - Optional display title.\n * @param source.providerExecuted - Whether the provider executed the tool.\n * @returns Base fields shared across all DynamicToolUIPart state variants.\n */\nexport const toolBase = (source: {\n toolCallId: string;\n toolName: string;\n title?: string;\n providerExecuted?: boolean;\n}): ToolBaseFields =>\n stripUndefined({\n type: 'dynamic-tool' as const,\n toolCallId: source.toolCallId,\n toolName: source.toolName,\n title: source.title,\n providerExecuted: source.providerExecuted,\n });\n\n// ---------------------------------------------------------------------------\n// Tool part transition\n// ---------------------------------------------------------------------------\n\n/**\n * Transition a DynamicToolUIPart to a new state based on a tool output chunk.\n * Pure function — does not mutate the input part.\n * @param part - The existing tool part to transition.\n * @param chunk - The tool output chunk describing the transition.\n * @returns A new DynamicToolUIPart in the target state.\n */\nexport const transitionToolPart = (part: AI.DynamicToolUIPart, chunk: ToolOutputChunk): AI.DynamicToolUIPart => {\n const base = toolBase(part);\n\n switch (chunk.type) {\n case 'tool-output-available': {\n return stripUndefined({\n ...base,\n state: 'output-available' as const,\n input: part.input,\n output: chunk.output,\n preliminary: chunk.preliminary,\n });\n }\n\n case 'tool-output-error': {\n return {\n ...base,\n state: 'output-error',\n input: part.input,\n errorText: chunk.errorText,\n };\n }\n\n case 'tool-output-denied': {\n return {\n ...base,\n state: 'output-denied',\n input: part.input,\n approval: { id: '', approved: false },\n };\n }\n\n case 'tool-approval-request': {\n return {\n ...base,\n state: 'approval-requested',\n input: part.input,\n approval: { id: chunk.approvalId },\n };\n }\n }\n};\n","/**\n * Client-published input folds and the pending-resolution buffering.\n *\n * Tool resolutions (`ToolResult`, `ToolResultError`, `ToolApprovalResponse`)\n * carry a `codecMessageId` targeting the assistant they amend. When that\n * assistant (or its tool part) has not yet arrived, the resolution is buffered\n * in `pendingToolResolutions` and {@link retryPendingResolutions} re-evaluates\n * it after every subsequent fold.\n */\n\nimport type * as AI from 'ai';\n\nimport type { ReducerMeta, ToolApprovalResponse, ToolResult, ToolResultError } from '../../core/codec/index.js';\nimport type {\n VercelToolApprovalResponsePayload,\n VercelToolResultErrorPayload,\n VercelToolResultPayload,\n} from './events.js';\nimport {\n ensureTrackers,\n getToolPart,\n type OwnerLookup,\n type PendingToolResolution,\n type VercelProjection,\n} from './reducer-state.js';\nimport { toolBase, transitionToolPart } from './tool-transitions.js';\n\n/**\n * Fold a user message into the projection, correlating on the wire\n * codec-message-id (the caller's `message.id` is preserved verbatim). A\n * multi-part user message fans out into one wire event per part, all sharing\n * the codec-message-id — folding appends the incoming parts to the existing\n * entry, reassembling the message part by part. The transport delivers each\n * wire exactly once (its per-message version high-water-mark drops replays),\n * so the merge sees every part once and stays consistent.\n *\n * Optimistic (serial-less) seeds need no special handling here: the transport\n * refolds the node from its log when the echo's serial arrives, rebuilding the\n * projection from a fresh `init` so the seed never coexists with its echo.\n * @param state - Projection to fold into.\n * @param message - The user message (or one decoded part of it) to add or merge.\n * @param meta - Transport-derived metadata carrying the codec-message-id.\n * @returns The same projection reference.\n */\nexport const foldUserMessage = (\n state: VercelProjection,\n message: AI.UIMessage,\n meta: ReducerMeta,\n): VercelProjection => {\n // Correlate the projection entry on the wire codec-message-id; the\n // caller-supplied `message.id` is preserved verbatim and surfaced to the\n // application unchanged. Without a codec-message-id the message has no\n // identity to key on, so it is appended as a fresh entry.\n const codecMessageId = meta.messageId;\n if (codecMessageId === undefined) {\n state.messages.push({ codecMessageId: message.id, message });\n return state;\n }\n const existing = state.messages.find((e) => e.codecMessageId === codecMessageId);\n if (existing === undefined) {\n state.messages.push({ codecMessageId, message });\n } else {\n // Merge by codec-message-id: keep the existing envelope (id and role are\n // stamped identically on every part of one message) and append the\n // incoming parts in fold order — wire serials preserve publish order.\n existing.message.parts.push(...message.parts);\n }\n return state;\n};\n\n/**\n * Fold a client-published `ToolResult`. The input carries\n * `codecMessageId` pointing at the assistant whose `dynamic-tool` part\n * holds the matching `toolCallId`. If the assistant and its matching\n * `dynamic-tool` part are both present, fold directly; otherwise pend\n * until that tool part arrives.\n * @param state - Projection to fold into.\n * @param event - The tool-result input (codecMessageId + domain payload).\n * @returns The same projection reference.\n */\nexport const foldClientToolResult = (\n state: VercelProjection,\n event: ToolResult<VercelToolResultPayload>,\n): VercelProjection => {\n const { toolCallId, output } = event.payload;\n return resolveOrPend(state, event.codecMessageId, toolCallId, { kind: 'tool-result', output });\n};\n\n/**\n * Fold a client-published `ToolResultError`. Mirrors\n * {@link foldClientToolResult} but with the error transition.\n * @param state - Projection to fold into.\n * @param event - The tool-result-error input (codecMessageId + domain payload).\n * @returns The same projection reference.\n */\nexport const foldClientToolResultError = (\n state: VercelProjection,\n event: ToolResultError<VercelToolResultErrorPayload>,\n): VercelProjection => {\n const { toolCallId, message } = event.payload;\n return resolveOrPend(state, event.codecMessageId, toolCallId, { kind: 'tool-result-error', message });\n};\n\n/**\n * Fold a client-published `ToolApprovalResponse`. The input carries\n * `codecMessageId` pointing at the assistant whose `dynamic-tool` part\n * holds the matching `toolCallId`. Approval → `approval-responded`;\n * denial → `output-denied` via {@link transitionToolPart}.\n * @param state - Projection to fold into.\n * @param event - The approval-response input.\n * @returns The same projection reference.\n */\nexport const foldToolApprovalResponse = (\n state: VercelProjection,\n event: ToolApprovalResponse<VercelToolApprovalResponsePayload>,\n): VercelProjection => {\n const { toolCallId, approved, reason } = event.payload;\n return resolveOrPend(state, event.codecMessageId, toolCallId, {\n kind: 'tool-approval-response',\n approved,\n ...(reason === undefined ? {} : { reason }),\n });\n};\n\n/**\n * Apply a resolution when its tool part is present, otherwise buffer it in\n * `pendingToolResolutions` for {@link retryPendingResolutions}.\n * @param state - Projection to fold into.\n * @param codecMessageId - The assistant the resolution targets.\n * @param toolCallId - The tool call being resolved.\n * @param resolution - The resolution variant to apply or buffer.\n * @returns The same projection reference.\n */\nconst resolveOrPend = (\n state: VercelProjection,\n codecMessageId: string,\n toolCallId: string,\n resolution: PendingToolResolution['resolution'],\n): VercelProjection => {\n const owner = findOwner(state, codecMessageId, toolCallId);\n if (owner) {\n applyResolution(owner, toolCallId, resolution);\n } else {\n state.pendingToolResolutions.push({ targetCodecMessageId: codecMessageId, toolCallId, resolution });\n }\n return state;\n};\n\n/**\n * Apply one tool resolution onto its located `dynamic-tool` part, replacing\n * the part with the transitioned shape — the single application point shared\n * by the direct folds and {@link retryPendingResolutions}.\n * @param owner - The located owner (message + tracker + part).\n * @param toolCallId - The tool call being resolved.\n * @param resolution - The resolution variant to apply.\n */\nconst applyResolution = (\n owner: OwnerLookup,\n toolCallId: string,\n resolution: PendingToolResolution['resolution'],\n): void => {\n switch (resolution.kind) {\n case 'tool-result': {\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, {\n type: 'tool-output-available',\n toolCallId,\n output: resolution.output,\n });\n break;\n }\n case 'tool-result-error': {\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, {\n type: 'tool-output-error',\n toolCallId,\n errorText: resolution.message,\n });\n break;\n }\n case 'tool-approval-response': {\n owner.message.parts[owner.tracker.partIndex] = approvalTransition(\n owner.part,\n resolution.approved,\n resolution.reason,\n );\n break;\n }\n }\n};\n\n/**\n * Re-attempt every pending tool resolution against the current projection.\n * Successfully promoted entries are removed from the pending list. Cheap:\n * bounded by the number of pending entries.\n * @param state - Projection to walk and mutate.\n */\nexport const retryPendingResolutions = (state: VercelProjection): void => {\n const next: VercelProjection['pendingToolResolutions'] = [];\n for (const pending of state.pendingToolResolutions) {\n const owner = findOwner(state, pending.targetCodecMessageId, pending.toolCallId);\n if (!owner) {\n next.push(pending);\n continue;\n }\n applyResolution(owner, pending.toolCallId, pending.resolution);\n }\n state.pendingToolResolutions = next;\n};\n\nconst findOwner = (state: VercelProjection, codecMessageId: string, toolCallId: string): OwnerLookup | undefined => {\n const entry = state.messages.find((e) => e.codecMessageId === codecMessageId);\n if (!entry) return undefined;\n const trackers = ensureTrackers(state, codecMessageId);\n const found = getToolPart(entry.message, trackers, toolCallId);\n if (!found) return undefined;\n return { message: entry.message, tracker: found.tracker, part: found.part };\n};\n\n/**\n * Build the next `dynamic-tool` part shape for an approval response.\n *\n * For `approved=true`, transition to `approval-responded` so the AI SDK's\n * multi-step loop will auto-run the tool on the next step.\n * `transitionToolPart` has no shape for this transition, so we synthesize\n * the part directly.\n *\n * For `approved=false`, delegate to `transitionToolPart` with a synthetic\n * `tool-output-denied` chunk so denial mirrors the chunk-driven path.\n * @param part - The existing `dynamic-tool` part being transitioned.\n * @param approved - Whether the user approved the tool execution.\n * @param reason - Optional human-readable reason.\n * @returns The replacement `dynamic-tool` part.\n */\nconst approvalTransition = (\n part: AI.DynamicToolUIPart,\n approved: boolean,\n reason: string | undefined,\n): AI.DynamicToolUIPart => {\n if (approved) {\n return {\n ...toolBase(part),\n state: 'approval-responded',\n input: 'input' in part ? part.input : undefined,\n approval: {\n id: 'approval' in part && part.approval ? part.approval.id : '',\n approved: true,\n ...(reason === undefined ? {} : { reason }),\n },\n };\n }\n return transitionToolPart(part, {\n type: 'tool-output-denied',\n toolCallId: part.toolCallId,\n ...(reason === undefined ? {} : { reason }),\n });\n};\n","/**\n * Lifecycle chunk folds: start, start-step, finish-step, finish, abort,\n * error, message-metadata.\n */\n\nimport type * as AI from 'ai';\n\nimport { ensureMessage, type VercelProjection } from './reducer-state.js';\n\n/**\n * Set a message's metadata from a chunk when both the message exists and the\n * chunk carries metadata. Shared by the `finish` and `message-metadata` cases,\n * which apply it identically. The `start` case is not routed through here — it\n * creates the message via `ensureMessage` first.\n * @param state - Projection holding the message.\n * @param messageId - The target codec-message-id.\n * @param metadata - The chunk's `messageMetadata`, or undefined to leave it unchanged.\n */\nconst applyMessageMetadata = (state: VercelProjection, messageId: string, metadata: AI.UIMessage['metadata']): void => {\n if (metadata === undefined) return;\n const message = state.messages.find((e) => e.codecMessageId === messageId)?.message;\n if (message) message.metadata = metadata;\n};\n\n/**\n * Fold a message-lifecycle chunk into the projection.\n * @param state - Projection to fold into.\n * @param chunk - The lifecycle chunk.\n * @param messageId - The target codec-message-id.\n * @returns The same projection reference.\n */\nexport const foldLifecycle = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'start' | 'start-step' | 'finish-step' | 'finish' | 'abort' | 'error' | 'message-metadata' }\n >,\n messageId: string,\n): VercelProjection => {\n switch (chunk.type) {\n case 'start': {\n // The projection entry is keyed on the wire codec-message-id\n // (`messageId`); every subsequent chunk for this message correlates on\n // that, independent of `message.id`. So we faithfully reproduce the\n // stream's own `messageId` on the reconstructed `UIMessage.id` (the\n // value surfaced to the application) without risk of orphaning later\n // chunks. When the stream omits it, the codec-message-id seeded by\n // `ensureMessage` stands as the fallback id.\n const message = ensureMessage(state, messageId);\n if (chunk.messageId !== undefined) message.id = chunk.messageId;\n if (chunk.messageMetadata !== undefined) message.metadata = chunk.messageMetadata;\n return state;\n }\n case 'start-step': {\n const message = ensureMessage(state, messageId);\n message.parts.push({ type: 'step-start' });\n return state;\n }\n case 'finish-step': {\n // Reset text/reasoning stream trackers so a follow-up step can start\n // new parts with potentially-reused stream ids.\n const trackers = state.trackers.get(messageId);\n if (trackers) {\n trackers.text.clear();\n trackers.reasoning.clear();\n }\n return state;\n }\n case 'finish': {\n applyMessageMetadata(state, messageId, chunk.messageMetadata);\n // Tracker state retained — late events still resolvable; cleanup happens at Run end.\n return state;\n }\n case 'abort':\n case 'error': {\n // No state mutation — run termination is observed via the wire run-end\n // event, not the projection.\n return state;\n }\n case 'message-metadata': {\n applyMessageMetadata(state, messageId, chunk.messageMetadata);\n return state;\n }\n }\n};\n","/**\n * Text and reasoning streaming folds: the {start, delta, end} lifecycle for\n * both `text-*` and `reasoning-*` chunks, which share the same shape.\n */\n\nimport type * as AI from 'ai';\n\nimport { ensureMessage, ensureTrackers, type VercelProjection } from './reducer-state.js';\n\n/**\n * Fold a text or reasoning streaming chunk into the projection.\n * @param state - Projection to fold into.\n * @param chunk - The text/reasoning start, delta, or end chunk.\n * @param messageId - The target codec-message-id.\n * @returns The same projection reference.\n */\nexport const foldTextOrReasoning = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'text-start' | 'text-delta' | 'text-end' | 'reasoning-start' | 'reasoning-delta' | 'reasoning-end' }\n >,\n messageId: string,\n): VercelProjection => {\n const message = ensureMessage(state, messageId);\n const trackers = ensureTrackers(state, messageId);\n\n const isText = chunk.type.startsWith('text-');\n const partType = isText ? 'text' : 'reasoning';\n const activeMap = isText ? trackers.text : trackers.reasoning;\n\n switch (chunk.type) {\n case 'text-start':\n case 'reasoning-start': {\n activeMap.set(chunk.id, message.parts.length);\n message.parts.push({ type: partType, text: '' });\n return state;\n }\n case 'text-delta':\n case 'reasoning-delta': {\n const idx = activeMap.get(chunk.id);\n if (idx === undefined) return state;\n const part = message.parts[idx];\n if (part?.type === partType) {\n part.text += chunk.delta;\n }\n return state;\n }\n case 'text-end':\n case 'reasoning-end': {\n activeMap.delete(chunk.id);\n return state;\n }\n }\n};\n","/**\n * Tool-input streaming folds: tool-input-start / -delta / -available / -error.\n * Tool deltas arrive as raw JSON fragments accumulated in the tracker's\n * `inputText` buffer and parsed on each delta.\n */\n\nimport type * as AI from 'ai';\n\nimport { parseJson } from '../../utils.js';\nimport { ensureMessage, ensureTrackers, getToolPart, type VercelProjection } from './reducer-state.js';\nimport { toolBase } from './tool-transitions.js';\n\n/**\n * Fold a tool-input streaming chunk into the projection.\n * @param state - Projection to fold into.\n * @param chunk - The tool-input start, delta, available, or error chunk.\n * @param messageId - The target codec-message-id.\n * @returns The same projection reference.\n */\nexport const foldToolInput = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'tool-input-start' | 'tool-input-delta' | 'tool-input-available' | 'tool-input-error' }\n >,\n messageId: string,\n): VercelProjection => {\n const message = ensureMessage(state, messageId);\n const trackers = ensureTrackers(state, messageId);\n\n switch (chunk.type) {\n case 'tool-input-start': {\n const partIndex = message.parts.length;\n message.parts.push({ ...toolBase(chunk), state: 'input-streaming', input: undefined });\n trackers.tools.set(chunk.toolCallId, { partIndex, inputText: '' });\n return state;\n }\n case 'tool-input-delta': {\n const tracker = trackers.tools.get(chunk.toolCallId);\n if (!tracker) return state;\n tracker.inputText += chunk.inputTextDelta;\n\n const parsedInput = parseJson(tracker.inputText);\n\n const found = getToolPart(message, trackers, chunk.toolCallId);\n if (!found) return state;\n message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'input-streaming',\n input: parsedInput,\n };\n return state;\n }\n case 'tool-input-available': {\n const found = getToolPart(message, trackers, chunk.toolCallId);\n if (!found) return state;\n message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'input-available',\n input: chunk.input,\n };\n return state;\n }\n case 'tool-input-error': {\n const found = getToolPart(message, trackers, chunk.toolCallId);\n if (found) {\n message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'output-error',\n input: chunk.input,\n errorText: chunk.errorText,\n };\n } else {\n const partIndex = message.parts.length;\n message.parts.push({\n ...toolBase(chunk),\n state: 'output-error',\n input: chunk.input,\n errorText: chunk.errorText,\n });\n trackers.tools.set(chunk.toolCallId, { partIndex, inputText: '' });\n }\n return state;\n }\n }\n};\n","/**\n * Agent-published tool-output transitions: tool-output-available /\n * tool-output-error / tool-output-denied / tool-approval-request.\n */\n\nimport type * as AI from 'ai';\n\nimport {\n ensureMessage,\n ensureTrackers,\n getToolPart,\n type OwnerLookup,\n type VercelProjection,\n} from './reducer-state.js';\nimport { transitionToolPart } from './tool-transitions.js';\n\n/**\n * Locate the `dynamic-tool` part for a `toolCallId` anywhere in the projection.\n * Agent-emitted second-pass tool outputs (after an approved tool runs) are\n * stamped with a fresh codec-message-id that differs from the assistant holding\n * the tool call, so they can't be found via `meta.messageId` — they fold onto\n * whichever message holds the matching tool call (created in the first pass or\n * by an approval response).\n * @param state - Projection to scan.\n * @param toolCallId - The tool call to locate.\n * @returns The owning message, tracker, and part, or `undefined` if absent.\n */\nconst findToolPartOwner = (state: VercelProjection, toolCallId: string): OwnerLookup | undefined => {\n for (const entry of state.messages) {\n const trackers = state.trackers.get(entry.codecMessageId);\n if (!trackers) continue;\n const found = getToolPart(entry.message, trackers, toolCallId);\n if (found) return { message: entry.message, tracker: found.tracker, part: found.part };\n }\n return undefined;\n};\n\n/**\n * Fold an agent-published tool-output chunk into the projection.\n * @param state - Projection to fold into.\n * @param chunk - The tool-output-available/-error/-denied or tool-approval-request chunk.\n * @param messageId - The target codec-message-id (used for the approval-request / denied paths).\n * @returns The same projection reference.\n */\nexport const foldToolOutput = (\n state: VercelProjection,\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'tool-output-available' | 'tool-output-error' | 'tool-output-denied' | 'tool-approval-request' }\n >,\n messageId: string,\n): VercelProjection => {\n // `tool-output-available` / `tool-output-error` after an approved tool runs\n // are emitted by streamText's continuation pass under a fresh\n // codec-message-id that differs from the assistant holding the tool call.\n // Resolve the owning part by toolCallId across the whole projection so the\n // output folds onto the original message. Deliberately do NOT materialise\n // `messageId` first — that would leave a phantom empty message behind the\n // fresh id. Drop on miss: a tool output with no matching tool call has no\n // anchor to attach to.\n if (chunk.type === 'tool-output-available' || chunk.type === 'tool-output-error') {\n const owner = findToolPartOwner(state, chunk.toolCallId);\n if (!owner) return state;\n owner.message.parts[owner.tracker.partIndex] = transitionToolPart(owner.part, chunk);\n return state;\n }\n\n // `tool-approval-request` (first pass) creates the part on the run's own\n // message; `tool-output-denied` transitions that same part. Both key on the\n // stamped messageId.\n const message = ensureMessage(state, messageId);\n const trackers = ensureTrackers(state, messageId);\n\n const found = getToolPart(message, trackers, chunk.toolCallId);\n if (!found) return state;\n\n message.parts[found.tracker.partIndex] = transitionToolPart(found.part, chunk);\n return state;\n};\n","/**\n * Vercel AI SDK reducer.\n *\n * Pure `(init, fold)` over the `VercelInput | VercelOutput` union. Folds\n * input variants (user-message, tool-result, tool-result-error,\n * tool-approval-response) and `UIMessageChunk` outputs into a\n * VercelProjection holding `UIMessage[]` plus internal stream-tracker\n * state.\n *\n * The reducer is stateless: every fold is `(state, event, meta) → state'`,\n * with no instance state. Mutation in place is allowed — the projection\n * is single-owner.\n *\n * The reducer does not dedup or reorder. The transport sequences events\n * canonically — ascending by wire serial across messages, in decode order\n * within a wire — and delivers each exactly once, so the reducer folds\n * unconditionally. Last-writer-wins for events competing over the same\n * logical state (e.g. two `tool-output-available` for one `toolCallId`)\n * falls out of fold order: the highest-serial event folds last.\n *\n * Client-published tool resolutions (`ToolResult`, `ToolResultError`,\n * `ToolApprovalResponse`) carry `codecMessageId` targeting the assistant\n * they amend; the reducer applies the resolution onto that assistant's\n * `dynamic-tool` part directly. If the assistant has not yet arrived in\n * the projection (out-of-order delivery), the resolution is buffered in\n * `pendingToolResolutions` and re-evaluated on each subsequent fold.\n *\n * This file is the reducer's public facade and dispatch: `init`,\n * `getMessages`, `fold`, and the output-chunk router. The per-concern fold\n * logic lives in the sibling `fold-*` modules over a shared `reducer-state`\n * base; the import graph is an acyclic DAG rooted here.\n */\n\nimport type * as AI from 'ai';\n\nimport type { CodecEvent, CodecMessage, ReducerMeta } from '../../core/codec/index.js';\nimport type { VercelInput, VercelOutput } from './events.js';\nimport { foldContentPart } from './fold-content.js';\nimport { foldDataPart } from './fold-data.js';\nimport {\n foldClientToolResult,\n foldClientToolResultError,\n foldToolApprovalResponse,\n foldUserMessage,\n retryPendingResolutions,\n} from './fold-input.js';\nimport { foldLifecycle } from './fold-lifecycle.js';\nimport { foldTextOrReasoning } from './fold-text.js';\nimport { foldToolInput } from './fold-tool-input.js';\nimport { foldToolOutput } from './fold-tool-output.js';\nimport type { VercelProjection } from './reducer-state.js';\n\n// ---------------------------------------------------------------------------\n// fold\n// ---------------------------------------------------------------------------\n\n/**\n * Fold one input or output event into the projection. Mutates and returns\n * `state`.\n *\n * The transport invokes `fold` exactly once per event, in canonical order,\n * so the reducer folds unconditionally — no dedup or high-water-mark here.\n * Competing events resolve by order (the highest-serial event folds last\n * and wins). Orphan events (e.g. tool-output for an unknown toolCallId) are\n * dropped silently inside the per-variant fold helpers.\n * @param state - Projection to fold into (may be mutated in place).\n * @param event - Input or output event to fold.\n * @param meta - Transport-derived metadata (serial, optional messageId).\n * @returns The same projection reference, possibly mutated.\n */\nexport const fold = (\n state: VercelProjection,\n event: CodecEvent<VercelInput, VercelOutput>,\n meta: ReducerMeta,\n): VercelProjection => {\n if (event.direction === 'input') {\n const input = event.event;\n switch (input.kind) {\n case 'user-message': {\n foldUserMessage(state, input.message, meta);\n break;\n }\n case 'regenerate': {\n // Regenerate input — wire-only signal. Carries no projection state;\n // the agent reads `target` / `parent` from the wire headers via\n // the input-event lookup path. No fold work to do here.\n break;\n }\n case 'tool-result': {\n foldClientToolResult(state, input);\n break;\n }\n case 'tool-result-error': {\n foldClientToolResultError(state, input);\n break;\n }\n case 'tool-approval-response': {\n foldToolApprovalResponse(state, input);\n break;\n }\n }\n } else {\n foldChunk(state, event.event, meta);\n }\n\n // Re-evaluate pending tool resolutions in case the just-folded event\n // produced the assistant they were waiting on. Cheap when the list is\n // empty (the common case).\n if (state.pendingToolResolutions.length > 0) {\n retryPendingResolutions(state);\n }\n\n return state;\n};\n\n// ---------------------------------------------------------------------------\n// UIMessageChunk dispatch\n// ---------------------------------------------------------------------------\n\nconst foldChunk = (state: VercelProjection, chunk: VercelOutput, meta: ReducerMeta): VercelProjection => {\n const messageId = meta.messageId;\n if (messageId === undefined) {\n // Without a target codec-message-id, a chunk has nowhere to land. Drop.\n return state;\n }\n\n switch (chunk.type) {\n case 'start':\n case 'start-step':\n case 'finish-step':\n case 'finish':\n case 'abort':\n case 'error':\n case 'message-metadata': {\n return foldLifecycle(state, chunk, messageId);\n }\n\n case 'text-start':\n case 'text-delta':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-delta':\n case 'reasoning-end': {\n return foldTextOrReasoning(state, chunk, messageId);\n }\n\n case 'tool-input-start':\n case 'tool-input-delta':\n case 'tool-input-available':\n case 'tool-input-error': {\n return foldToolInput(state, chunk, messageId);\n }\n\n case 'tool-output-available':\n case 'tool-output-error':\n case 'tool-output-denied':\n case 'tool-approval-request': {\n return foldToolOutput(state, chunk, messageId);\n }\n\n case 'file':\n case 'source-url':\n case 'source-document': {\n return foldContentPart(state, chunk, messageId);\n }\n\n default: {\n if (chunk.type.startsWith('data-')) {\n return foldDataPart(state, chunk, messageId);\n }\n return state;\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// getMessages\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the UIMessage list from a projection, each paired with its\n * codec-message-id. Client-published tool resolutions amend existing\n * assistants in place via `kind: 'tool-result'` etc. — they never\n * materialise as their own UIMessage in the projection, so no filtering is\n * needed here.\n * @param projection - Projection produced by `init` + repeated `fold` calls.\n * @returns The visible messages with their codec-message-ids, in publication order.\n */\nexport const getMessages = (projection: VercelProjection): CodecMessage<AI.UIMessage>[] => projection.messages;\n\nexport { init, type VercelProjection } from './reducer-state.js';\n","/**\n * Vercel AI SDK codec — `UIMessageCodec`.\n *\n * Assembled by `defineCodec` from the codec's parts: the reducer\n * (`init`/`fold`/`getMessages`), the declarative output and input descriptor\n * tables (`outputs` / `inputs`, each a builder function `defineCodec` injects\n * the direction-scoped builder into), and the decode lifecycle policy.\n * `defineCodec` builds the generic encoder/decoder and merges the well-known\n * input factories internally.\n *\n * ```ts\n * import { UIMessageCodec } from '@ably/ai-transport/vercel';\n *\n * const encoder = UIMessageCodec.createEncoder(writer, options);\n * const decoder = UIMessageCodec.createDecoder();\n * const projection = UIMessageCodec.init();\n * ```\n */\n\nimport { defineCodec } from '../../core/codec/index.js';\nimport { createVercelDecodeLifecycle } from './decode-lifecycle.js';\nimport type { VercelInput, VercelOutput } from './events.js';\nimport { inputs } from './inputs.js';\nimport { outputs } from './outputs.js';\nimport { fold, getMessages, init } from './reducer.js';\n\n/**\n * Vercel AI SDK codec implementing\n * `Codec<VercelInput, VercelOutput, VercelProjection, UIMessage>`. `VercelProjection`\n * and `UIMessage` are inferred from the reducer.\n */\nexport const UIMessageCodec = defineCodec<VercelInput, VercelOutput>()({\n // Spec: AIT-CT1a3, AIT-ST1a3 — registers this codec as an Ably agent.\n adapterTag: 'vercel-ai-sdk-ui-message',\n reducer: { init, fold, getMessages },\n output: outputs,\n input: inputs,\n decodeLifecycle: createVercelDecodeLifecycle,\n});\n\nexport type { VercelInput, VercelOutput } from './events.js';\nexport { type VercelProjection } from './reducer.js';\n","/**\n * Type-safe EventEmitter wrapping Ably's internal EventEmitter.\n *\n * Takes a single `EventsMap` type parameter — an interface mapping event names\n * to payload types — rather than Ably's three type parameters. Adapted from\n * the ably-chat-js SDK.\n *\n * ```ts\n * interface MyEvents {\n * reaction: { emoji: string };\n * status: { online: boolean };\n * }\n *\n * const emitter = new EventEmitter<MyEvents>(logger);\n * emitter.on('reaction', (event) => console.log(event.emoji));\n * emitter.emit('reaction', { emoji: '👍' });\n * ```\n */\n\nimport * as Ably from 'ably';\n\nimport type { Logger } from './logger.js';\n\n/** Callback receiving a union of all possible event payloads. */\ntype Callback<EventsMap> = (arg: EventsMap[keyof EventsMap]) => void;\n\n/** Callback receiving the payload for a single event type. */\ntype CallbackSingle<K> = (arg: K) => void;\n\n/**\n * Type-safe interface for the Ably EventEmitter, parameterized by an EventsMap\n * that maps event names to their payload types.\n */\ninterface InterfaceEventEmitter<EventsMap> extends Ably.EventEmitter<Callback<EventsMap>, void, keyof EventsMap> {\n /** Emit an event with a type-safe payload. Payload is optional for `undefined`-typed events. */\n emit<K extends keyof EventsMap>(\n event: K,\n ...args: EventsMap[K] extends undefined ? [EventsMap[K]?] : [EventsMap[K]]\n ): void;\n\n /** Subscribe to a single event with a typed callback. */\n on<K extends keyof EventsMap>(event: K, callback: CallbackSingle<EventsMap[K]>): void;\n /** Subscribe to two events with a union-typed callback. */\n on<K1 extends keyof EventsMap, K2 extends keyof EventsMap>(\n events: [K1, K2],\n callback: CallbackSingle<EventsMap[K1] | EventsMap[K2]>,\n ): void;\n /** Subscribe to three events with a union-typed callback. */\n on<K1 extends keyof EventsMap, K2 extends keyof EventsMap, K3 extends keyof EventsMap>(\n events: [K1, K2, K3],\n callback: CallbackSingle<EventsMap[K1] | EventsMap[K2] | EventsMap[K3]>,\n ): void;\n /** Subscribe to an array of events. */\n on(events: (keyof EventsMap)[], callback: Callback<EventsMap>): void;\n /** Subscribe to all events. */\n on(callback: Callback<EventsMap>): void;\n\n /** Unsubscribe a callback from a specific event. */\n off<K extends keyof EventsMap>(event: K, listener: CallbackSingle<EventsMap[K]>): void;\n /** Unsubscribe a callback from all events, or remove all listeners if no callback provided. */\n off(listener?: Callback<EventsMap>): void;\n}\n\n/**\n * Bridge from our {@link Logger} to the Ably EventEmitter's internal logger\n * contract. Ably's EventEmitter calls `logger.logAction(level, action, message)`\n * when a listener throws — we route that to our Logger's `error` method.\n * @param logger - The application logger to delegate to.\n * @returns An object satisfying the Ably EventEmitter's logger interface.\n */\nconst toAblyLogger = (logger: Logger): unknown => ({\n logAction: (_level: number, action: string, message?: string) => {\n logger.error(action, { detail: message });\n },\n shouldLog: () => true,\n});\n\n// CAST: Access Ably's internal EventEmitter constructor. Not publicly exported\n// but available to other Ably SDKs. Ably always catches listener exceptions\n// internally; the logger parameter ensures those caught exceptions are logged\n// rather than silently swallowed.\nconst InternalEventEmitter: new <EventsMap>(logger: unknown) => InterfaceEventEmitter<EventsMap> = (\n Ably.Realtime as unknown as { EventEmitter: new <EventsMap>(logger: unknown) => InterfaceEventEmitter<EventsMap> }\n).EventEmitter;\n\n/**\n * Type-safe EventEmitter based on Ably's internal EventEmitter.\n *\n * Provides the same semantics as {@link Ably.EventEmitter} (error isolation\n * between listeners, synchronous dispatch) but with a single `EventsMap` type\n * parameter for ergonomic type safety.\n *\n * Requires a {@link Logger} so that listener exceptions are routed through\n * the application's logging infrastructure rather than silently swallowed.\n */\nexport class EventEmitter<EventsMap> extends InternalEventEmitter<EventsMap> {\n /**\n * Create a new EventEmitter.\n * @param logger - Application logger. Listener exceptions are logged at error level.\n */\n constructor(logger: Logger) {\n super(toAblyLogger(logger));\n }\n}\n","import * as Ably from 'ably';\n\nimport { ErrorCode } from './errors.js';\n\n/**\n * Structured logger with leveled output and hierarchical context.\n * Implementations filter messages by level and delegate to a {@link LogHandler}.\n */\nexport interface Logger {\n /**\n * Log a message at the trace level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n trace(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the debug level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n debug(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the info level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n info(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the warn level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n warn(message: string, context?: LogContext): void;\n\n /**\n * Log a message at the error level.\n * @param message The message to log.\n * @param context The context of the log message as key-value pairs.\n */\n error(message: string, context?: LogContext): void;\n\n /**\n * Creates a new logger with a context that will be merged with any context provided to individual log calls.\n * The context will be overridden by any matching keys in the individual log call's context.\n * @param context The context to use for all log calls.\n * @returns A new logger instance with the context.\n */\n withContext(context: LogContext): Logger;\n}\n\n/**\n * Represents the different levels of logging that can be used.\n */\nexport enum LogLevel {\n /**\n * Something routine and expected has occurred. This level will provide logs for the vast majority of operations\n * and function calls.\n */\n Trace = 'trace',\n\n /**\n * Development information, messages that are useful when trying to debug library behavior,\n * but superfluous to normal operation.\n */\n Debug = 'debug',\n\n /**\n * Informational messages. Operationally significant to the library but not out of the ordinary.\n */\n Info = 'info',\n\n /**\n * Anything that is not immediately an error, but could cause unexpected behavior in the future. For example,\n * passing an invalid value to an option. Indicates that some action should be taken to prevent future errors.\n */\n Warn = 'warn',\n\n /**\n * A given operation has failed and cannot be automatically recovered. The error may threaten the continuity\n * of operation.\n */\n Error = 'error',\n\n /**\n * No logging will be performed.\n */\n Silent = 'silent',\n}\n\n/**\n * Represents the context of a log message.\n * It is an object of key-value pairs that can be used to provide additional context to a log message.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type LogContext = Record<string, any>;\n\n/**\n * A function that can be used to handle log messages.\n * @param message The message to log.\n * @param level The log level of the message.\n * @param context The context of the log message as key-value pairs.\n */\nexport type LogHandler = (message: string, level: LogLevel, context?: LogContext) => void;\n\n/**\n * A simple console logger that logs messages to the console.\n * @param message The message to log.\n * @param level The log level of the message.\n * @param context - The context of the log message as key-value pairs.\n */\nexport const consoleLogger = (message: string, level: LogLevel, context?: LogContext) => {\n const contextString = context ? `, context: ${JSON.stringify(context)}` : '';\n const formattedMessage = `[${new Date().toISOString()}] ${level.valueOf().toUpperCase()} ably-ai-transport: ${message}${contextString}`;\n\n switch (level) {\n case LogLevel.Trace:\n case LogLevel.Debug: {\n console.log(formattedMessage);\n break;\n }\n case LogLevel.Info: {\n console.info(formattedMessage);\n break;\n }\n case LogLevel.Warn: {\n console.warn(formattedMessage);\n break;\n }\n case LogLevel.Error: {\n console.error(formattedMessage);\n break;\n }\n case LogLevel.Silent: {\n break;\n }\n }\n};\n\n/**\n * Options for creating a logger.\n */\nexport interface LoggerOptions {\n /**\n * The handler that receives formatted log messages. Defaults to {@link consoleLogger} when omitted.\n */\n logHandler?: LogHandler;\n /**\n * The minimum level to emit; messages below this level are suppressed. Must be a valid {@link LogLevel}, otherwise logger creation throws.\n */\n logLevel: LogLevel;\n}\n\n/**\n * Creates a {@link Logger} from the given options.\n * @param options The handler and minimum level for the logger.\n * @returns A logger that filters by level and delegates to the handler.\n * @throws {@link Ably.ErrorInfo} with {@link ErrorCode.InvalidArgument} if `options.logLevel` is not a recognised {@link LogLevel}.\n */\nexport const makeLogger = (options: LoggerOptions): Logger => {\n const logHandler = options.logHandler ?? consoleLogger;\n\n return new DefaultLogger(logHandler, options.logLevel);\n};\n\n/**\n * A convenient list of log levels as numbers that can be used for easier comparison.\n */\nenum LogLevelNumber {\n Trace = 0,\n Debug = 1,\n Info = 2,\n Warn = 3,\n Error = 4,\n Silent = 5,\n}\n\n/**\n * A mapping of log levels to their numeric equivalents.\n */\nconst logLevelNumberMap = new Map<LogLevel, LogLevelNumber>([\n [LogLevel.Trace, LogLevelNumber.Trace],\n [LogLevel.Debug, LogLevelNumber.Debug],\n [LogLevel.Info, LogLevelNumber.Info],\n [LogLevel.Warn, LogLevelNumber.Warn],\n [LogLevel.Error, LogLevelNumber.Error],\n [LogLevel.Silent, LogLevelNumber.Silent],\n]);\n\n/**\n * A default logger implementation.\n */\nclass DefaultLogger implements Logger {\n private readonly _handler: LogHandler;\n private readonly _levelNumber: LogLevelNumber;\n private readonly _context?: LogContext;\n\n constructor(handler: LogHandler, level: LogLevel, context?: LogContext) {\n this._handler = handler;\n this._context = context;\n\n const levelNumber = logLevelNumberMap.get(level);\n if (levelNumber === undefined) {\n throw new Ably.ErrorInfo(`unable to create logger; invalid log level: ${level}`, ErrorCode.InvalidArgument, 400);\n }\n\n this._levelNumber = levelNumber;\n }\n\n trace(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Trace, LogLevelNumber.Trace, context);\n }\n\n debug(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Debug, LogLevelNumber.Debug, context);\n }\n\n info(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Info, LogLevelNumber.Info, context);\n }\n\n warn(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Warn, LogLevelNumber.Warn, context);\n }\n\n error(message: string, context?: LogContext): void {\n this._write(message, LogLevel.Error, LogLevelNumber.Error, context);\n }\n\n withContext(context: LogContext): Logger {\n // Get the original log level by finding the key in logLevelNumberMap that matches this._levelNumber.\n // The Error fallback is defensive and unreachable in practice: _levelNumber always originates from the map.\n const originalLevel =\n [...logLevelNumberMap.entries()].find(([, value]) => value === this._levelNumber)?.[0] ?? LogLevel.Error;\n\n return new DefaultLogger(this._handler, originalLevel, this._mergeContext(context));\n }\n\n private _write(message: string, level: LogLevel, levelNumber: LogLevelNumber, context?: LogContext): void {\n if (levelNumber >= this._levelNumber) {\n this._handler(message, level, this._mergeContext(context));\n }\n }\n\n private _mergeContext(context?: LogContext): LogContext | undefined {\n if (!this._context) {\n return context ?? undefined;\n }\n\n return context ? { ...this._context, ...context } : this._context;\n }\n}\n","/**\n * Shared tool-part type guard for the Vercel layer.\n *\n * The codec normalises every tool part to the `dynamic-tool` shape, but the AI\n * SDK emits `tool-${name}` parts for statically-declared tools. Both shapes\n * carry `toolCallId` and `state`. The guard accepts either representation so\n * the transport's unresolved-tool detection and the React overlay merge can\n * match tool parts uniformly — and so the cross-representation rule lives in\n * one place rather than being re-spelled per call site.\n */\n\nimport type * as AI from 'ai';\n\n/** A UIMessage tool part in either the `dynamic-tool` or `tool-${name}` representation. */\nexport type ToolPart = AI.DynamicToolUIPart | AI.ToolUIPart;\n\n/**\n * Whether a UIMessage part is a tool part of either representation. The\n * `toolCallId`/`state` shape check is defensive against a future AI SDK release\n * introducing a non-tool variant under the `tool-` prefix (none exists today).\n * @param part - The UIMessage part to inspect.\n * @returns True when the part is a tool part.\n */\nexport const isToolPart = (part: AI.UIMessage['parts'][number]): part is ToolPart =>\n (part.type === 'dynamic-tool' || part.type.startsWith('tool-')) && 'toolCallId' in part && 'state' in part;\n","/**\n * Vercel-owned per-run output stream.\n *\n * Builds the `ReadableStream<UIMessageChunk>` that `useChat` consumes by\n * subscribing to the session Tree's `output` and `run` events for a single\n * run. Streaming is a useChat-integration concern, so it lives in the Vercel\n * layer rather than the generic core: the core Tree is the fan-out point, and\n * this projects its events into the shape `useChat` expects.\n *\n * Close semantics — the stream the consumer reads ends when:\n * - a **terminal chunk** (`finish` / `error` / `abort`) is folded for the run.\n * This is the signal `useChat`'s `sendAutomaticallyWhen` waits for, and it\n * fires even when the run merely *suspends* for a tool call (a tool-calls\n * `finish` ends the consumer stream while the core run stays alive in the\n * Tree for the continuation); or\n * - the run reaches `run-end`, which is always terminal (safety net for a run\n * that ends without emitting a terminal chunk). A `run-suspend` keeps the\n * core run alive and does not close the consumer stream.\n *\n * It errors when the session emits a non-fatal `error` (e.g. channel\n * continuity loss, or an agent-reported mid-run error), so the consumer's\n * reader rejects rather than hanging.\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport type { ClientSession } from '../../core/transport/types.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';\n\ntype VercelSession = ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;\n\n/**\n * Whether a Vercel output chunk ends the consumer-facing stream. The terminal\n * variants are `finish` (end of an LLM turn, including tool-calls), `error`,\n * and `abort`.\n * @param output - The decoded output chunk.\n * @returns True when the chunk should close the consumer stream.\n */\nconst isTerminalChunk = (output: VercelOutput): boolean =>\n output.type === 'finish' || output.type === 'error' || output.type === 'abort';\n\n/** A consumer-facing run output stream plus the handle to close it externally. */\ninterface RunOutputStream {\n /** The stream of decoded outputs for the run, as `useChat` consumes it. */\n stream: ReadableStream<VercelOutput>;\n /** Close the stream now (e.g. on local cancel). Idempotent. */\n close: () => void;\n}\n\n/**\n * Create a consumer-facing output stream for a send, sourced from the session\n * Tree's events. See the module docs for close/error semantics. The returned\n * `close` lets the caller settle the stream for conditions the Tree doesn't\n * surface (local cancel). Session errors are wired internally to error the\n * stream.\n *\n * Outputs route PURELY by the triggering input's codec-message-id — the key the\n * client owns from send time, before the agent mints the runId. The agent's\n * minted runId is supplied as a promise so the run-end safety-net can still\n * close the stream once it resolves.\n * @param session - The Vercel client session whose Tree to observe.\n * @param runId - The agent-minted runId, resolved when run-start is observed.\n * Used only by the run-end safety-net; routing keys on `inputCodecMessageId`.\n * @param inputCodecMessageId - The triggering input's codec-message-id. An\n * output routes to this stream when it carries this id.\n * @returns The stream and its external close handle.\n */\nexport const createRunOutputStream = (\n session: VercelSession,\n runId: Promise<string>,\n inputCodecMessageId: string,\n): RunOutputStream => {\n const holder: { controller?: ReadableStreamDefaultController<VercelOutput> } = {};\n // ReadableStream's start() runs synchronously, so the controller is captured\n // before the constructor returns.\n const unsubscribe: (() => void)[] = [];\n const stream = new ReadableStream<VercelOutput>({\n start: (controller) => {\n holder.controller = controller;\n },\n cancel: () => {\n teardown();\n },\n });\n const { controller } = holder;\n if (!controller) {\n throw new Ably.ErrorInfo(\n 'unable to create run stream; ReadableStream start() was not called synchronously',\n ErrorCode.SessionSubscriptionError,\n 500,\n );\n }\n\n // The agent mints the runId; learn it (for the run-end safety-net) when the\n // promise resolves. Fire-and-forget: the stream opens on the input key, so a\n // never-resolving runId only forgoes the safety-net, not normal close.\n let resolvedRunId: string | undefined;\n // Best-effort: failure only disables the run-end safety-net; normal close is\n // the terminal chunk. `void` discards the promise (no await needed here).\n void runId.then(\n (id) => {\n resolvedRunId = id;\n },\n () => {\n /* session closed before run-start; safety-net stays disarmed */\n },\n );\n\n let settled = false;\n const teardown = (): void => {\n for (const unsub of unsubscribe) unsub();\n unsubscribe.length = 0;\n };\n // Settle the stream at most once: run the controller action (close/error),\n // swallow the throw if the consumer already cancelled, then tear down.\n const settle = (action: () => void): void => {\n if (settled) return;\n settled = true;\n try {\n action();\n } catch {\n /* consumer already cancelled the stream */\n }\n teardown();\n };\n const close = (): void => {\n settle(() => {\n controller.close();\n });\n };\n const error = (reason: Ably.ErrorInfo): void => {\n settle(() => {\n controller.error(reason);\n });\n };\n\n unsubscribe.push(\n session.tree.on('output', (event) => {\n if (event.inputCodecMessageId !== inputCodecMessageId) return;\n for (const output of event.events) {\n try {\n controller.enqueue(output);\n } catch {\n close();\n return;\n }\n if (isTerminalChunk(output)) {\n close();\n return;\n }\n }\n }),\n session.tree.on('run', (event) => {\n // run-end is always terminal; a run-suspend (event.type === 'suspend')\n // keeps the core run alive and must not close the consumer stream. Match\n // against the resolved runId once the agent has minted it.\n if (event.type === 'end' && resolvedRunId !== undefined && event.runId === resolvedRunId) {\n close();\n }\n }),\n session.on('error', (reason) => {\n error(reason);\n }),\n );\n\n return { stream, close };\n};\n","/**\n * Vercel chat transport: wraps a core ClientSession to satisfy the\n * ChatTransport interface that useChat expects.\n *\n * This is a thin adapter — the real logic lives in the core client session.\n * The chat transport maps Vercel's sendMessages/reconnectToStream contract\n * to the core session's send/cancel methods.\n *\n * useChat manages message state before calling sendMessages:\n * - submit-message (new): appends the new user message, passes the full array\n * - submit-message (edit): truncates after the edited message, replaces it,\n * passes the truncated array with messageId set\n * - regenerate-message: truncates after the target, passes the truncated array\n *\n * The adapter uses `(trigger, last-message role)` to determine the\n * history/messages split:\n * - submit-message + last message is a user message: that last message is new\n * (publish to channel), rest is history. A new submit and an edit both take\n * this path — an edit just carries a messageId.\n * - submit-message + last message is an assistant already in the tree\n * (continuation): no new messages, entire array is history\n * - regenerate-message: no new messages, entire array is history\n *\n * For an edit (submit-message with messageId) and for forking off an\n * unresolved tool call, the adapter computes fork metadata (forkOf/parent)\n * from the conversation tree so the server can place the response on the\n * correct branch. Regeneration fork metadata is NOT computed here —\n * `View.regenerate` derives forkOf/parent from the tree itself.\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport type { CodecMessage } from '../../core/codec/index.js';\nimport type { ActiveRun, ClientSession, SendOptions } from '../../core/transport/types.js';\nimport { ErrorCode } from '../../errors.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport { LogLevel, makeLogger } from '../../logger.js';\nimport { errorCause, errorMessage } from '../../utils.js';\nimport type { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';\nimport { UIMessageCodec } from '../codec/index.js';\nimport { isToolPart, type ToolPart } from '../tool-part.js';\nimport { createRunOutputStream } from './run-output-stream.js';\n\n// ---------------------------------------------------------------------------\n// ChatTransport options\n// ---------------------------------------------------------------------------\n\n/**\n * Context passed to {@link ChatTransportOptions.prepareSendMessagesRequest} for\n * customizing the HTTP POST body and headers.\n */\nexport interface SendMessagesRequestContext {\n /** Chat session ID (from useChat's id). */\n chatId?: string;\n /** What triggered the request: user sent a message, or requested regeneration. */\n trigger: 'submit-message' | 'regenerate-message';\n /**\n * The message ID for edit or regeneration requests. For regeneration,\n * identifies the assistant message to regenerate. For edits (submit-message\n * with messageId), identifies the user message being replaced. Undefined\n * when submitting a new message.\n */\n messageId?: string;\n /** Previous messages in the conversation (context for the LLM). */\n history: AI.UIMessage[];\n /** The new message(s) being sent (to publish to the channel). Empty for regeneration and for continuations (an auto-submit where the last message is an already-tracked assistant). */\n messages: AI.UIMessage[];\n /** The codec-message-id of the message being forked — the edited user message, or the preceding assistant when forking off an unresolved tool call. Undefined for regeneration (View.regenerate derives it) and fresh sends. */\n forkOf?: string;\n /** The codec-message-id of the predecessor in the conversation thread. */\n parent?: string;\n}\n\n/** Default agent endpoint the transport POSTs invocations to — mirrors Vercel's DefaultChatTransport. */\nconst DEFAULT_VERCEL_API = '/api/chat';\n\n/** Options for customizing the ChatTransport behavior. */\nexport interface ChatTransportOptions {\n /**\n * Endpoint the transport POSTs the invocation pointer to, to wake the\n * agent. Mirrors useChat's request-driven contract. Default `/api/chat`.\n */\n api?: string;\n /** Fetch credentials mode for the invocation POST (e.g. `'include'`). */\n credentials?: RequestCredentials;\n /** Custom fetch implementation for the invocation POST. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /**\n * Customize the invocation POST before sending. Called by sendMessages()\n * with the conversation context; the returned `body` is merged into the\n * POST body (the run's invocation identifiers always take precedence) and\n * `headers` are added to the request. Use it for auth headers or extra\n * agent metadata.\n * @param context - The conversation context for the current request.\n * @returns The body and headers to merge into the invocation POST.\n */\n prepareSendMessagesRequest?: (context: SendMessagesRequestContext) => {\n body?: Record<string, unknown>;\n headers?: Record<string, string>;\n };\n}\n\n// ---------------------------------------------------------------------------\n// ChatTransport interface\n// ---------------------------------------------------------------------------\n\n/**\n * Additional options passed through from useChat alongside the core\n * sendMessages/reconnectToStream parameters.\n *\n * Mirrors the AI SDK's internal ChatRequestOptions type, which is not\n * exported from the `ai` package.\n */\ninterface ChatRequestOptions {\n /** Additional headers for the request. */\n headers?: Record<string, string> | Headers;\n /** Additional JSON body properties for the request. */\n body?: object;\n /** Custom metadata to attach to the request. */\n metadata?: unknown;\n}\n\n/**\n * Transport interface for Vercel AI SDK's useChat hook.\n *\n * Structurally compatible with the AI SDK's internal `ChatTransport<UIMessage>`\n * interface. Extended with `close()` for releasing the underlying Ably transport\n * resources and `streaming` / `onStreamingChange` for coordinating with\n * useMessageSync.\n */\nexport interface ChatTransport {\n /** Send messages and return a streaming response of UIMessageChunk events. */\n sendMessages: (\n options: {\n /** The type of message submission — new message or regeneration. */\n trigger: 'submit-message' | 'regenerate-message';\n /** Unique identifier for the chat session. */\n chatId: string;\n /** ID of the message to regenerate, or undefined for new messages. */\n messageId: string | undefined;\n /** Array of UI messages representing the conversation history. */\n messages: AI.UIMessage[];\n /** Signal to abort the request if needed. */\n abortSignal: AbortSignal | undefined;\n } & ChatRequestOptions,\n ) => Promise<ReadableStream<AI.UIMessageChunk>>;\n\n /**\n * Reconnect to an existing streaming response. Returns null if no active\n * stream exists for the specified chat session.\n */\n reconnectToStream: (\n options: {\n /** Unique identifier for the chat session to reconnect to. */\n chatId: string;\n } & ChatRequestOptions,\n ) => Promise<ReadableStream<AI.UIMessageChunk> | null>;\n\n /** Close the underlying transport, releasing all resources. */\n close(): Promise<void>;\n\n /** Whether an own-run stream is currently being consumed by useChat. */\n readonly streaming: boolean;\n\n /**\n * Subscribe to streaming state changes. The callback fires when the\n * ChatTransport transitions between streaming and idle. Used by\n * useMessageSync to gate setMessages calls during active streams.\n * @param callback - Called with `true` when a stream starts, `false` when it ends.\n * @returns Unsubscribe function.\n */\n onStreamingChange(callback: (streaming: boolean) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Stream wrapper — passthrough that signals completion via a promise\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a ReadableStream in a passthrough TransformStream that resolves a\n * promise when the stream completes or errors. The returned stream passes\n * all chunks through unchanged, and `fail(reason)` errors the readable side\n * useChat consumes without cancelling or otherwise disturbing the source run\n * stream (used when the agent-invocation POST fails).\n * @param source - The original stream to wrap.\n * @returns The wrapped stream, a `done` promise that resolves when the stream\n * closes, and a `fail` callback that errors the wrapped stream.\n */\n\nconst wrapStreamWithDone = <T>(\n source: ReadableStream<T>,\n): { stream: ReadableStream<T>; done: Promise<void>; fail: (reason: Ably.ErrorInfo) => void } => {\n let resolveDone: () => void;\n const done = new Promise<void>((resolve) => {\n resolveDone = resolve;\n });\n\n const passthrough = new TransformStream<T, T>({\n flush: () => {\n resolveDone();\n },\n });\n\n // Aborting this signal errors the destination (the readable useChat reads)\n // with the abort reason. `preventCancel` keeps the source run stream intact\n // so the tree/observers are unaffected — only the useChat-facing view fails.\n const failController = new AbortController();\n\n // Pipe in the background. If the source errors/cancels, or `fail()` aborts,\n // resolve done so the serialization queue advances.\n // Fire-and-forget: the pipe runs independently; errors surface through\n // the readable side that useChat consumes.\n source.pipeTo(passthrough.writable, { signal: failController.signal, preventCancel: true }).catch(() => {\n resolveDone();\n });\n\n return {\n stream: passthrough.readable,\n done,\n fail: (reason: Ably.ErrorInfo) => {\n failController.abort(reason);\n },\n };\n};\n\n// ---------------------------------------------------------------------------\n// Unresolved tool call detection\n// ---------------------------------------------------------------------------\n\n/**\n * Whether an assistant message has a `dynamic-tool` part that can't resolve\n * without further user action. Matches:\n * - `input-streaming` / `input-available` — tool call emitted, not yet run.\n * - `approval-requested` — waiting for the user.\n *\n * Excludes `approval-responded` (streamText will run the tool this run)\n * and all terminal `output-*` states.\n * @param msg - The UIMessage to inspect.\n * @returns True when a fork-on-send is warranted to avoid shipping a\n * dangling tool call to the LLM.\n */\nconst hasUnresolvedToolCall = (msg: AI.UIMessage): boolean =>\n msg.role === 'assistant' &&\n msg.parts.some(\n (p) =>\n isToolPart(p) &&\n (p.state === 'input-streaming' || p.state === 'input-available' || p.state === 'approval-requested'),\n );\n\n/**\n * `dynamic-tool` part states that mean \"the LLM produced a tool call and\n * is waiting on it\". Used to detect new client-side resolutions in the\n * useChat overlay relative to the tree.\n */\nconst UNRESOLVED_TOOL_STATES = new Set(['input-streaming', 'input-available', 'approval-requested']);\n\n/**\n * Walk the useChat message overlay against the session tree and synthesize\n * the {@link VercelInput}s needed to resolve every `dynamic-tool` part the\n * user acted on (executed a tool, approved, denied) but the tree's reduced\n * state hasn't reflected yet.\n *\n * Each input carries the prior assistant's tree codec-message-id (the one\n * holding the original `dynamic-tool` part the resolution targets) in its\n * `codecMessageId` field, so the encoder stamps `codec-message-id`\n * and the reducer's direct-fold path lands the resolution on that assistant\n * in one step — no cross-message redirect-by-toolCallId fallback. Every\n * variant rides the `ai-input` wire, matching its publisher (client → input).\n *\n * The resulting inputs are passed alongside the continuation `view.send`\n * so the channel publish and the continuation POST land as ONE atomic\n * operation — the agent's `loadConversation()` history walk is guaranteed\n * to see them because the channel publish happens before the POST inside\n * `_internalSend`.\n *\n * Three resolutions are produced:\n *\n * - `approval-responded` overlay vs `approval-requested` tree →\n * `tool-approval-response` carrying the user's decision\n * (`approved` = `overlayPart.approval.approved`, i.e. approve or deny)\n * - `output-available` overlay vs unresolved tree → `tool-result`\n * - `output-error` overlay vs unresolved tree → `tool-result-error`\n * @param codecMessages - The visible tree messages paired with their codec-message-ids.\n * @param messages - useChat's local overlay messages.\n * @returns The continuation inputs to publish, in tree order. Each input\n * carries its own `codecMessageId` targeting the prior assistant it folds\n * onto.\n */\nconst deriveContinuationInputs = (\n codecMessages: CodecMessage<AI.UIMessage>[],\n messages: AI.UIMessage[],\n): VercelInput[] => {\n const inputs: VercelInput[] = [];\n for (const overlay of messages) {\n if (overlay.role !== 'assistant') continue;\n // Match the overlay to its tree message by domain id (both sides\n // reconstruct the same stream id), but address the emitted inputs by\n // the tree message's codec-message-id — the agent folds tool\n // resolutions onto the assistant by codec-message-id, never by the\n // domain `message.id`.\n const treeEntry = codecMessages.find((p) => p.message.id === overlay.id);\n if (!treeEntry) continue;\n const { codecMessageId, message: treeMessage } = treeEntry;\n\n for (const overlayPart of overlay.parts) {\n if (!isToolPart(overlayPart)) continue;\n // The codec normalises every tool part to `dynamic-tool`, but the\n // AI SDK's useChat overlay emits `tool-${name}` parts for statically\n // declared tools. Match by toolCallId rather than the type prefix\n // so the cross-representation comparison works regardless of which\n // side the tool was declared on.\n const treePart = treeMessage.parts.find(\n (p: AI.UIMessage['parts'][number]): p is ToolPart => isToolPart(p) && p.toolCallId === overlayPart.toolCallId,\n );\n\n // Approval response: useChat's `addToolApprovalResponse` flipped the\n // overlay part to `approval-responded` while the tree still sits on\n // `approval-requested`. Publish a `tool-approval-response` TInput so the\n // agent's projection sees the decision.\n if (overlayPart.state === 'approval-responded' && (!treePart || treePart.state === 'approval-requested')) {\n inputs.push(\n UIMessageCodec.createToolApprovalResponse(codecMessageId, {\n toolCallId: overlayPart.toolCallId,\n approved: overlayPart.approval.approved,\n ...(overlayPart.approval.reason === undefined ? {} : { reason: overlayPart.approval.reason }),\n }),\n );\n continue;\n }\n\n // Client-tool resolution: overlay has `output-available` / `output-error`\n // while the tree's part is still unresolved. Construct a TInput\n // variant (not a UIMessageChunk) so the encoder publishes on the\n // `ai-input` wire — client tool results belong on `ai-input`, matching\n // their client publisher, not on `ai-output`.\n if (overlayPart.state !== 'output-available' && overlayPart.state !== 'output-error') continue;\n // Tree already resolved (echo arrived back) — nothing to do.\n if (treePart && !UNRESOLVED_TOOL_STATES.has(treePart.state)) continue;\n\n if (overlayPart.state === 'output-available') {\n inputs.push(\n UIMessageCodec.createToolResult(codecMessageId, {\n toolCallId: overlayPart.toolCallId,\n output: overlayPart.output,\n }),\n );\n } else {\n inputs.push(\n UIMessageCodec.createToolResultError(codecMessageId, {\n toolCallId: overlayPart.toolCallId,\n message: overlayPart.errorText,\n }),\n );\n }\n }\n }\n return inputs;\n};\n\n/**\n * Find the codec-message-id immediately preceding the message identified by\n * domain id `domainId` in the flat visible conversation. The target is\n * located by its domain `message.id` (the id useChat references), but the\n * returned value is the predecessor's codec-message-id — never a domain id.\n * Returns undefined if the target is the first message or not found.\n * @param codecMessages - Visible messages paired with their codec-message-ids.\n * @param domainId - The domain id of the target message.\n * @returns The predecessor's codec-message-id, or undefined.\n */\nconst findPredecessorCodecId = (codecMessages: CodecMessage<AI.UIMessage>[], domainId: string): string | undefined => {\n const idx = codecMessages.findIndex((p) => p.message.id === domainId);\n if (idx <= 0) return undefined;\n return codecMessages[idx - 1]?.codecMessageId;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/** Internal EventEmitter events map backing the transport's streaming state. */\ninterface ChatTransportEventsMap {\n /** Fired on every streaming-state transition with the new value. */\n streaming: boolean;\n}\n\n/**\n * Create a Vercel ChatTransport from a core ClientSession.\n *\n * Exposes a `streaming` flag and `onStreamingChange` callback so that\n * `useMessageSync` can gate `setMessages` calls during active own-run\n * streams, preventing the push/replace ID mismatch in useChat's `write()`.\n *\n * Note: concurrent `sendMessage` calls from the same user are a useChat\n * limitation that cannot be fixed from the transport layer. The\n * developer must respect useChat's `status` and only call `sendMessage`\n * when status is `'ready'`.\n * @param session - The core client session to wrap.\n * @param chatOptions - Optional hooks for customizing request construction.\n * @returns A {@link ChatTransport} compatible with Vercel's useChat hook.\n */\nexport const createChatTransport = (\n session: ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>,\n chatOptions?: ChatTransportOptions,\n): ChatTransport => {\n // -- Invocation POST config (the transport owns waking the agent) ----------\n const api = chatOptions?.api ?? DEFAULT_VERCEL_API;\n const fetchFn = chatOptions?.fetch ?? globalThis.fetch.bind(globalThis);\n const credentials = chatOptions?.credentials;\n\n // -- Streaming state -------------------------------------------------------\n // Backed by the shared EventEmitter for listener error isolation (one bad\n // onStreamingChange handler can't prevent others from firing or block the\n // state transition) and uniform emitter behaviour across the SDK. The\n // factory takes no logger, so a silent one is used — listener exceptions are\n // swallowed by the emitter rather than surfaced.\n let _streaming = false;\n const emitter = new EventEmitter<ChatTransportEventsMap>(makeLogger({ logLevel: LogLevel.Silent }));\n\n const setStreaming = (value: boolean): void => {\n _streaming = value;\n emitter.emit('streaming', value);\n };\n\n // -- sendMessages implementation -------------------------------------------\n\n const sendMessages: ChatTransport['sendMessages'] = async (opts) => {\n const { messages, abortSignal, trigger, messageId } = opts;\n\n // The visible messages paired with their codec-message-ids. useChat\n // references messages by their domain `message.id`; we match on that to\n // locate a message in the tree, then route every transport operation by\n // the message's codec-message-id (the SDK never correlates on the domain\n // id, which may differ from the codec-message-id).\n const codecMessages = session.view.getMessages();\n const codecIdByDomainId = new Map(codecMessages.map((m) => [m.message.id, m.codecMessageId]));\n const codecIdOf = (domainId: string): string | undefined => codecIdByDomainId.get(domainId);\n\n // useChat calls sendMessages in three distinct modes. We disambiguate\n // by (trigger, last-message role) so each mode dispatches correctly:\n //\n // - 'regenerate-message' → fork an assistant\n // - 'submit-message' + last message is assistant → continuation\n // (auto-submit after\n // addToolResult, or\n // multi-step tool use)\n // - 'submit-message' + last message is user → new user message\n // (or edit if\n // messageId is set)\n //\n // Continuation mode must NOT publish the assistant as a new message or\n // treat messageId as a fork target — useChat v6's sendAutomaticallyWhen\n // path always sets messageId to the last message id regardless.\n const lastMessage = messages.at(-1);\n const lastMessageInTree = !!lastMessage && codecIdByDomainId.has(lastMessage.id);\n const isContinuation = trigger === 'submit-message' && lastMessage?.role === 'assistant' && lastMessageInTree;\n\n // Fork-on-unresolved-tool: user sent a new message while the preceding\n // assistant has an unresolved tool call (approval-requested, input-*).\n // Fork the new message off the preceding assistant so the unresolved\n // tool call stays dormant on a sibling branch. Inference for this run runs\n // on the clean fork — the LLM never sees the dangling tool_use.\n //\n // Only applies to fresh user-message submits (not continuations, not\n // regenerates, not edits-with-messageId).\n //\n // `messages.at(-1)` is the fresh user-prompt being submitted right now;\n // `messages.at(-2)` is therefore the prior assistant whose tool state\n // we need to inspect for the unresolved-tool gate below.\n const precedingMessage =\n trigger === 'submit-message' && !messageId && lastMessage?.role === 'user' ? messages.at(-2) : undefined;\n // The domain id of the preceding assistant when it carries an unresolved\n // tool call and is present in the tree — the new user message forks off it.\n const forkSourceDomainId =\n precedingMessage && hasUnresolvedToolCall(precedingMessage) && codecIdByDomainId.has(precedingMessage.id)\n ? precedingMessage.id\n : undefined;\n\n // Determine the history/messages split based on mode.\n let newMessages: AI.UIMessage[];\n let history: AI.UIMessage[];\n\n if (trigger === 'regenerate-message' || isContinuation) {\n newMessages = [];\n history = messages;\n } else {\n if (messages.length === 0) {\n throw new Ably.ErrorInfo(\n 'unable to send messages; messages array is empty for submit-message trigger',\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n // CAST: length check above guarantees at least one element; .at(-1) cannot be undefined.\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style -- prefer `as` over `!` per TYPES.md\n newMessages = [messages.at(-1) as AI.UIMessage];\n // When forking off an unresolved tool call, drop the unresolved\n // assistant from history too — it belongs on the sibling branch, not\n // the ancestor chain of the new message.\n history = forkSourceDomainId ? messages.slice(0, -2) : messages.slice(0, -1);\n }\n\n // Compute fork metadata for edit (submit-message with messageId) and\n // fork-on-unresolved-tool. Regenerate is NOT precomputed here —\n // `View.regenerate` derives forkOf/parent from the tree itself and\n // overrides anything we'd set.\n let forkOf: string | undefined;\n let parent: string | undefined;\n\n if (trigger === 'submit-message' && messageId && !isContinuation) {\n // Edit: messageId is the domain id of the user message being replaced.\n // forkOf = its codec-message-id, parent = the immediately-preceding\n // codec-message-id in the flat conversation.\n forkOf = codecIdOf(messageId);\n parent = findPredecessorCodecId(codecMessages, messageId);\n } else if (forkSourceDomainId) {\n // Fork off the preceding assistant — the new user message becomes a\n // sibling of the unresolved tool call assistant, rooted at its parent.\n forkOf = codecIdOf(forkSourceDomainId);\n parent = findPredecessorCodecId(codecMessages, forkSourceDomainId);\n }\n\n let sendBody: Record<string, unknown>;\n let sendHeaders: Record<string, string> | undefined;\n\n if (chatOptions?.prepareSendMessagesRequest) {\n const prepared = chatOptions.prepareSendMessagesRequest({\n chatId: opts.chatId,\n trigger,\n messageId,\n history,\n messages: newMessages,\n forkOf,\n parent,\n });\n sendBody = prepared.body ?? {};\n sendHeaders = prepared.headers;\n } else {\n sendBody = {};\n sendHeaders = undefined;\n }\n\n const sendOpts: SendOptions = {};\n if (forkOf !== undefined) sendOpts.forkOf = forkOf;\n if (parent !== undefined) sendOpts.parent = parent;\n // Continuations reuse the suspended assistant's runId so the agent's\n // existing run resumes under a fresh invocation rather than spinning\n // up a brand-new run. `isContinuation` implies `lastMessage` is defined.\n if (isContinuation) {\n // `isContinuation` implies `lastMessage` is defined (it gates on\n // `lastMessage?.role`). Route the runId lookup by codec-message-id.\n const codecId = codecIdOf(lastMessage.id);\n const run = codecId === undefined ? undefined : session.view.runOf(codecId);\n if (run) sendOpts.runId = run.runId;\n }\n\n // Dispatch by mode:\n //\n // - Continuation: derive tool-resolution events from useChat's overlay\n // vs the tree and pair each with the prior assistant's tree codec-message-id —\n // the SDK stamps the wire's `codec-message-id` to that id so the\n // reducer's direct fold path runs (no redirect, no consume).\n // - Regenerate: route through `view.regenerate`. The View mints a\n // wire-only regenerate event (`ait-regenerate`) carrying\n // `forkOf=A1` / `parent=U1` on transport headers. U1 is NOT\n // republished — A1 and A2 group as tree siblings under U1 via the\n // existing forkOf machinery. The LLM receives the truncated history\n // through U1 inclusive via the body.\n // - Fresh send / edit: publish the new user-message input(s) via\n // `view.send`.\n let run: ActiveRun;\n if (isContinuation) {\n const inputs = deriveContinuationInputs(codecMessages, messages);\n run = await session.view.send(inputs, sendOpts);\n } else if (trigger === 'regenerate-message') {\n if (messageId === undefined) {\n throw new Ably.ErrorInfo(\n 'unable to regenerate; regenerate-message trigger fired without messageId',\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n // useChat passes the assistant's domain id; route by its codec-message-id.\n const regenCodecId = codecIdOf(messageId);\n if (regenCodecId === undefined) {\n throw new Ably.ErrorInfo(\n `unable to regenerate; message not visible: ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n run = await session.view.regenerate(regenCodecId, sendOpts);\n } else {\n const inputs = newMessages.map((m) => UIMessageCodec.createUserMessage(m));\n run = await session.view.send(inputs, sendOpts);\n }\n\n // Build the consumer-facing stream from the Tree's events for this run.\n // Streaming is a useChat concern owned by the Vercel layer; the core\n // session exposes no per-run stream. Key it on\n // `run.inputCodecMessageId` — the triggering input's codec-message-id, which\n // the client owns from send time and the agent echoes as\n // `input-codec-message-id`. The agent mints the runId, supplied as\n // `run.runId` (a promise) for the run-end safety-net.\n const runStream = createRunOutputStream(session, run.runId, run.inputCodecMessageId);\n\n if (abortSignal) {\n const onAbort = (): void => {\n // Best-effort cancel via the run handle (knows its own key / runId);\n // the core resolves the runId once the agent mints it.\n void run.cancel();\n // Close the consumer stream immediately so useChat's reader ends\n // without waiting for the agent's run-end round-trip.\n runStream.close();\n };\n // useChat sets `status: 'submitted'` synchronously inside `makeRequest`\n // BEFORE awaiting `transport.sendMessages`. That immediately enables\n // the Stop button in the UI. If the user clicks Stop while\n // `session.view.send` is still awaiting the run-start ack (which\n // can take seconds for a real LLM), useChat aborts the signal before\n // we ever get here. `addEventListener('abort', ...)` does not fire\n // for an already-aborted signal, so we'd silently lose the cancel\n // and the agent would keep streaming.\n if (abortSignal.aborted) {\n onAbort();\n } else {\n abortSignal.addEventListener('abort', onAbort, { once: true });\n }\n }\n\n // Wrap the stream to detect completion. The streaming flag gates\n // useMessageSync so that setMessages doesn't interfere with\n // useChat's internal write() during active streams.\n const { stream, done, fail } = wrapStreamWithDone(runStream.stream);\n setStreaming(true);\n\n // Fire-and-forget: clear the streaming flag when the stream ends.\n void done.then(() => {\n setStreaming(false);\n });\n\n // Wake the agent: POST the invocation pointer to the configured endpoint.\n // useChat's transport contract is request-driven, so the transport owns\n // this POST (the core session is HTTP-free). Fire-and-forget — `await`\n // would delay the stream return, and the agent's response arrives over\n // the Ably channel, not the HTTP response. The run's invocation\n // identifiers always win over any custom body so the agent can parse it\n // via Invocation.fromJSON. A failed POST means the agent never woke, so\n // error the useChat-facing stream; the core run and observers are\n // untouched.\n const postBody = { ...sendBody, ...run.toInvocation().toJSON() };\n fetchFn(api, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...sendHeaders },\n body: JSON.stringify(postBody),\n ...(credentials ? { credentials } : {}),\n })\n .then((response) => {\n if (!response.ok) {\n fail(\n new Ably.ErrorInfo(\n `unable to send; HTTP POST to ${api} returned ${String(response.status)} ${response.statusText}`,\n ErrorCode.SessionSendFailed,\n response.status,\n ),\n );\n }\n })\n .catch((error: unknown) => {\n fail(\n new Ably.ErrorInfo(\n `unable to send; HTTP POST to ${api} failed: ${errorMessage(error)}`,\n ErrorCode.SessionSendFailed,\n 500,\n errorCause(error),\n ),\n );\n });\n\n return stream;\n };\n\n return {\n sendMessages,\n\n // Observer mode handles in-progress streams automatically.\n // The transport subscribes before attach — on the next server append,\n // observer accumulation emits lifecycle events that useMessageSync\n // upserts into React state.\n // eslint-disable-next-line unicorn/no-null, @typescript-eslint/promise-function-async -- null is required by the AI SDK ChatTransport contract; no await needed\n reconnectToStream: () => Promise.resolve(null),\n\n close: async () => session.close(),\n\n get streaming(): boolean {\n return _streaming;\n },\n\n onStreamingChange: (callback: (streaming: boolean) => void): (() => void) => {\n emitter.on('streaming', callback);\n return () => {\n emitter.off('streaming', callback);\n };\n },\n };\n};\n","/** SDK version. Kept in sync with `package.json` by the `/release` workflow. */\nexport const VERSION = '0.3.0';\n","/**\n * Wraps the two paths chat-js uses (see ChatClient._addAgent): the\n * `options.agents` mutation (read by ably-js when opening the initial\n * WebSocket) and the `params.agent` channel option (sent on ATTACH so\n * an already-open connection still carries the identifier).\n *\n * `options.agents` is a private API on the Realtime client — no public\n * typed accessor exists in the `ably` package — so this module casts to a\n * `RealtimeWithOptions` shape to write it.\n */\n\nimport type * as Ably from 'ably';\n\nimport { VERSION } from '../version.js';\n\ninterface RealtimeWithOptions extends Ably.Realtime {\n options: { agents?: Record<string, string | undefined> };\n}\n\nconst SDK_NAME = 'ai-transport-js';\n\n/**\n * Merge `agents` into `client.options.agents` and return the space-separated\n * `params.agent` string for channel ATTACH.\n * @param client - The Ably Realtime client to mutate.\n * @param agents - Map of agent-name to version strings to register.\n * @returns Channel options containing `params.agent` for `channels.get`.\n */\nconst injectAgents = (\n client: Ably.Realtime,\n // CAST: Ably.Realtime's public type omits `options.agents`, but the SDK\n // does carry it at runtime. ably-chat-js relies on the same shape — see\n // ChatClient._addAgent in https://github.com/ably/ably-chat-js.\n agents: Record<string, string>,\n): { params: { agent: string } } => {\n const realtime = client as RealtimeWithOptions;\n realtime.options.agents = { ...realtime.options.agents, ...agents };\n return { params: { agent: joinAgents(agents) } };\n};\n\n/**\n * Build the agent-name → version map this SDK registers for the given codec.\n * @param codec - The codec instance whose optional identifier opts into registration.\n * @param codec.adapterTag - The optional Ably-Agent identifier; registered as an agent when present.\n * @returns Map of agent name to version, always including this SDK.\n */\nconst buildAgents = (codec?: { readonly adapterTag?: string }): Record<string, string> => {\n const adapterTag = codec?.adapterTag;\n const agents: Record<string, string> = { [SDK_NAME]: VERSION };\n if (adapterTag) agents[adapterTag] = VERSION;\n return agents;\n};\n\n/**\n * Render an agents map as the space-separated `name/version` string Ably expects.\n * @param agents - Map of agent name to version.\n * @returns The space-separated `name/version` agent string.\n */\nconst joinAgents = (agents: Record<string, string>): string =>\n Object.entries(agents)\n .map(([name, version]) => `${name}/${version}`)\n .join(' ');\n\n/**\n * The space-separated `params.agent` string this SDK stamps on channel ATTACH —\n * `ai-transport-js/<version>` plus the codec's adapter tag when present. Pure:\n * unlike {@link registerAgent} it does not mutate the client. Use it to seed a\n * `<ChannelProvider options>` so ably-js's React hooks append their own agent\n * additively (`channelOptionsForReactHooks`) rather than overwriting this SDK's.\n * @param codec - The codec instance whose optional identifier opts into registration.\n * @param codec.adapterTag - The optional Ably-Agent identifier; registered as an agent when present.\n * @returns The channel `params.agent` string.\n */\nexport const channelAgent = (codec?: { readonly adapterTag?: string }): string => joinAgents(buildAgents(codec));\n\n/**\n * Register this SDK (and optionally a codec) on the supplied Realtime client\n * and return the channel options the caller should pass to\n * `client.channels.get(...)` so the agent is also carried on channel ATTACH.\n * Sets `options.agents['ai-transport-js'] = VERSION`. When the codec carries an\n * `adapterTag`, also sets `options.agents[adapterTag] = VERSION`.\n * Idempotent — repeated calls with the same client and codec produce the same keys/values.\n * Spec: AIT-CT1a, AIT-CT1a2, AIT-CT1a3, AIT-ST1a, AIT-ST1a2, AIT-ST1a3.\n * @param client - The Ably Realtime client to register on.\n * @param codec - The codec instance whose optional identifier opts into registration.\n * @param codec.adapterTag - The optional Ably-Agent identifier; registered as an agent when present.\n * @returns Channel options containing `params.agent` for `channels.get`.\n */\nexport const registerAgent = (\n client: Ably.Realtime,\n codec?: { readonly adapterTag?: string },\n): { params: { agent: string } } => injectAgents(client, buildAgents(codec));\n","/**\n * Channel-mode resolution shared by the sessions and the React provider.\n *\n * Presence, pub/sub, and annotation publishing are all part of the server's\n * default channel-mode set, so a channel attached with no mode flags is granted\n * them automatically. LiveObjects is different: object operations require the\n * `object_subscribe` / `object_publish` modes, which are NOT in the default\n * set, so the channel must be attached with explicit modes to use them.\n *\n * Setting `modes` on the wire is a full replacement, not additive: the moment\n * an ATTACH carries any mode flag the server takes that bitfield verbatim and\n * never falls back to its default set. So requesting object modes means also\n * requesting everything the default set would grant, plus the object modes.\n * {@link AIT_BASE_MODES} is exactly the server default, so opting into extra\n * modes adds the extras and changes nothing else.\n *\n * Every place that resolves the session's channel options — both session\n * constructors and the React `<ChannelProvider>` — must funnel through\n * {@link resolveChannelModes} so they all request the SAME modes in the SAME\n * order. ably-js compares modes order- and duplicate-sensitively when deciding\n * whether a `setOptions` call needs a reattach; identical arrays compare equal,\n * so consistent resolution avoids both spurious reattaches and silent mode\n * reversion when one writer omits modes another set.\n */\n\nimport type * as Ably from 'ably';\n\n/**\n * The modes AI Transport always needs — byte-for-byte the server's default\n * channel-mode set (`PUBLISH | SUBSCRIBE | PRESENCE | PRESENCE_SUBSCRIBE |\n * ANNOTATION_PUBLISH`). Because this equals the default, requesting it plus\n * extra modes (e.g. {@link OBJECT_MODES}) grants the same access as the default\n * set plus those extras.\n */\nexport const AIT_BASE_MODES: readonly Ably.ChannelMode[] = [\n 'PUBLISH',\n 'SUBSCRIBE',\n 'PRESENCE',\n 'PRESENCE_SUBSCRIBE',\n 'ANNOTATION_PUBLISH',\n];\n\n/**\n * The channel modes required to read and write Ably LiveObjects. Pass as the\n * session's `channelModes` option (`channelModes: OBJECT_MODES`) to enable the\n * `object` accessor on a session and the LiveObjects channel hooks under a\n * `<ClientSessionProvider>`.\n */\nexport const OBJECT_MODES: readonly Ably.ChannelMode[] = ['OBJECT_SUBSCRIBE', 'OBJECT_PUBLISH'];\n\n/**\n * Canonical ordering for every known channel mode. {@link resolveChannelModes}\n * emits modes in this order so two resolutions of the same mode set produce\n * identical arrays, which ably-js treats as equal (no reattach churn).\n */\nconst MODE_ORDER: readonly Ably.ChannelMode[] = [\n 'PUBLISH',\n 'SUBSCRIBE',\n 'PRESENCE',\n 'PRESENCE_SUBSCRIBE',\n 'OBJECT_PUBLISH',\n 'OBJECT_SUBSCRIBE',\n 'ANNOTATION_PUBLISH',\n 'ANNOTATION_SUBSCRIBE',\n];\n\n/**\n * Resolve the channel modes AI Transport should request on its channel.\n *\n * Returns `undefined` when the caller asks for no extra modes, so the channel\n * attaches with no mode flags and the server applies its default set. When\n * extra modes are supplied (e.g. {@link OBJECT_MODES}),\n * returns {@link AIT_BASE_MODES} unioned with them, de-duplicated and in a\n * fixed canonical order so repeated resolutions compare equal.\n *\n * Modes outside {@link MODE_ORDER} (which should not occur for a valid\n * {@link Ably.ChannelMode}, but is possible with the type's lowercase aliases)\n * are appended after the canonical modes, sorted alphabetically, so the result\n * is still deterministic.\n * @param extraModes - Additional modes to request on top of {@link AIT_BASE_MODES}. Omit or pass an empty array to request no modes at all.\n * @returns The canonically-ordered, de-duplicated mode set, or `undefined` when no extra modes were requested.\n */\nexport const resolveChannelModes = (extraModes?: readonly Ably.ChannelMode[]): Ably.ChannelMode[] | undefined => {\n if (extraModes === undefined || extraModes.length === 0) return undefined;\n const requested = new Set<Ably.ChannelMode>([...AIT_BASE_MODES, ...extraModes]);\n const ordered = MODE_ORDER.filter((mode) => requested.has(mode));\n const unknown = [...requested].filter((mode) => !MODE_ORDER.includes(mode)).toSorted();\n return [...ordered, ...unknown];\n};\n","/**\n * loadHistoryPages — shared low-level history pagination primitive.\n *\n * Consumed by both client (via `load-history.ts`, which layers a complete-\n * domain-message counter on top) and agent (directly, for input-event lookup\n * and conversation hydration). Returns raw Ably messages; does NOT decode.\n *\n * Behaviour:\n * - Attaches the channel (idempotent) then pages via `channel.history()`,\n * using `untilAttach: true` for gapless continuity with any live subscription.\n * - Exposes the underlying pagination as a cursor with `hasNext()` (cheap,\n * no network) and `next()` (one Ably page per call, newest-first within\n * the page).\n * - Per-page failures are retried with bounded exponential backoff; on\n * exhaustion throws `Ably.ErrorInfo` with code `HistoryFetchFailed`.\n * - `signal.aborted` is checked between pages; rejects with\n * `Ably.ErrorInfo` (InvalidArgument) when aborted.\n *\n * Spec: AIT-CT11 / AIT-ST hydration.\n */\n\nimport * as Ably from 'ably';\n\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport { errorCause, errorMessage } from '../../utils.js';\n\n/** Options for {@link loadHistoryPages}. */\nexport interface LoadHistoryPagesOptions {\n /** Wire-message limit per Ably page. */\n pageLimit: number;\n /** Set `untilAttach: true` on the underlying history query for gapless continuity with live subscriptions. Default: true. */\n untilAttach?: boolean;\n /** AbortSignal checked between pages. Rejects with InvalidArgument when aborted. */\n signal?: AbortSignal;\n /** Max retries per `page.next()` / initial `history()` failure. Default: 3. */\n maxRetries?: number;\n /** Initial retry backoff in ms (doubled per attempt). Default: 100. */\n retryBackoffMs?: number;\n /** Logger for diagnostic output. */\n logger?: Logger;\n}\n\n/**\n * Cursor over the channel's history pages.\n *\n * `hasNext()` is cheap (cursor-only, no network); `next()` issues one Ably\n * page fetch (with retry/backoff) and returns its messages. Once `next()`\n * returns `undefined` the cursor is exhausted.\n */\nexport interface HistoryPagesCursor {\n /** True when another Ably page is available (cheap to check; no network). */\n hasNext(): boolean;\n /**\n * Fetch the next Ably page's messages (newest-first within the page).\n * Returns `undefined` when no more pages are available or the abort\n * signal has fired.\n */\n next(): Promise<readonly Ably.InboundMessage[] | undefined>;\n}\n\n/**\n * Sleep for `ms` milliseconds, honouring an AbortSignal.\n * @param ms - Milliseconds to wait.\n * @param signal - Optional abort signal; rejects when fired.\n */\n// eslint-disable-next-line @typescript-eslint/promise-function-async -- the function body is the Promise constructor; async would wrap it in an extra Promise\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Ably.ErrorInfo('unable to wait; signal aborted', ErrorCode.InvalidArgument, 400));\n return;\n }\n const timer: ReturnType<typeof setTimeout> | number = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n // Node returns an unref-able Timeout; browsers return a number. Unref so\n // a retry backoff cannot keep a Node process alive by itself.\n if (typeof timer === 'object') timer.unref();\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(new Ably.ErrorInfo('unable to wait; signal aborted', ErrorCode.InvalidArgument, 400));\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Invoke `fetchPage`, retrying on failure with exponential backoff. Throws\n * the last failure wrapped as `HistoryFetchFailed` once retries are\n * exhausted.\n * @param fetchPage - The page fetch to retry (initial `channel.history()` call or a `page.next()`).\n * @param maxRetries - Maximum number of attempts after the initial call.\n * @param initialBackoffMs - Starting backoff delay (doubled per attempt).\n * @param signal - Optional abort signal; cancels remaining retries.\n * @param logger - Optional logger.\n * @returns The fetched page, or `undefined` when pagination is exhausted.\n */\nconst fetchPageWithRetry = async (\n fetchPage: () => Promise<Ably.PaginatedResult<Ably.InboundMessage> | undefined>,\n maxRetries: number,\n initialBackoffMs: number,\n signal: AbortSignal | undefined,\n logger: Logger | undefined,\n): Promise<Ably.PaginatedResult<Ably.InboundMessage> | undefined> => {\n let lastError: unknown;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (signal?.aborted) {\n throw new Ably.ErrorInfo(\n 'unable to fetch history page; signal aborted',\n ErrorCode.InvalidArgument,\n 400,\n errorCause(lastError),\n );\n }\n try {\n return await fetchPage();\n } catch (error) {\n lastError = error;\n if (attempt === maxRetries) break;\n const backoff = initialBackoffMs * 2 ** attempt;\n logger?.debug('loadHistoryPages.fetchPageWithRetry(); page fetch failed, retrying', {\n attempt: attempt + 1,\n maxRetries,\n backoff,\n });\n await sleep(backoff, signal);\n }\n }\n throw new Ably.ErrorInfo(\n `unable to fetch history page; ${errorMessage(lastError)}`,\n ErrorCode.HistoryFetchFailed,\n 500,\n errorCause(lastError),\n );\n};\n\n/**\n * Page through channel history, returning a cursor over Ably pages.\n *\n * Newest-first within each yielded page (matching Ably's native ordering).\n * Caller drives the cursor — calling `next()` until it returns `undefined`\n * or stopping early when a domain-specific stop condition is met\n * (e.g. complete-message counter satisfied, target codec-message-id found,\n * parent chain walk reaches root).\n *\n * The initial Ably history call is awaited eagerly so the returned cursor\n * already knows whether there are pages available (via `hasNext()`).\n * @param channel - The Ably channel to read history from.\n * @param options - Pagination options.\n * @returns A cursor with `hasNext()` (cheap, cursor-only) and `next()` (fetches one page with retry).\n * @throws {Ably.ErrorInfo} `HistoryFetchFailed` on exhausted retry of the initial fetch, or `InvalidArgument` on signal abort.\n */\nexport const loadHistoryPages = async (\n channel: Ably.RealtimeChannel,\n options: LoadHistoryPagesOptions,\n): Promise<HistoryPagesCursor> => {\n const { pageLimit, untilAttach = true, signal, maxRetries = 3, retryBackoffMs = 100, logger } = options;\n\n if (signal?.aborted) {\n throw new Ably.ErrorInfo('unable to load history; signal aborted', ErrorCode.InvalidArgument, 400);\n }\n\n await channel.attach();\n\n const historyParams: Ably.RealtimeHistoryParams = { limit: pageLimit, untilAttach };\n\n let currentPage: Ably.PaginatedResult<Ably.InboundMessage> | undefined = await fetchPageWithRetry(\n // eslint-disable-next-line @typescript-eslint/promise-function-async -- channel.history returns a real Promise\n () => channel.history(historyParams),\n maxRetries,\n retryBackoffMs,\n signal,\n logger,\n );\n let firstYielded = false;\n\n // Compute whether the cursor has another page available. Cheap — no\n // network. Reflects the latest fetched page's `hasNext()` plus the signal\n // check.\n const hasNext = (): boolean => {\n if (currentPage === undefined) return false;\n if (signal?.aborted) return false;\n if (!firstYielded) return true;\n return currentPage.hasNext();\n };\n\n const next = async (): Promise<readonly Ably.InboundMessage[] | undefined> => {\n if (currentPage === undefined) return undefined;\n if (signal?.aborted) {\n throw new Ably.ErrorInfo('unable to load history; signal aborted', ErrorCode.InvalidArgument, 400);\n }\n\n if (!firstYielded) {\n firstYielded = true;\n return currentPage.items;\n }\n\n if (!currentPage.hasNext()) {\n currentPage = undefined;\n return undefined;\n }\n\n const nextPage: Ably.PaginatedResult<Ably.InboundMessage> | undefined = await fetchPageWithRetry(\n async () => (await currentPage?.next()) ?? undefined,\n maxRetries,\n retryBackoffMs,\n signal,\n logger,\n );\n if (!nextPage) {\n currentPage = undefined;\n return undefined;\n }\n currentPage = nextPage;\n return nextPage.items;\n };\n\n return { hasNext, next };\n};\n","/**\n * AgentView — internal, server-side message-loading + input-event lookup for\n * AgentSession.\n *\n * Encapsulates everything the agent needs to read conversation state off the\n * channel: locating the triggering input event before `run-start`\n * ({@link AgentView.findInputEvent}), and reconstructing the ancestor chain for\n * an LLM prompt ({@link AgentView.loadConversation} / {@link AgentView.messages}).\n *\n * It does NOT own the materialisation Tree — AgentSession owns the Tree and the\n * applier (and swaps them on channel continuity loss) and injects them here as\n * `readonly` fields, the same way ClientSession wires `DefaultView`. Because\n * AgentSession swaps the Tree, it RECREATES the AgentView on continuity loss\n * (a fresh instance bound to the fresh Tree/applier) rather than mutating it —\n * so this class never needs a tree accessor or a reset hook.\n *\n * This is deliberately internal: it is not exported from any entry point and\n * does NOT implement the public `View` interface (that is the client-side\n * `DefaultView`, unrelated to this class).\n *\n * Both `findInputEvent` and `loadConversation` drive ONE history-walk mechanism\n * — the single-flight chain in {@link AgentView._driveHistoryChain} — so a\n * `start()` input scan and a concurrent `loadConversation` share folded pages\n * instead of each scanning the channel.\n */\n\nimport * as Ably from 'ably';\n\nimport { HEADER_EVENT_ID } from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport { compareBySerial, errorCause, errorMessage, getTransportHeaders } from '../../utils.js';\nimport type { Codec, CodecInputEvent, CodecOutputEvent } from '../codec/types.js';\nimport type { WireApplier } from './decode-fold.js';\nimport { type HistoryPagesCursor, loadHistoryPages } from './load-history-pages.js';\nimport type { TreeInternal } from './tree.js';\nimport type { ConversationNode, Tree } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Input-event lookup result\n// ---------------------------------------------------------------------------\n\n/**\n * Result of {@link AgentView.findInputEvent}. The lookup races the session's\n * Tree (`findAblyMessageByEventId` pre-scan + `'ably-message'` event for live\n * arrivals) against a bounded history scan; resolves with the matched messages\n * sorted by Ably `serial` ascending.\n *\n * Run.start reads `firstHeaders` / `firstClientId` from the smallest-serial\n * matched message to derive per-run metadata (run-id, parent, forkOf,\n * continuation flag, publisher clientId). The Tree has already folded each\n * message by the time the lookup resolves, so callers do NOT need to decode the\n * raw matched messages themselves.\n */\nexport interface InputEventLookupResult {\n /** Raw Ably messages matched by the lookup, sorted by serial ascending. */\n rawMessages: Ably.InboundMessage[];\n /** Transport headers of the smallest-serial matched message (run metadata). */\n firstHeaders?: Record<string, string>;\n /** Publisher's Ably channel-level `clientId` from the smallest-serial message. */\n firstClientId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Ancestor-chain walk over the Tree\n// ---------------------------------------------------------------------------\n\n/**\n * Walk parent pointers from an anchor codec-message-id back through the\n * Tree to the conversation root, returning nodes in root-first order. When\n * `maxRuns` is set, the walk stops before the RunNode that would exceed the\n * bound, so the bounding run's own input node(s) are still included (input\n * nodes never count toward the bound). The chain therefore starts with the\n * input that triggered its oldest run, never with an assistant reply.\n *\n * Returns an empty array when the anchor isn't in the Tree.\n * @param tree - The materialisation tree to walk.\n * @param anchor - The codec-message-id to start from (typically the current run's input).\n * @param maxRuns - Optional bound on the number of ancestor reply RunNodes in the chain.\n * @param currentRunId - The current run's id. Its own RunNode (reachable when\n * the anchor's wire carried the run-id) is conversation tail, not ancestor\n * context, so it never counts toward `maxRuns`.\n * @returns Nodes from root to anchor in chronological order.\n */\nexport const walkAncestorChain = <TOutput extends CodecOutputEvent, TProjection>(\n tree: Tree<TOutput, TProjection>,\n anchor: string | undefined,\n maxRuns?: number,\n currentRunId?: string,\n): readonly ConversationNode<TProjection>[] => {\n if (anchor === undefined) return [];\n const chain: ConversationNode<TProjection>[] = [];\n let current = tree.getNodeByCodecMessageId(anchor);\n const seen = new Set<string>();\n let runs = 0;\n while (current !== undefined) {\n // Defensive cycle guard — `parentCodecMessageId` chains should be DAGs;\n // a cycle indicates Tree corruption but we don't want to infinite-loop.\n const key = current.kind === 'run' ? current.runId : current.codecMessageId;\n if (seen.has(key)) break;\n if (current.kind === 'run' && current.runId !== currentRunId) {\n // Stop before a run that would exceed the bound — the input node(s)\n // above the last in-bound run belong to its turn and stay included.\n if (maxRuns !== undefined && runs >= maxRuns) break;\n runs += 1;\n }\n seen.add(key);\n chain.unshift(current);\n const parentId = current.parentCodecMessageId;\n if (parentId === undefined) break;\n current = tree.getNodeByCodecMessageId(parentId);\n }\n return chain;\n};\n\n/**\n * Count the ancestor reply RunNodes in a chain. Used to bound the walk via\n * the `maxRuns` option; the current run's own node never counts.\n * @param chain - Ancestor chain to count over.\n * @param currentRunId - The current run's id, excluded from the count.\n * @returns Number of ancestor reply RunNodes in the chain.\n */\nconst countReplyRuns = <TProjection>(\n chain: readonly ConversationNode<TProjection>[],\n currentRunId?: string,\n): number => {\n let count = 0;\n for (const node of chain) if (node.kind === 'run' && node.runId !== currentRunId) count++;\n return count;\n};\n\n/**\n * Wrap an unknown history-walk failure as `Ably.ErrorInfo`, preserving the\n * original code/statusCode when the failure already carried them and\n * attaching the original as `cause`. Falls back to `HistoryFetchFailed`.\n * @param operation - The failed operation, phrased for an `unable to <operation>; <reason>` message.\n * @param error - The thrown value.\n * @returns The wrapped error.\n */\nconst wrapHistoryError = (operation: string, error: unknown): Ably.ErrorInfo => {\n const errInfo = errorCause(error);\n return new Ably.ErrorInfo(\n `unable to ${operation}; ${errorMessage(error)}`,\n errInfo?.code ?? ErrorCode.HistoryFetchFailed,\n errInfo?.statusCode ?? 500,\n errInfo,\n );\n};\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Constructor dependencies for {@link AgentView}, injected by AgentSession.\n *\n * AgentView holds `tree` + `applier` directly (like `DefaultView`). AgentSession\n * owns them and, because it SWAPS the Tree on continuity loss, recreates the\n * AgentView with the fresh Tree/applier rather than mutating them in place.\n */\nexport interface AgentViewOptions<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> {\n /** The session's materialisation Tree (read for walks; folded into by history). */\n tree: TreeInternal<TInput, TOutput, TProjection>;\n /** The Ably channel to read history from. */\n channel: Ably.RealtimeChannel;\n /** Codec used to project per-node messages. */\n codec: Codec<TInput, TOutput, TProjection, TMessage>;\n /** The Tree's decode-and-apply engine; history pages fold through it. */\n applier: WireApplier;\n /** Logger for diagnostic output. */\n logger?: Logger;\n /**\n * Age bound for the input-event scan: the scan gives up paging once it\n * crosses `Date.now() - inputEventLookbackMs`. Applied only to\n * `findInputEvent`, never to the ancestor-hydration walk.\n */\n inputEventLookbackMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Internal server-side view: input-event lookup + conversation loading over the\n * session Tree. See the file header for the ownership boundary.\n */\nexport class AgentView<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage> {\n private readonly _tree: TreeInternal<TInput, TOutput, TProjection>;\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: Codec<TInput, TOutput, TProjection, TMessage>;\n private readonly _applier: WireApplier;\n private readonly _logger?: Logger;\n private readonly _inputEventLookbackMs: number;\n\n /**\n * Tail of the single-flight history-hydration chain. Each walk links behind\n * the current tail and becomes the new tail, so concurrent calls serialise\n * and share each other's folded pages instead of each scanning the channel.\n * A link never rejects (it records its error locally), so a follower awaiting\n * the tail is isolated from a prior link's failure.\n */\n private _hydrationMutex: Promise<void> | undefined;\n /**\n * Shared history-walk cursor for this AgentView's attach epoch — ONE backward\n * `untilAttach` pagination that both `findInputEvent` and `loadConversation`\n * advance. `findInputEvent` pages it until the trigger is found (or its\n * lookback give-up point) and pauses; `loadConversation` resumes from that\n * position instead of re-paging from newest, so the channel is walked once.\n * Created lazily on first use (no per-caller signal, so it outlives any one\n * caller; no lookback, so it can reach attach). The single-flight chain\n * (`_hydrationMutex`) serialises access so it is never paged concurrently. A\n * continuity-loss swap recreates the whole AgentView, so there is no in-place\n * reset.\n */\n private _cursor: HistoryPagesCursor | undefined;\n /**\n * True once the shared cursor reached attach (channel exhausted). Because the\n * cursor carries no lookback, its exhaustion is always genuine (never a\n * lookback boundary), so either caller may record it; a lookback-bounded\n * `findInputEvent` scan stops via an early `break` that leaves the cursor\n * non-exhausted, so it never sets this.\n */\n private _historyExhausted = false;\n\n constructor(options: AgentViewOptions<TInput, TOutput, TProjection, TMessage>) {\n this._tree = options.tree;\n this._channel = options.channel;\n this._codec = options.codec;\n this._applier = options.applier;\n this._inputEventLookbackMs = options.inputEventLookbackMs;\n this._logger = options.logger?.withContext({ component: 'AgentView' });\n }\n\n /**\n * Fold a single wire message into the Tree: decode-and-apply via the applier,\n * then notify Tree subscribers and populate the event-id index. Mirrors\n * AgentSession's live `_foldWire`; history pages fold through this.\n * @param wire - The inbound Ably message to fold.\n */\n private _foldWire(wire: Ably.InboundMessage): void {\n this._applier.apply(wire);\n this._tree.emitAblyMessage(wire);\n }\n\n // -------------------------------------------------------------------------\n // Input-event lookup\n // -------------------------------------------------------------------------\n\n /**\n * Find every message whose `event-id` matches one of `expectedEventIds`,\n * racing three sources:\n *\n * 1. A pre-scan of the Tree via `findAblyMessageByEventId` for messages already\n * folded into it from prior live arrivals.\n * 2. A live listener on the Tree's `ably-message` event for new arrivals\n * during the call.\n * 3. The shared history walk (lookback-bounded) — pages fold into the Tree\n * and surface through the same `ably-message` event.\n *\n * Resolves when every expected event-id has been matched. Per-id race\n * resolution — whichever source surfaces a matched message first wins\n * (dedup by serial). On timeout: cancels the in-flight history scan and\n * rejects with `InputEventNotFound`, wrapping any history-scan failure as\n * `cause` so a broken history fetch isn't masked behind the timeout. On\n * signal abort: rejects with `InvalidArgument`.\n *\n * `firstHeaders` and `firstClientId` are read from the matched message with\n * the smallest serial (`compareBySerial`), giving stable run-level\n * metadata regardless of arrival ordering across sources.\n * @param opts - Lookup parameters.\n * @param opts.invocationId - The invocation id this lookup is for (logging / error messages).\n * @param opts.runId - The run id this lookup is for (logging / error messages).\n * @param opts.expectedEventIds - The set of `event-id`s the lookup must observe before resolving.\n * @param opts.timeoutMs - Maximum total wait across live + history sources.\n * @param opts.signal - AbortSignal that aborts the lookup if the run is cancelled.\n * @returns Raw matched Ably messages sorted by serial ascending, plus the\n * smallest-serial message's headers and clientId for downstream metadata.\n */\n // eslint-disable-next-line @typescript-eslint/promise-function-async -- the body IS a Promise executor; async would double-wrap it\n findInputEvent(opts: {\n invocationId: string;\n runId: string;\n expectedEventIds: readonly string[];\n timeoutMs: number;\n signal: AbortSignal;\n }): Promise<InputEventLookupResult> {\n const { invocationId, runId, expectedEventIds, timeoutMs, signal } = opts;\n const logger = this._logger;\n const expectedSet = new Set(expectedEventIds);\n const expectedCount = expectedSet.size;\n\n const matchedByEventId = new Map<string, Ably.InboundMessage>();\n\n // Bounded history fetch in parallel with the live wait; this controller\n // lets the lookup cancel the in-flight fetch on timeout / abort,\n // independently of the run signal.\n const historyController = new AbortController();\n\n return new Promise<InputEventLookupResult>((resolve, reject) => {\n let settled = false;\n // A genuine history-scan failure (not a cancel-induced abort) recorded\n // so the timeout rejection can surface it as `cause` — the live path\n // may still win the race, so the failure alone doesn't reject.\n let historyError: Ably.ErrorInfo | undefined;\n /* eslint-disable prefer-const -- forward-declared so cleanup() / onCancelled() can reference before the listener register or the timeout schedule has run. */\n let unregisterLive: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | number | undefined;\n /* eslint-enable */\n\n const cleanup = (): void => {\n if (unregisterLive) unregisterLive();\n if (timer !== undefined) clearTimeout(timer);\n historyController.abort();\n signal.removeEventListener('abort', onCancelled);\n };\n\n const onCancelled = (): void => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Ably.ErrorInfo(\n `unable to look up input event; run ${runId} was cancelled`,\n ErrorCode.InvalidArgument,\n 400,\n ),\n );\n };\n\n const finishOk = (): void => {\n if (settled) return;\n settled = true;\n cleanup();\n // Sort matched messages by serial for deterministic publish-order\n // delivery to the caller — firstHeaders / firstClientId come from\n // the smallest-serial message.\n const sorted = [...matchedByEventId.values()].toSorted(compareBySerial);\n let firstHeaders: Record<string, string> | undefined;\n let firstClientId: string | undefined;\n for (const m of sorted) {\n if (firstHeaders === undefined) {\n firstHeaders = getTransportHeaders(m);\n firstClientId = m.clientId;\n break;\n }\n }\n logger?.debug('AgentView.findInputEvent(); collected input events', {\n runId,\n invocationId,\n count: sorted.length,\n });\n resolve({ rawMessages: sorted, firstHeaders, firstClientId });\n };\n\n // Consider a message for matching against the expected set; returns true\n // when the lookup is now fully satisfied.\n const consider = (m: Ably.InboundMessage): boolean => {\n if (settled) return false;\n const headers = getTransportHeaders(m);\n const eventId = headers[HEADER_EVENT_ID];\n if (!eventId || !expectedSet.has(eventId) || matchedByEventId.has(eventId)) return false;\n matchedByEventId.set(eventId, m);\n return matchedByEventId.size >= expectedCount;\n };\n\n signal.addEventListener('abort', onCancelled, { once: true });\n if (signal.aborted) {\n onCancelled();\n return;\n }\n\n // 1. Pre-scan the Tree's event-id index for already-folded matches.\n // Multi-run sessions where a prior run folded the message hit here\n // synchronously.\n for (const id of expectedEventIds) {\n const ablyMessage = this._tree.findAblyMessageByEventId(id);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- settled may mutate via synchronous callbacks during consider()\n if (ablyMessage && consider(ablyMessage) && !settled) {\n finishOk();\n return;\n }\n }\n\n // 2. Subscribe to the Tree's `ably-message` event for live arrivals.\n // The applier folds first; `emitAblyMessage` notifies subscribers\n // AND populates the event-id index. Wires fed in by the parallel\n // history fetch flow through the same event so the listener picks\n // them up uniformly.\n unregisterLive = this._tree.on('ably-message', (msg) => {\n if (consider(msg) && !settled) finishOk();\n });\n\n // 3. Drive the shared history walk in parallel, lookback-bounded so the\n // scan gives up (pausing the cursor) once it pages past the window\n // rather than walking the whole channel for a missing trigger. Each\n // page folds into the Tree, triggering the listener above. The cursor\n // stays paused at its position; a later loadConversation resumes it.\n // The resolution is discarded — findInputEvent never records exhaustion\n // (loadConversation does, if it drives the cursor to attach).\n this._driveHistoryChain(\n () => settled,\n historyController.signal,\n this._inputEventLookbackMs,\n 'scan history for input event',\n ).catch((error: unknown) => {\n if (settled) return;\n historyError =\n error instanceof Ably.ErrorInfo ? error : wrapHistoryError('scan history for input event', error);\n logger?.warn('AgentView.findInputEvent(); history scan failed (continuing on live path)', {\n error: errorMessage(error),\n });\n });\n\n // 4. Overall timeout — cancels the in-flight history fetch and\n // rejects with InputEventNotFound.\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Ably.ErrorInfo(\n `unable to look up input event; received ${String(matchedByEventId.size)} of ${String(expectedCount)} input events for invocation ${invocationId} within ${String(timeoutMs)}ms`,\n ErrorCode.InputEventNotFound,\n 504,\n historyError,\n ),\n );\n }, timeoutMs);\n // Node returns an unref-able Timeout; browsers return a number. Unref\n // so a parked lookup cannot keep a Node process alive by itself.\n if (typeof timer === 'object') timer.unref();\n });\n }\n\n // -------------------------------------------------------------------------\n // Conversation walk\n // -------------------------------------------------------------------------\n\n /**\n * Reconstruct the conversation by walking the parent chain from the run's\n * input node back to the conversation root, reading already-folded\n * projections off the Tree's nodes.\n *\n * Hydrates the Tree as needed via the shared history walk\n * ({@link AgentView._hydrateAncestors}), then concatenates\n * `codec.getMessages(node.projection)` per node (root first) and appends the\n * current run's projection at the tail.\n * @param runId - The current run's id (for the tail run's projection lookup).\n * @param assistantParentFallback - The current run's input node codec-message-id.\n * @param signal - AbortSignal; rejects with InvalidArgument when aborted.\n * @param maxRuns - Optional bound on the parent walk; counts reply RunNodes.\n * @param runIdAdopted - True when the run-id came from outside (runtime\n * override or continuation), so its node may exist in channel history;\n * false for agent-minted ids, whose run-start only ever arrives via the\n * live echo.\n * @param regenerateTarget - The codec-message-id being regenerated, or\n * undefined; the run that owns it is flattened only up to that message so\n * the reconstructed history stops before the assistant message being\n * replaced (which the model would otherwise reject).\n * @returns The branch's messages (root-first) and the current run's projection.\n */\n async loadConversation(\n runId: string,\n assistantParentFallback: string | undefined,\n signal: AbortSignal,\n maxRuns: number | undefined,\n runIdAdopted: boolean,\n regenerateTarget?: string,\n ): Promise<{ messages: TMessage[]; projection: TProjection }> {\n if (signal.aborted) {\n throw new Ably.ErrorInfo(\n `unable to load conversation; run ${runId} was cancelled`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n await this._hydrateAncestors(runId, assistantParentFallback, signal, maxRuns, runIdAdopted);\n\n return this._collectConversation(runId, assistantParentFallback, maxRuns, regenerateTarget);\n }\n\n /**\n * Walk the parent chain from `anchor` over the current Tree and concatenate\n * each node's projected messages (root-first), then append the current run's\n * own messages when its RunNode isn't already on the chain. Shared by\n * {@link AgentView.loadConversation} and {@link AgentView.messages}. Pure read\n * over whatever is currently folded — no fetching.\n * @param runId - The current run's id (for the tail run's projection lookup).\n * @param anchor - The current run's input node codec-message-id.\n * @param maxRuns - Optional bound on the ancestor walk (counts reply runs).\n * @param regenerateTarget - The codec-message-id being regenerated; when set,\n * the walk stops before that message (a regenerate of a non-head message\n * anchors at the target's predecessor, so flattening its run whole would\n * re-emit the target and end the history on the message being replaced).\n * @returns The conversation messages (root-first) and the current run's\n * projection (the codec's empty init when the run has no node yet).\n */\n private _collectConversation(\n runId: string,\n anchor: string | undefined,\n maxRuns?: number,\n regenerateTarget?: string,\n ): { messages: TMessage[]; projection: TProjection } {\n const tree = this._tree;\n const chain = walkAncestorChain(tree, anchor, maxRuns, runId);\n const runNode = tree.getRunNode(runId);\n const messages: TMessage[] = [];\n for (const node of chain) {\n for (const m of this._codec.getMessages(node.projection)) {\n if (regenerateTarget !== undefined && m.codecMessageId === regenerateTarget) {\n return { messages, projection: runNode?.projection ?? this._codec.init() };\n }\n messages.push(m.message);\n }\n }\n\n if (runNode !== undefined && !chain.some((n) => n.kind === 'run' && n.runId === runId)) {\n for (const m of this._codec.getMessages(runNode.projection)) {\n messages.push(m.message);\n }\n }\n\n return { messages, projection: runNode?.projection ?? this._codec.init() };\n }\n\n /**\n * Synchronous live read of the conversation messages for `Run.messages`:\n * walk the parent chain from `anchor` (no `maxRuns` bound), concatenate each\n * ancestor's projection, then append the current run's messages if its node\n * isn't already on the chain. No I/O — reflects whatever is currently folded.\n * @param runId - The current run's id (for the tail run's projection lookup).\n * @param anchor - The current run's input node codec-message-id (assistantParentFallback).\n * @param regenerateTarget - The codec-message-id being regenerated; when set,\n * the walk stops before it (see {@link AgentView._collectConversation}).\n * @returns The conversation messages, root-first.\n */\n messages(runId: string, anchor: string | undefined, regenerateTarget?: string): TMessage[] {\n return this._collectConversation(runId, anchor, undefined, regenerateTarget).messages;\n }\n\n // -------------------------------------------------------------------------\n // Shared history walk\n // -------------------------------------------------------------------------\n\n /**\n * Single-flight chain entry shared by `findInputEvent` and `loadConversation`.\n * Serialises behind any in-flight walk so the shared cursor is advanced by one\n * caller at a time (never paged concurrently), then runs one\n * {@link AgentView._walkSharedHistory}. A link never rejects (it records its\n * error locally), so a follower awaiting the chain tail is isolated from a\n * prior link's failure; this method rethrows the wrapped error from its own\n * frame after awaiting.\n *\n * Returns `exhausted` but never records `_historyExhausted`; the caller records\n * it (both callers may, since the shared cursor's exhaustion is always genuine\n * — see {@link AgentView._historyExhausted}).\n * @param shouldStop - Polled before each page; true pauses this walk.\n * @param signal - Per-call abort signal (checked between pages).\n * @param lookbackMs - Optional give-up bound for the input scan (early break).\n * @param operationLabel - Verb for the wrapped error message.\n * @returns `{ exhausted }` — true only when the shared cursor reached attach.\n */\n private async _driveHistoryChain(\n shouldStop: () => boolean,\n signal: AbortSignal,\n lookbackMs: number | undefined,\n operationLabel: string,\n ): Promise<{ exhausted: boolean }> {\n let exhausted = false;\n let fetchError: Ably.ErrorInfo | undefined;\n const prev = this._hydrationMutex ?? Promise.resolve();\n const mine = (async (): Promise<void> => {\n await prev.catch(() => {\n /* a prior link's failure is its own to throw; this link fetches independently */\n });\n if (this._historyExhausted || signal.aborted || shouldStop()) return;\n try {\n exhausted = await this._walkSharedHistory(shouldStop, signal, lookbackMs);\n } catch (error) {\n fetchError = wrapHistoryError(operationLabel, error);\n }\n })();\n this._hydrationMutex = mine;\n await mine;\n if (fetchError !== undefined) throw fetchError;\n return { exhausted };\n }\n\n /**\n * Advance the SHARED history cursor (lazily opening it once per attach epoch)\n * and fold each page into the session Tree via the injected `fold`, stopping\n * when `shouldStop()` returns true, the channel is exhausted, the signal\n * aborts, a continuity-loss Tree swap abandons the walk, or — when `lookbackMs`\n * is given — the walk pages past the lookback window. The cursor is NOT closed\n * on stop: it stays paused at its current position so a later caller resumes\n * from there rather than re-paging from newest. Throws (caller-wrapped) on a\n * fetch failure after `loadHistoryPages`' per-page retries.\n * @param shouldStop - Polled before each page; true pauses the walk.\n * @param signal - Per-call abort signal (checked between pages; the shared cursor carries none).\n * @param lookbackMs - Optional give-up bound: stop paging once a page's oldest\n * message predates `Date.now() - lookbackMs`. An early `break`, NOT a cursor\n * bound, so the cursor stays resumable and exhaustion is never reported here.\n * @returns True only when the cursor genuinely reached attach — NOT when\n * paused by the predicate / lookback, a Tree swap, or signal abort.\n */\n private async _walkSharedHistory(\n shouldStop: () => boolean,\n signal: AbortSignal,\n lookbackMs?: number,\n ): Promise<boolean> {\n if (this._cursor === undefined) {\n this._cursor = await loadHistoryPages(this._channel, {\n pageLimit: 200,\n untilAttach: true,\n logger: this._logger,\n });\n }\n const cursor = this._cursor;\n while (cursor.hasNext() && !shouldStop()) {\n if (signal.aborted) return false;\n const chunk = await cursor.next();\n // `next()` returning undefined means the cursor is permanently spent\n // (it has cleared its current page) — genuine exhaustion.\n if (!chunk) break;\n // Ably returns history pages newest-first; fold in chronological order so\n // codec projections build oldest-to-newest (matches the live decode loop).\n for (const wire of chunk.toReversed()) {\n this._foldWire(wire);\n }\n // findInputEvent's give-up bound: once this page predates the lookback\n // window, stop scanning. The cursor stays open (hasNext() still true), so\n // loadConversation can resume past here and this never reports exhaustion.\n if (lookbackMs !== undefined) {\n const oldest = chunk.at(-1);\n if (oldest?.timestamp !== undefined && oldest.timestamp < Date.now() - lookbackMs) break;\n }\n }\n // Genuine exhaustion only: the cursor reached attach and the walk wasn't aborted.\n return !cursor.hasNext() && !signal.aborted;\n }\n\n /**\n * Populate the Tree with enough ancestor coverage to walk from `anchor` to\n * root (or `maxRuns` reply runs back) by driving the shared history walk.\n * Records `_historyExhausted` only when a FULL (no-lookback) walk genuinely\n * exhausts the channel.\n * @param runId - The current run's id (when adopted, its node must be present in the Tree before the walk is complete).\n * @param anchor - The input codec-message-id to walk from. Undefined means no walk is needed (current run only).\n * @param signal - AbortSignal.\n * @param maxRuns - Optional bound on the ancestor walk.\n * @param runIdAdopted - Whether the run-id came from outside (override or continuation) and so may name a run present in channel history.\n * @throws {Ably.ErrorInfo} `InvalidArgument` when `signal` aborts;\n * `HistoryFetchFailed` — or the underlying Ably code when the failure\n * carried one — (original as `cause`) when this caller's own history\n * fetch fails after retries.\n */\n private async _hydrateAncestors(\n runId: string,\n anchor: string | undefined,\n signal: AbortSignal,\n maxRuns: number | undefined,\n runIdAdopted: boolean,\n ): Promise<void> {\n // Check whether the Tree already has what we need: the current run node\n // exists AND (no anchor OR anchor's chain reaches root / maxRuns).\n const needsFetch = (): boolean => {\n const tree = this._tree;\n // Only an adopted run-id (runtime override or continuation) can name a\n // run already present in channel history. A fresh agent-minted run's\n // run-start is published after attach, so the `untilAttach` walk can\n // never surface it; demanding it would page the whole channel to\n // exhaustion. Fresh runs are satisfied by start()'s optimistic insert.\n // For adopted ids the node must be serial-CONFIRMED: an override id's\n // optimistic insert is serial-less, and its history content (if any)\n // still needs hydrating.\n if (runIdAdopted && tree.getRunNode(runId)?.startSerial === undefined) return true;\n if (anchor === undefined) return false;\n if (tree.getNodeByCodecMessageId(anchor) === undefined) return true;\n const chain = walkAncestorChain(tree, anchor, maxRuns, runId);\n const head = chain[0];\n const reachedRoot = head !== undefined && head.parentCodecMessageId === undefined;\n // The bound is only satisfied once the bounding run's triggering input\n // is in the chain — a head that is still an ancestor RunNode means the\n // input above it hasn't been hydrated yet (assistant-first context).\n const reachedLimit =\n maxRuns !== undefined &&\n countReplyRuns(chain, runId) >= maxRuns &&\n head !== undefined &&\n (head.kind !== 'run' || head.runId === runId);\n return !reachedRoot && !reachedLimit;\n };\n\n // Already satisfied, or a prior full walk this epoch drove history to\n // exhaustion (fetching again cannot reveal more) — nothing to do.\n if (!needsFetch() || this._historyExhausted) return;\n\n let exhausted: boolean;\n try {\n // Full walk — NO lookback — so an exhausted return is authoritative for\n // the attach epoch and may be recorded.\n ({ exhausted } = await this._driveHistoryChain(() => !needsFetch(), signal, undefined, 'hydrate ancestors'));\n } catch (error) {\n this._logger?.error('AgentView._hydrateAncestors(); history fetch failed', {\n runId,\n error: errorMessage(error),\n });\n throw error;\n }\n if (exhausted) this._historyExhausted = true;\n // A between-pages abort unwinds the fold cleanly (no throw); surface it as\n // the cancellation the caller expects rather than returning partial history.\n if (signal.aborted && needsFetch()) {\n throw new Ably.ErrorInfo('unable to hydrate ancestors; signal aborted', ErrorCode.InvalidArgument, 400);\n }\n }\n}\n\n/**\n * Create an {@link AgentView}. Factory entry point mirroring `createTree`;\n * AgentSession never calls `new AgentView` directly.\n * @param options - Injected dependencies.\n * @returns A new AgentView.\n */\nexport const createAgentView = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n options: AgentViewOptions<TInput, TOutput, TProjection, TMessage>,\n): AgentView<TInput, TOutput, TProjection, TMessage> => new AgentView(options);\n","/**\n * Transport header builder.\n *\n * Single source of truth for which transport headers every transport\n * message carries. Used by the agent session (pipe) and by\n * the client session (optimistic message stamping).\n */\n\nimport * as Ably from 'ably';\n\nimport {\n EVENT_RUN_END,\n EVENT_RUN_RESUME,\n EVENT_RUN_START,\n EVENT_RUN_SUSPEND,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_ERROR_CODE,\n HEADER_ERROR_MESSAGE,\n HEADER_EVENT_ID,\n HEADER_FORK_OF,\n HEADER_INPUT_CLIENT_ID,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_INVOCATION_ID,\n HEADER_MSG_REGENERATE,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_RUN_CLIENT_ID,\n HEADER_RUN_ID,\n HEADER_RUN_REASON,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport type { RunEndReason, RunLifecycleEvent } from './types.js';\n\n/**\n * Build the standard transport header set for a message.\n * @param opts - The header values to include.\n * @param opts.role - Message role (e.g. \"user\", \"assistant\").\n * @param opts.runId - Run correlation ID, or `undefined` for a fresh client\n * input (the agent mints run-ids, so it is not known synchronously). Omitted\n * from the headers when undefined; a continuation still carries the known run-id.\n * @param opts.codecMessageId - Message identity — the wire `codec-message-id` for this message.\n * @param opts.runClientId - ClientId of the run initiator.\n * @param opts.parent - Preceding message's codec-message-id (for branching).\n * @param opts.forkOf - Forked user-prompt's codec-message-id (for edits — creates a Run-level fork sibling).\n * @param opts.regenerates - Assistant codec-message-id this run regenerates. Stamps\n * `msg-regenerate`. Distinct from `forkOf`: regenerate is a\n * continuation of the prior run (no Run-level fork), with the message\n * replacement resolved at projection extraction time.\n * @param opts.invocationId - Agent-minted invocation id. Stamped by the agent on every event it publishes for the invocation (run lifecycle + outputs) so the client can observe it; not set by the client on the input.\n * @param opts.inputClientId - ClientId of the input event (the `ai-input`) that\n * drove the current invocation. The agent reads it from the publisher's\n * Ably-level `clientId` on the matched input event and re-stamps it on its\n * own publishes (run lifecycle + outputs). Differs from `runClientId` on\n * continuation invocations driven by an input from a non-owner.\n * @param opts.inputEventId - Per-event identifier. Set on each client-published user-prompt message; the invocation body's `inputEventIds` lists the ids the agent should look up.\n * @param opts.inputCodecMessageId - The codec-message-id of the input event that\n * triggered the current invocation (the one whose `event-id` matched the\n * invocation's `inputEventId`). The agent re-stamps it on every event it\n * publishes for the invocation (run lifecycle + outputs), mirroring\n * `inputClientId`, so the client can correlate any of those events back to\n * the originating input by the id it owned at send time.\n * @returns A headers record with the transport headers set.\n */\nexport const buildTransportHeaders = (opts: {\n role: string;\n runId?: string;\n codecMessageId: string;\n runClientId?: string;\n parent?: string;\n forkOf?: string;\n regenerates?: string;\n invocationId?: string;\n inputClientId?: string;\n inputCodecMessageId?: string;\n inputEventId?: string;\n}): Record<string, string> => {\n const h: Record<string, string> = {\n [HEADER_ROLE]: opts.role,\n [HEADER_CODEC_MESSAGE_ID]: opts.codecMessageId,\n };\n if (opts.runId !== undefined) h[HEADER_RUN_ID] = opts.runId;\n if (opts.runClientId !== undefined) h[HEADER_RUN_CLIENT_ID] = opts.runClientId;\n if (opts.parent) h[HEADER_PARENT] = opts.parent;\n if (opts.forkOf) h[HEADER_FORK_OF] = opts.forkOf;\n if (opts.regenerates) h[HEADER_MSG_REGENERATE] = opts.regenerates;\n if (opts.invocationId) h[HEADER_INVOCATION_ID] = opts.invocationId;\n if (opts.inputClientId !== undefined) h[HEADER_INPUT_CLIENT_ID] = opts.inputClientId;\n if (opts.inputCodecMessageId !== undefined) h[HEADER_INPUT_CODEC_MESSAGE_ID] = opts.inputCodecMessageId;\n if (opts.inputEventId) h[HEADER_EVENT_ID] = opts.inputEventId;\n return h;\n};\n\n/**\n * Build the transport header set for a run-lifecycle event (run-start,\n * run-resume, run-suspend, run-end). Single source of truth for lifecycle\n * header stamping, mirroring {@link buildTransportHeaders} for the\n * message-carrier path. Every field except `runId`/`runClientId` is optional\n * and omitted when not provided.\n *\n * A resume suppresses the structural `parent` / `forkOf` / `regenerates`\n * headers — the caller passes them only for a fresh run-start. `reason` is\n * stamped only on run-end.\n * @param opts - The lifecycle header values to include.\n * @param opts.runId - The run's id.\n * @param opts.runClientId - ClientId of the run initiator (empty string when unknown).\n * @param opts.parent - Structural parent codec-message-id (fresh run-start only).\n * @param opts.forkOf - Forked user-prompt codec-message-id (fresh run-start only).\n * @param opts.regenerates - Regenerated assistant codec-message-id (fresh run-start only).\n * @param opts.invocationId - Agent-minted invocation id; carried on every lifecycle event.\n * @param opts.inputClientId - ClientId of the triggering input event.\n * @param opts.inputCodecMessageId - Codec-message-id of the triggering input event.\n * @param opts.reason - Terminal reason; stamped on run-end only.\n * @param opts.errorCode - Numeric error code stamped as `error-code` on\n * run-end. Set only when the run ended in error and the agent supplied an\n * error to surface; gives codec-agnostic consumers a baseline failure detail.\n * @param opts.errorMessage - Error message stamped as `error-message` on\n * run-end. Paired with `errorCode`; set under the same condition.\n * @returns A headers record with the lifecycle headers set.\n */\nexport const buildLifecycleHeaders = (opts: {\n runId: string;\n runClientId: string;\n parent?: string;\n forkOf?: string;\n regenerates?: string;\n invocationId?: string;\n inputClientId?: string;\n inputCodecMessageId?: string;\n reason?: RunEndReason;\n errorCode?: number;\n errorMessage?: string;\n}): Record<string, string> => {\n const h: Record<string, string> = {\n [HEADER_RUN_ID]: opts.runId,\n [HEADER_RUN_CLIENT_ID]: opts.runClientId,\n };\n if (opts.reason !== undefined) h[HEADER_RUN_REASON] = opts.reason;\n if (opts.parent !== undefined) h[HEADER_PARENT] = opts.parent;\n if (opts.forkOf !== undefined) h[HEADER_FORK_OF] = opts.forkOf;\n if (opts.regenerates !== undefined) h[HEADER_MSG_REGENERATE] = opts.regenerates;\n if (opts.invocationId !== undefined) h[HEADER_INVOCATION_ID] = opts.invocationId;\n if (opts.inputClientId !== undefined) h[HEADER_INPUT_CLIENT_ID] = opts.inputClientId;\n if (opts.inputCodecMessageId !== undefined) h[HEADER_INPUT_CODEC_MESSAGE_ID] = opts.inputCodecMessageId;\n if (opts.errorCode !== undefined) h[HEADER_ERROR_CODE] = String(opts.errorCode);\n if (opts.errorMessage !== undefined) h[HEADER_ERROR_MESSAGE] = opts.errorMessage;\n return h;\n};\n\n/** The four run-lifecycle Ably message names. */\ntype RunLifecycleName =\n | typeof EVENT_RUN_START\n | typeof EVENT_RUN_SUSPEND\n | typeof EVENT_RUN_RESUME\n | typeof EVENT_RUN_END;\n\n/**\n * Whether an Ably message `name` is one of the run-lifecycle event names\n * (run-start / run-suspend / run-resume / run-end). Single source of truth for\n * the classification both decode loops and the agent's history scan use to\n * route lifecycle wires away from the codec decoder. Narrows `name` to a\n * lifecycle name so callers can pass it straight to {@link parseRunLifecycle}.\n * @param name - The inbound Ably message `name`, or undefined.\n * @returns True when `name` is a run-lifecycle event name.\n */\nexport const isRunLifecycleName = (name: string | undefined): name is RunLifecycleName =>\n name === EVENT_RUN_START || name === EVENT_RUN_SUSPEND || name === EVENT_RUN_RESUME || name === EVENT_RUN_END;\n\n/**\n * Reconstruct the terminal `Ably.ErrorInfo` for a run that ended in error, from\n * its run-end transport headers. Reads the `error-code` / `error-message`\n * headers the agent stamps (see {@link buildLifecycleHeaders}); falls back to a\n * generic code/message when a run ended in error without detail. Single source\n * of truth for the header→ErrorInfo derivation, shared by the client session's\n * `on('error')` emit and the Tree's `RunInfo.error`.\n * @param headers - Transport headers from the inbound run-end message.\n * @returns The reconstructed terminal error.\n */\nexport const buildRunEndError = (headers: Record<string, string>): Ably.ErrorInfo => {\n const codeRaw = headers[HEADER_ERROR_CODE];\n const parsedCode = codeRaw === undefined ? Number.NaN : Number(codeRaw);\n const code = Number.isFinite(parsedCode) ? parsedCode : ErrorCode.SessionSubscriptionError;\n const message = headers[HEADER_ERROR_MESSAGE] ?? 'agent reported an error';\n // 5-digit codes encode their HTTP status in the leading 3 digits; otherwise 500.\n const statusCode = code >= 10000 && code < 60000 ? Math.floor(code / 100) : 500;\n return new Ably.ErrorInfo(message, code, statusCode);\n};\n\n/**\n * Parse an inbound run-lifecycle Ably message into a {@link RunLifecycleEvent}.\n *\n * Single source of truth for turning the wire run-lifecycle message `name`,\n * transport headers, and channel serial into the structured lifecycle event\n * the Tree consumes. Used by the client decode loop (live) and the View's\n * history replay so both build the event identically.\n * @param name - The inbound Ably message `name`.\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial of the message, or `undefined` for an\n * optimistic local event. Stamped onto the returned event.\n * @param timestamp - Ably server timestamp (epoch ms) of the message, or\n * `undefined` for an optimistic local event. Stamped onto the returned\n * event; drives the Tree's event-log retention clock.\n * @returns The lifecycle event, or `undefined` when `name` is not a\n * run-lifecycle event name or the message carries no `run-id`.\n */\nexport const parseRunLifecycle = (\n name: string,\n headers: Record<string, string>,\n serial: string | undefined,\n timestamp: number | undefined,\n): RunLifecycleEvent | undefined => {\n const runId = headers[HEADER_RUN_ID];\n if (!runId) return undefined;\n\n const clientId = headers[HEADER_RUN_CLIENT_ID] ?? '';\n const stamped = timestamp === undefined ? {} : { timestamp };\n\n if (name === EVENT_RUN_START) {\n const parent = headers[HEADER_PARENT];\n const forkOf = headers[HEADER_FORK_OF];\n const regenerates = headers[HEADER_MSG_REGENERATE];\n return {\n type: 'start',\n runId,\n clientId,\n serial,\n invocationId: headers[HEADER_INVOCATION_ID] ?? '',\n ...stamped,\n ...(parent !== undefined && { parent }),\n ...(forkOf !== undefined && { forkOf }),\n ...(regenerates !== undefined && { regenerates }),\n };\n }\n\n if (name === EVENT_RUN_SUSPEND) {\n return { type: 'suspend', runId, clientId, serial, invocationId: headers[HEADER_INVOCATION_ID] ?? '', ...stamped };\n }\n\n if (name === EVENT_RUN_RESUME) {\n return { type: 'resume', runId, clientId, serial, invocationId: headers[HEADER_INVOCATION_ID] ?? '', ...stamped };\n }\n\n if (name === EVENT_RUN_END) {\n // CAST: agent always writes a valid RunEndReason; default to 'complete' for robustness.\n const reason = (headers[HEADER_RUN_REASON] ?? 'complete') as RunEndReason;\n const invocationId = headers[HEADER_INVOCATION_ID] ?? '';\n if (reason === 'error') {\n return {\n type: 'end',\n runId,\n clientId,\n serial,\n invocationId,\n reason,\n ...stamped,\n error: buildRunEndError(headers),\n };\n }\n return { type: 'end', runId, clientId, serial, invocationId, reason, ...stamped };\n }\n\n return undefined;\n};\n","/**\n * Shared wire decode-and-apply engine.\n *\n * The client's live decode loop and the View's history replay both reconstruct\n * the conversation Tree from the same raw Ably wire log. This module is the one\n * place that classifies a wire message (run-lifecycle vs codec-decoded), parses\n * or decodes it, and applies it to the Tree — so the two paths can never drift.\n *\n * The engine is exposed as a {@link WireApplier} binding one Tree to one\n * decoder. A Tree has exactly one applier (the session constructs it and hands\n * it to every View), so every route a wire message can arrive by — the live\n * subscription, View history pagination, the agent's hydration walks — feeds\n * the same decoder. The decoder's version-guarded stream trackers then make\n * re-delivery across routes (an attach-boundary in-flight stream, a replayed\n * history page) decode to nothing instead of double-folding. The delivery's\n * `version.serial` is also threaded into the Tree, whose per-entry\n * `decodedThrough` high-water-mark drops whole-wire replays that no decoder\n * state can see (stateless discrete re-decodes).\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_RUN_ID } from '../../constants.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport type { CodecInputEvent, CodecOutputEvent, Decoder } from '../codec/types.js';\nimport { isRunLifecycleName, parseRunLifecycle } from './headers.js';\nimport type { TreeInternal } from './tree.js';\nimport type { RunLifecycleEvent } from './types.js';\n\n/**\n * The decode-and-apply engine for one Tree: a single codec decoder bound to a\n * single Tree, shared by every route that feeds the Tree wire messages.\n */\nexport interface WireApplier {\n /**\n * Apply one inbound wire message to the bound tree.\n *\n * Run-lifecycle messages are turned into a {@link RunLifecycleEvent} via\n * {@link parseRunLifecycle} and applied with `applyRunLifecycle`; everything\n * else is decoded with the bound decoder and applied with `applyMessage`,\n * skipping wire-only carriers that decode to no events and carry no run-id\n * (the eventual reply run is created later by its run-start).\n *\n * Does NOT emit the tree's `ably-message` event — the caller owns that,\n * because the live loop emits per message while history replay emits in a\n * batch once the whole page is applied. Returns the parsed lifecycle event\n * so a live caller can run its own side-effects (resolving a pending\n * run-start, surfacing an agent error); returns `undefined` for a\n * codec-decoded message or a lifecycle message that carried no run-id.\n * @param rawMsg - The inbound Ably wire message.\n * @returns The parsed run-lifecycle event, or `undefined`.\n */\n apply(rawMsg: Ably.InboundMessage): RunLifecycleEvent | undefined;\n}\n\n/**\n * Classify, decode, and apply one inbound wire message to the tree. See\n * {@link WireApplier.apply} for the contract.\n * @param tree - The tree to apply the message to.\n * @param decoder - The codec decoder used for non-lifecycle messages.\n * @param rawMsg - The inbound Ably wire message.\n * @returns The parsed run-lifecycle event, or `undefined`.\n */\nconst applyWireMessage = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(\n tree: TreeInternal<TInput, TOutput, TProjection>,\n decoder: Decoder<TInput, TOutput>,\n rawMsg: Ably.InboundMessage,\n): RunLifecycleEvent | undefined => {\n const headers = getTransportHeaders(rawMsg);\n const serial = rawMsg.serial;\n // Top-level timestamp — the message's create time on every delivery (an\n // append's own receive time lives in `version.timestamp`). The retention\n // clock is sound on this timeline because run-end, a fresh create published\n // after every wire of its run, bounds the node's last activity.\n const timestamp = rawMsg.timestamp;\n\n if (isRunLifecycleName(rawMsg.name)) {\n const event = parseRunLifecycle(rawMsg.name, headers, serial, timestamp);\n if (event) tree.applyRunLifecycle(event);\n return event;\n }\n\n const { inputs, outputs } = decoder.decode(rawMsg);\n if (inputs.length > 0 || outputs.length > 0 || headers[HEADER_RUN_ID]) {\n tree.applyMessage({ inputs, outputs }, headers, serial, timestamp, rawMsg.version.serial);\n }\n return undefined;\n};\n\n/**\n * Bind a Tree and a decoder into the Tree's single {@link WireApplier}.\n * @param tree - The tree the applier feeds.\n * @param decoder - The codec decoder shared by every route into the tree.\n * @returns The applier.\n */\nexport const createWireApplier = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(\n tree: TreeInternal<TInput, TOutput, TProjection>,\n decoder: Decoder<TInput, TOutput>,\n): WireApplier => ({\n apply: (rawMsg: Ably.InboundMessage): RunLifecycleEvent | undefined => applyWireMessage(tree, decoder, rawMsg),\n});\n","/**\n * Helpers for FIFO-bounded `Map`s — Maps used as capacity-limited buffers that\n * must evict their oldest entry when full.\n */\n\n/**\n * Make room for a new key in a FIFO-bounded map: if `map` is at (or over)\n * `limit` and does not already contain `key`, evict the oldest entry (insertion\n * order) and return its key so the caller can log the eviction. Returns\n * `undefined` when nothing was evicted (the key already exists, the map is\n * below the limit, or it is empty).\n *\n * The caller performs the actual set/append afterwards — this only frees a\n * slot — so it works for maps whose values are replaced and for maps whose\n * values are appended-to lists.\n * @param map - The bounded map to evict from.\n * @param key - The key about to be added; an existing key never evicts.\n * @param limit - The maximum number of entries the map may hold.\n * @returns The evicted key, or `undefined` if nothing was evicted.\n */\nexport const evictOldestIfFull = <K, V>(map: Map<K, V>, key: K, limit: number): K | undefined => {\n if (map.has(key) || map.size < limit) return undefined;\n const oldest = map.keys().next().value;\n if (oldest === undefined) return undefined;\n map.delete(oldest);\n return oldest;\n};\n","/**\n * Pure stream piping function.\n *\n * Reads outputs from a ReadableStream, writes them to an encoder via\n * `publishOutput`, and handles cancel/error. No dependencies on run\n * state or session internals.\n */\n\nimport type { Logger } from '../../logger.js';\nimport type { CodecInputEvent, CodecOutputEvent, Encoder, WriteOptions } from '../codec/types.js';\nimport type { StreamResult } from './types.js';\n\n/**\n * Adapt an AbortSignal into a promise that resolves once the signal aborts,\n * paired with a cleanup that detaches the listener. With no signal the promise\n * never resolves (there is no cancellation path); an already-aborted signal\n * resolves immediately. `cleanup` is a no-op unless a listener was attached.\n * @param signal - The AbortSignal to watch, or undefined for no cancellation.\n * @returns The abort promise and a cleanup to call when racing is done.\n */\nconst abortSignalToPromise = (signal: AbortSignal | undefined): { promise: Promise<void>; cleanup: () => void } => {\n let listener: (() => void) | undefined;\n const promise =\n signal === undefined\n ? // eslint-disable-next-line @typescript-eslint/no-empty-function -- never-resolving promise: no signal means no cancellation path\n new Promise<void>(() => {})\n : signal.aborted\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n listener = () => {\n resolve();\n };\n signal.addEventListener('abort', listener, { once: true });\n });\n const cleanup = (): void => {\n if (listener && signal) signal.removeEventListener('abort', listener);\n };\n return { promise, cleanup };\n};\n\n/**\n * Pipe an output stream through an encoder to the channel.\n *\n * Returns when the stream completes, is cancelled (via signal), or errors.\n * The `reason` field of the result indicates which case occurred.\n * @param stream - The output stream to read from.\n * @param encoder - The encoder to publish outputs through.\n * @param signal - AbortSignal to monitor for cancellation.\n * @param onCancelled - Optional callback invoked when the stream is cancelled, before the stream ends.\n * @param resolveWriteOptions - Optional per-output hook returning {@link WriteOptions} overrides to pass to `encoder.publishOutput`.\n * @param logger - Optional logger for diagnostic output.\n * @returns A {@link StreamResult}: `reason` is why the pipe ended, and `error` holds the caught error when `reason` is `'error'`.\n */\nexport const pipeStream = async <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>(\n stream: ReadableStream<TOutput>,\n encoder: Encoder<TInput, TOutput>,\n signal: AbortSignal | undefined,\n onCancelled?: (write: (output: TOutput) => Promise<void>) => void | Promise<void>,\n resolveWriteOptions?: (output: TOutput) => WriteOptions | undefined,\n logger?: Logger,\n): Promise<StreamResult> => {\n logger?.trace('pipeStream();');\n\n const reader = stream.getReader();\n const abort = abortSignalToPromise(signal);\n\n let reason: StreamResult['reason'] = 'complete';\n let caughtError: Error | undefined;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop broken by return/break\n while (true) {\n // .then() is intentional: transforms the AbortSignal into a discriminant\n // for Promise.race — no async/await equivalent for this pattern.\n const result = await Promise.race([reader.read(), abort.promise.then(() => 'cancelled' as const)]);\n\n if (result === 'cancelled') {\n reason = 'cancelled';\n logger?.debug('pipeStream(); stream cancelled by AbortSignal');\n if (onCancelled) {\n await onCancelled(async (output: TOutput) => encoder.publishOutput(output));\n }\n // Transport mechanics only — close in-flight streamed messages as\n // cancelled. Run termination is the transport ai-run-end event,\n // guaranteed by Run.pipe on a cancelled result.\n await encoder.cancelStreams();\n break;\n }\n\n const { done, value } = result;\n if (done) {\n // An agent-side self-abort (e.g. the AI SDK's abort signal firing)\n // completes the stream without end chunks for in-flight streamed\n // messages. Terminate any still-open wire streams with a cancelled\n // status so decoders and history see a terminal; streams that closed\n // normally are skipped (no-op on a clean completion).\n await encoder.cancelStreams();\n await encoder.close();\n logger?.debug('pipeStream(); stream completed');\n break;\n }\n\n await encoder.publishOutput(value, resolveWriteOptions?.(value));\n }\n } catch (error) {\n reason = 'error';\n caughtError = error instanceof Error ? error : new Error(String(error));\n logger?.error('pipeStream(); stream error', { error: caughtError.message });\n try {\n await encoder.close();\n } catch {\n // Best-effort: encoder close in the error path may also fail\n // (e.g. channel disconnected). The original error is preserved in\n // the StreamResult reason (\"error\").\n }\n } finally {\n abort.cleanup();\n reader.releaseLock();\n }\n\n return { reason, error: caughtError };\n};\n","/**\n * Server-side run state management and lifecycle event publishing.\n *\n * Owns the authoritative run lifecycle. Tracks active runs with their\n * AbortControllers and clientIds. Publishes run-start, run-resume, run-suspend, and\n * run-end events on the Ably channel so all clients can react to run\n * state changes.\n */\n\nimport type * as Ably from 'ably';\n\nimport { EVENT_RUN_END, EVENT_RUN_RESUME, EVENT_RUN_START, EVENT_RUN_SUSPEND } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { buildLifecycleHeaders } from './headers.js';\nimport type { RunEndReason } from './types.js';\n\n/**\n * Per-invocation metadata carried on a run's opening lifecycle event. A\n * continuation (re-entering an existing run) sets `continuation` and omits the\n * structural `parent` / `forkOf` / `regenerates` fields.\n */\ninterface StartRunMetadata {\n /** Structural parent codec-message-id (fresh run-start only). */\n parent?: string;\n /** Forked user-prompt codec-message-id for an edit (fresh run-start only). */\n forkOf?: string;\n /** Regenerated assistant codec-message-id (fresh run-start only). */\n regenerates?: string;\n /** Agent-minted invocation id, carried on the lifecycle event. */\n invocationId?: string;\n /** ClientId of the triggering input event. */\n inputClientId?: string;\n /** Codec-message-id of the triggering input event. */\n inputCodecMessageId?: string;\n /** When true, publish `ai-run-resume` (re-entry) instead of `ai-run-start`. */\n continuation?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\n/** Manages active runs and publishes run lifecycle events on the channel. */\nexport interface RunManager {\n /**\n * Register a run and publish its opening lifecycle event. Publishes\n * `ai-run-start` for a fresh run, or `ai-run-resume` when `metadata.continuation`\n * is set (a subsequent invocation re-entering an existing run). A resume omits\n * the structural `parent` / `forkOf` / `regenerates` headers — the original\n * run-start owns the run's structure.\n */\n startRun(runId: string, clientId?: string, controller?: AbortController, metadata?: StartRunMetadata): Promise<void>;\n /**\n * Suspend a run. Publishes run-suspend on the channel and drops the run's\n * active-run entry — the agent process terminates on suspend, so there is no\n * live AbortController to retain. A cancel arriving during suspension is a\n * no-op; the resuming invocation re-registers the run via {@link startRun}.\n * Carries the same per-invocation attribution as {@link endRun}\n * (`inputClientId`, `inputCodecMessageId`), since a suspend is the terminal\n * event of the suspending invocation just as run-end is of an ending one.\n */\n suspendRun(runId: string, invocationId?: string, inputClientId?: string, inputCodecMessageId?: string): Promise<void>;\n /**\n * End a run. Publishes run-end on the channel (stamping `reason` as the\n * run-reason header) and drops the run's active-run entry. Carries the same\n * per-invocation attribution as {@link suspendRun} (`invocationId`,\n * `inputClientId`, `inputCodecMessageId`), since run-end is the terminal event\n * of the ending invocation. When `reason` is `'error'` and an `error` is\n * supplied, its `code` and `message` are additionally stamped as the\n * `error-code` / `error-message` headers — a codec-agnostic baseline failure\n * detail for consumers; omitting `error` publishes a bare `reason: 'error'`.\n */\n endRun(\n runId: string,\n reason: RunEndReason,\n invocationId?: string,\n inputClientId?: string,\n inputCodecMessageId?: string,\n error?: Ably.ErrorInfo,\n ): Promise<void>;\n /** Get the clientId that owns a run. */\n getClientId(runId: string): string | undefined;\n /** Cancel all active runs and clear state. */\n close(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal state\n// ---------------------------------------------------------------------------\n\ninterface ActiveRunEntry {\n controller: AbortController;\n clientId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nclass DefaultRunManager implements RunManager {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _logger: Logger | undefined;\n private readonly _activeRuns = new Map<string, ActiveRunEntry>();\n\n constructor(channel: Ably.RealtimeChannel, logger?: Logger) {\n this._channel = channel;\n this._logger = logger?.withContext({ component: 'RunManager' });\n }\n\n async startRun(\n runId: string,\n clientId?: string,\n externalController?: AbortController,\n metadata?: StartRunMetadata,\n ): Promise<void> {\n this._logger?.trace('DefaultRunManager.startRun();', { runId, clientId });\n\n const controller = externalController ?? new AbortController();\n const resolvedClientId = clientId ?? '';\n this._activeRuns.set(runId, { controller, clientId: resolvedClientId });\n\n // A continuation re-enters an already-started run: publish `ai-run-resume`\n // rather than `ai-run-start`. Resume is a pure re-entry signal — the\n // original run-start already established the run's structure, so the\n // parent / forkOf / regenerates metadata is NOT re-stamped here (doing so\n // would point the run at content within itself). The agent learned this is\n // a continuation from the run-id on the triggering input; the re-entry is\n // conveyed to clients by the event name, not a header echo. The\n // invocation-id / input attribution headers are carried on both.\n const continuation = metadata?.continuation === true;\n\n const headers = buildLifecycleHeaders({\n runId,\n runClientId: resolvedClientId,\n parent: continuation ? undefined : metadata?.parent,\n forkOf: continuation ? undefined : metadata?.forkOf,\n regenerates: continuation ? undefined : metadata?.regenerates,\n invocationId: metadata?.invocationId,\n inputClientId: metadata?.inputClientId,\n inputCodecMessageId: metadata?.inputCodecMessageId,\n });\n\n await this._channel.publish({\n name: continuation ? EVENT_RUN_RESUME : EVENT_RUN_START,\n extras: { ai: { transport: headers } },\n });\n\n this._logger?.debug('DefaultRunManager.startRun(); run started', { runId });\n }\n\n async suspendRun(\n runId: string,\n invocationId?: string,\n inputClientId?: string,\n inputCodecMessageId?: string,\n ): Promise<void> {\n this._logger?.trace('DefaultRunManager.suspendRun();', { runId });\n await this._publishTerminal(EVENT_RUN_SUSPEND, runId, { invocationId, inputClientId, inputCodecMessageId });\n this._logger?.debug('DefaultRunManager.suspendRun(); run suspended', { runId });\n }\n\n async endRun(\n runId: string,\n reason: RunEndReason,\n invocationId?: string,\n inputClientId?: string,\n inputCodecMessageId?: string,\n error?: Ably.ErrorInfo,\n ): Promise<void> {\n this._logger?.trace('DefaultRunManager.endRun();', { runId, reason });\n // Stamp error detail only for a terminal error the agent chose to surface\n // (AIT-ST6b4: explicit, never automatic). error-code / error-message are\n // generic transport headers, so any codec or consumer can read them.\n const errorAttribution = reason === 'error' && error ? { errorCode: error.code, errorMessage: error.message } : {};\n await this._publishTerminal(EVENT_RUN_END, runId, {\n reason,\n invocationId,\n inputClientId,\n inputCodecMessageId,\n ...errorAttribution,\n });\n this._logger?.debug('DefaultRunManager.endRun(); run ended', { runId, reason });\n }\n\n /**\n * Publish a run's terminal lifecycle event (run-suspend or run-end) and drop\n * its active-run entry. Both events are the suspending/ending invocation's\n * terminal signal, carrying the same per-invocation correlation; they differ\n * only by event name and the run-reason header (run-end). Publishes BEFORE\n * dropping local state so a publish failure leaves the run in the active set.\n * @param eventName - The lifecycle event to publish (run-suspend or run-end).\n * @param runId - The run being suspended or ended.\n * @param attribution - Per-invocation correlation and the terminal reason.\n * @param attribution.reason - Terminal reason; set for run-end, omitted for run-suspend.\n * @param attribution.invocationId - The invocation's id.\n * @param attribution.inputClientId - ClientId of the triggering input event.\n * @param attribution.inputCodecMessageId - Codec-message-id of the triggering input event.\n * @param attribution.errorCode - Numeric error code; set for run-end only when a terminal error is surfaced.\n * @param attribution.errorMessage - Error message; paired with errorCode.\n */\n private async _publishTerminal(\n eventName: string,\n runId: string,\n attribution: {\n reason?: RunEndReason;\n invocationId?: string;\n inputClientId?: string;\n inputCodecMessageId?: string;\n errorCode?: number;\n errorMessage?: string;\n },\n ): Promise<void> {\n const resolvedClientId = this._activeRuns.get(runId)?.clientId ?? '';\n const headers = buildLifecycleHeaders({ runId, runClientId: resolvedClientId, ...attribution });\n await this._channel.publish({ name: eventName, extras: { ai: { transport: headers } } });\n this._activeRuns.delete(runId);\n }\n\n getClientId(runId: string): string | undefined {\n return this._activeRuns.get(runId)?.clientId;\n }\n\n close(): void {\n this._logger?.trace('DefaultRunManager.close();', { activeRuns: this._activeRuns.size });\n for (const state of this._activeRuns.values()) {\n state.controller.abort();\n }\n this._activeRuns.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a run manager bound to the given channel.\n * @param channel - The Ably channel to publish lifecycle events on.\n * @param logger - Optional logger for diagnostic output.\n * @returns A new {@link RunManager} instance.\n */\nexport const createRunManager = (channel: Ably.RealtimeChannel, logger?: Logger): RunManager =>\n new DefaultRunManager(channel, logger);\n","/**\n * Shared lifecycle plumbing for the client and agent sessions.\n *\n * Both `DefaultClientSession` and `DefaultAgentSession` gate their writes on\n * `connect()` having run, detach their channel best-effort on close, and react\n * to channel continuity loss with the same detection rule and error shape.\n * These helpers own that common machinery so the two sessions cannot drift on\n * the connection guard, the detach-swallow behaviour, or — most importantly —\n * the continuity-loss predicate, which encodes channel protocol semantics\n * (Spec AIT-CT19 / AIT-ST12). Each session keeps its own divergent reaction to\n * continuity loss (the client emits; the agent aborts runs and swaps its Tree).\n */\n\nimport * as Ably from 'ably';\n\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\n\n/**\n * Resolve a session's connect guard: return the in-flight/settled connect\n * promise, or reject with `InvalidArgument` when `connect()` has not been\n * called. Callers `await` the result before any write.\n * @param connectPromise - The session's connect promise, or `undefined` when not yet connected.\n * @param method - The method name being guarded, for the error message.\n * @returns The connect promise.\n * @throws {Ably.ErrorInfo} `InvalidArgument` when `connectPromise` is `undefined`.\n */\nexport const requireConnected = async (connectPromise: Promise<void> | undefined, method: string): Promise<void> => {\n if (!connectPromise) {\n throw new Ably.ErrorInfo(\n `unable to ${method}; connect() must be called before ${method}()`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n return connectPromise;\n};\n\n/**\n * Detach the session's channel on close, best-effort. `connect()` subscribes\n * (which implicitly attaches), so a detach is only attempted when `connect()`\n * ran. A detach failure (e.g. the channel is already FAILED) must not throw out\n * of `close()`, so it is swallowed and logged at debug.\n * @param channel - The session's channel.\n * @param connectPromise - The session's connect promise; detach is skipped when `undefined`.\n * @param logger - Logger for the swallowed-failure debug line, or `undefined`.\n * @param component - The owning class name, used as the log message prefix.\n */\nexport const bestEffortDetach = async (\n channel: Ably.RealtimeChannel,\n connectPromise: Promise<void> | undefined,\n logger: Logger | undefined,\n component: string,\n): Promise<void> => {\n if (connectPromise === undefined) return;\n try {\n await channel.detach();\n } catch (error) {\n logger?.debug(`${component}.close(); channel detach failed`, { error });\n }\n};\n\n/**\n * Whether a channel state change breaks message continuity:\n * - FAILED, SUSPENDED, DETACHED — no more messages expected (or a gap)\n * - ATTACHED with `resumed: false` (an UPDATE) — messages were lost\n *\n * The initial attach (ATTACHED with no prior attach) is the caller's concern\n * and is not handled here.\n * @param stateChange - The channel state change to classify.\n * @returns True when continuity was lost.\n */\nexport const isContinuityLost = (stateChange: Ably.ChannelStateChange): boolean => {\n const { current, resumed } = stateChange;\n return (\n current === 'failed' || current === 'suspended' || current === 'detached' || (current === 'attached' && !resumed)\n );\n};\n\n/**\n * Build the `ChannelContinuityLost` error for a continuity-breaking state\n * change, attaching the state change's `reason` as `cause`.\n * @param stateChange - The continuity-breaking state change.\n * @param verb - The operation that can no longer proceed, for the\n * `unable to <verb>; ...` message (e.g. \"deliver events\", \"continue\").\n * @returns The continuity-loss error.\n */\nexport const continuityLostError = (stateChange: Ably.ChannelStateChange, verb: string): Ably.ErrorInfo => {\n const { current } = stateChange;\n return new Ably.ErrorInfo(\n `unable to ${verb}; channel continuity lost (${current}${current === 'attached' ? ', resumed: false' : ''})`,\n ErrorCode.ChannelContinuityLost,\n 500,\n stateChange.reason,\n );\n};\n","/**\n * `toCodecEvents` — tag a decoded message's events with their wire direction.\n *\n * A decoded message is already split into inputs and outputs by the decoder\n * (driven by the Ably message name — the authoritative direction signal). This\n * helper folds that split into the ordered {@link CodecEvent} stream the reducer\n * consumes, so the direction is carried explicitly rather than re-inferred from\n * each event's shape. Inputs are tagged before outputs, preserving the wire\n * order within a single message (a message is single-direction, so the relative\n * order of the two groups is immaterial).\n */\n\nimport type { CodecEvent, CodecInputEvent, CodecOutputEvent, DecodedMessage } from './types.js';\n\n/**\n * Tag a decoded message's events with their wire direction.\n * @template TInput - The codec's input union.\n * @template TOutput - The codec's output union.\n * @param decoded - The decoder's input/output split for one inbound message.\n * @returns The events as a direction-tagged {@link CodecEvent} list, inputs first.\n */\nexport const toCodecEvents = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>(\n decoded: DecodedMessage<TInput, TOutput>,\n): CodecEvent<TInput, TOutput>[] => [\n ...decoded.inputs.map((event): CodecEvent<TInput, TOutput> => ({ direction: 'input', event })),\n ...decoded.outputs.map((event): CodecEvent<TInput, TOutput> => ({ direction: 'output', event })),\n];\n","/**\n * Per-node event log.\n *\n * Each node retains the decoded events it was folded from, grouped by\n * wire-message serial and ordered ascending by serial. The log captures\n * canonical serial order regardless of delivery order, so a node's event\n * sequence can be re-derived in that order even when wires arrive late\n * (cross-publisher reordering) or out of order (history pages applying older\n * messages after newer ones).\n *\n * Within one serial, deliveries are sequenced by `Message.version.serial`\n * (lexicographically ordered per mutation — platform guarantee): each entry\n * records the highest version decoded into it, so a delivery the entry has\n * already incorporated — a whole-wire replay from a second hydration, a\n * remount, or an agent re-walk — is recognised and dropped at the transport.\n *\n * {@link WireLog} encapsulates the entry list and all of its mutation: the\n * caller hands it a wire and is told only how to fold (see {@link WireLogFold}).\n */\n\n/** One wire message in a node's event log: a serial and its decoded events. */\ninterface WireLogEntry<TEvent> {\n /** Ably channel serial of the wire message. */\n serial: string;\n /**\n * The wire's codec-message-id — the reducer routing key the events were\n * folded alongside; undefined when the wire carried none.\n */\n messageId: string | undefined;\n /**\n * The decoded events from this wire message's deliveries, in arrival order.\n * Same-serial deliveries (the create plus each append/update) extend the\n * entry, so the list accumulates across deliveries.\n */\n events: TEvent[];\n /**\n * The highest `Message.version.serial` decoded into this entry. Versions\n * are lexicographically comparable within one serial, so a delivery at or\n * below this value is already incorporated and must not fold again. In\n * practice every delivery carries a version (a never-mutated message's\n * version serial equals its serial); the message serial is used as the floor\n * only as a defensive fallback for the type-optional absent case.\n */\n decodedThrough: string;\n}\n\n/** How a {@link WireLog.record} call tells the caller to fold the wire's events. */\nexport type WireLogFold =\n /**\n * The version guard rejected a re-delivery the log already incorporated — a\n * whole-wire replay, or a newer version of a non-streamed wire (an edited\n * discrete). Nothing was recorded; fold nothing.\n */\n | 'dropped'\n /**\n * The events extend the log tail (in-order delivery) or landed on a swept\n * log; fold them onto the node's existing projection.\n */\n | 'incremental'\n /**\n * An earlier-serial wire arrived late, so incremental folding would corrupt\n * serial order; rebuild the projection from the whole log via {@link replay}.\n */\n | 'refold';\n\n/**\n * A node's event log: one entry per wire-message serial, kept ascending by\n * serial, each accumulating that serial's decoded events in arrival order.\n */\nexport class WireLog<TEvent> {\n private readonly _entries: WireLogEntry<TEvent>[] = [];\n private _swept = false;\n\n /**\n * Whether the retention sweep has dropped this log's decoded payloads. A\n * swept log keeps each entry's replay key (serial + `decodedThrough`) so it\n * still recognises whole-wire replays, but it can no longer be refolded.\n * @returns True once {@link sweep} has run.\n */\n get swept(): boolean {\n return this._swept;\n }\n\n /**\n * Record a wire message's decoded events and report how to fold them.\n *\n * Events for an already-logged serial are guarded by the entry's\n * `decodedThrough` version before being recorded; a new serial is inserted\n * at the position that keeps the log ascending by serial (Ably serials order\n * lexicographically). The version guard fires only for deliveries carrying\n * an explicit `version.serial`: in-contract mutations always do, while a\n * version-less delivery records unguarded (and never advances\n * `decodedThrough`), matching the decoder's convention.\n *\n * On a swept log the payload is not retained (only the replay key is), so\n * the fold is never `refold` — a genuinely-new wire there is outside the\n * reorder window and folds incrementally in arrival order.\n * @param serial - The Ably channel serial of the wire message.\n * @param messageId - The wire's codec-message-id, or undefined.\n * @param events - The decoded events to record, in arrival order.\n * @param version - The delivery's `Message.version.serial`, or undefined\n * when the delivery carried none (guard disabled for this delivery).\n * @param streamed - Whether the delivery is part of a streamed wire; a\n * guarded newer delivery for a non-streamed wire is an edited discrete and\n * is dropped.\n * @returns How the caller should fold the events.\n */\n record(\n serial: string,\n messageId: string | undefined,\n events: TEvent[],\n version: string | undefined,\n streamed: boolean,\n ): WireLogFold {\n // A swept log retains replay keys but not payloads: record an empty event\n // list so the key advances while nothing is stored. The caller folds the\n // events it already holds.\n const index = this._recordEntry(serial, messageId, this._swept ? [] : events, version, streamed);\n if (index === undefined) return 'dropped';\n if (this._swept) return 'incremental';\n return index === this._entries.length - 1 ? 'incremental' : 'refold';\n }\n\n /**\n * Replay every recorded event in canonical order — wire messages ascending\n * by serial, events within a wire in arrival order — each with its wire's\n * routing metadata, for a refold.\n * @param visit - Called once per event, in canonical order.\n */\n replay(visit: (event: TEvent, serial: string, messageId: string | undefined) => void): void {\n for (const entry of this._entries) {\n for (const event of entry.events) visit(event, entry.serial, entry.messageId);\n }\n }\n\n /**\n * Drop the decoded payloads (the unbounded cost) but keep each entry's\n * replay key, so a post-sweep whole-wire replay is still recognised and\n * dropped rather than re-folded. The log becomes {@link swept}; a refold can\n * no longer rebuild the dropped events, which `swept` reflects.\n */\n sweep(): void {\n this._swept = true;\n for (const entry of this._entries) entry.events.length = 0;\n }\n\n /**\n * Insert or extend the entry for `serial`, guarding replays by version.\n * @param serial - The Ably channel serial of the wire message.\n * @param messageId - The wire's codec-message-id, or undefined.\n * @param events - The decoded events to store (empty on a swept log).\n * @param version - The delivery's `Message.version.serial`, or undefined.\n * @param streamed - Whether the delivery is part of a streamed wire.\n * @returns The index of the entry the events landed in, or `undefined` when\n * the version guard dropped the delivery.\n */\n private _recordEntry(\n serial: string,\n messageId: string | undefined,\n events: TEvent[],\n version: string | undefined,\n streamed: boolean,\n ): number | undefined {\n // Scan from the tail: live delivery appends at (or extends) the end, so the\n // match is almost always within the last entry or two.\n for (let i = this._entries.length - 1; i >= 0; i--) {\n const entry = this._entries[i];\n if (!entry) break; // unreachable\n if (entry.serial === serial) {\n // Version guard: drop a re-delivery the entry already incorporated — a\n // replay (version at or below the high-water-mark) or an edit to a\n // discrete (a newer version of a non-streamed wire, not propagated).\n if (version !== undefined && (version <= entry.decodedThrough || !streamed)) {\n return undefined;\n }\n entry.events.push(...events);\n if (version !== undefined) entry.decodedThrough = version;\n return i;\n }\n if (entry.serial < serial) {\n this._entries.splice(i + 1, 0, { serial, messageId, events: [...events], decodedThrough: version ?? serial });\n return i + 1;\n }\n }\n // Lower than every logged serial (or the log is empty): insert at the head.\n this._entries.unshift({ serial, messageId, events: [...events], decodedThrough: version ?? serial });\n return 0;\n }\n}\n","/**\n * Tree — materializes a branching conversation as a forest of nodes. Each turn\n * is two nodes: a user {@link InputNode} keyed by its client-owned\n * codec-message-id and an agent {@link RunNode} keyed by the agent-minted\n * run-id, parented to the input node.\n *\n * Each node holds a per-node codec {@link TProjection} which the Tree folds\n * from inbound events. The Tree owns the complete conversation state across\n * every observed node. The {@link View} walks the parent chain to extract a\n * flat message list for rendering.\n *\n * `applyMessage()` is the entry point for inbound channel messages — it\n * classifies a run-less user input into an input node (keyed by\n * codec-message-id) or routes a run-bearing wire to its reply run (keyed by\n * run-id), folds events into that node's projection, and maintains a secondary\n * `codecMessageId -> nodeKey` index. `applyRunLifecycle()` handles run-start /\n * run-suspend / run-resume / run-end events.\n *\n * Sibling structure: editing a prompt produces a sibling input node linked by\n * {@link InputNode.forkOf}; regenerating a reply produces a sibling reply run\n * sharing the same input-node parent (no fork-of).\n */\n\nimport type * as Ably from 'ably';\n\nimport {\n HEADER_CODEC_MESSAGE_ID,\n HEADER_EVENT_ID,\n HEADER_FORK_OF,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_INVOCATION_ID,\n HEADER_MSG_REGENERATE,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_RUN_CLIENT_ID,\n HEADER_RUN_ID,\n HEADER_STREAM,\n} from '../../constants.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport type { Logger } from '../../logger.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport { toCodecEvents } from '../codec/codec-event.js';\nimport type { CodecEvent, CodecInputEvent, CodecOutputEvent, Reducer } from '../codec/types.js';\nimport type { ConversationNode, InputNode, OutputEvent, RunLifecycleEvent, RunNode, Tree } from './types.js';\nimport { WireLog } from './wire-log.js';\n\n// ---------------------------------------------------------------------------\n// Internal node type\n// ---------------------------------------------------------------------------\n\n/**\n * How long (in ms, on the Ably message-timestamp timeline) a structurally\n * complete run's event log is retained after the node's last observed\n * activity. Bounds cross-publisher live delivery reorder: a wire can be\n * delivered after a higher-serial wire by at most this window. Conservative\n * placeholder pending confirmation of the actual cross-region bound.\n */\nexport const REORDER_WINDOW_MS = 120_000;\n\ninterface InternalNode<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection> {\n node: ConversationNode<TProjection>;\n /** Insertion sequence — tiebreaker for nodes with no sort serial (optimistic). */\n insertSeq: number;\n /**\n * The node's event log: every serial-bearing wire applied to this node, in\n * canonical serial order. Owns its own record/refold/replay-guard/sweep\n * mutation (see {@link WireLog}). Optimistic (serial-less) applies are not\n * recorded.\n */\n log: WireLog<CodecEvent<TInput, TOutput>>;\n /**\n * Max Ably message timestamp (epoch ms) of everything applied to this node,\n * including its run lifecycle events; 0 until a timestamped apply. The\n * retention sweep measures {@link REORDER_WINDOW_MS} from here.\n */\n lastActivityTs: number;\n /**\n * Whether this run's `ai-run-start` has been observed (run nodes only —\n * always false for input nodes). The structural half of log retention:\n * run-start is the run's serial floor, so once it is observed no older\n * history page can deliver further wires for this node.\n */\n runStartSeen: boolean;\n /** Whether this node is already queued for sweeping (guards double-enqueue). */\n sweepQueued: boolean;\n /**\n * Whether an optimistic (serial-less) seed has been folded into the\n * projection but not into the log. The first serial-bearing wire (the echo)\n * refolds the node from the log alone, discarding the seed, then clears\n * this — so a codec needs no seed-replacement logic of its own.\n */\n optimistic: boolean;\n}\n\n/**\n * The primary key a node is indexed under: a reply run's `runId`, or an input\n * node's `codecMessageId` (the client owns it before the agent mints a runId).\n * @param node - The node to key.\n * @returns The node's primary key.\n */\nexport const nodeKey = <TProjection>(node: ConversationNode<TProjection>): string =>\n node.kind === 'run' ? node.runId : node.codecMessageId;\n\n/**\n * The serial a node sorts by: a reply run's `startSerial`, an input node's\n * `serial`. Undefined for an optimistic (not-yet-acked) node, which tail-sorts.\n * @param node - The node to read.\n * @returns The sort serial, or undefined for an optimistic node.\n */\nconst sortSerial = <TProjection>(node: ConversationNode<TProjection>): string | undefined =>\n node.kind === 'run' ? node.startSerial : node.serial;\n\n/**\n * Add a value to a `Map<K, Set<V>>`, creating the bucket Set on first use.\n * @param map - The Map to mutate.\n * @param key - The bucket key.\n * @param value - The value to add.\n */\nconst addToSetMap = <K, V>(map: Map<K, Set<V>>, key: K, value: V): void => {\n let set = map.get(key);\n if (!set) {\n set = new Set();\n map.set(key, set);\n }\n set.add(value);\n};\n\n/**\n * Remove a value from a `Map<K, Set<V>>`, dropping the bucket when it empties.\n * @param map - The Map to mutate.\n * @param key - The bucket key.\n * @param value - The value to remove.\n */\nconst deleteFromSetMap = <K, V>(map: Map<K, Set<V>>, key: K, value: V): void => {\n const set = map.get(key);\n if (!set) return;\n set.delete(value);\n if (set.size === 0) map.delete(key);\n};\n\n// ---------------------------------------------------------------------------\n// Internal interface — extended surface consumed by View / ClientSession\n// ---------------------------------------------------------------------------\n\n/** Internal tree surface used by View and ClientSession — not part of the public Tree API. */\nexport interface TreeInternal<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n> extends Tree<TOutput, TProjection> {\n /**\n * Walk the visible node chain (both input nodes and reply runs) along the\n * selected branches, in chronological order. The View renders from this.\n * @param selections - Per-group selected member key, keyed by group root.\n * @returns The visible nodes in chronological order.\n */\n visibleNodes(selections?: Map<string, string>): ConversationNode<TProjection>[];\n\n /**\n * Get the \"group root\" key for a sibling group — the stable key the\n * selection map is keyed by (the earliest edit version for input nodes, the\n * original reply for a regenerate group).\n */\n getGroupRoot(key: string): string;\n\n /**\n * The reply runs parented at an input node (its codec-message-id), in\n * iteration order. Empty when none have been observed. Used to resolve a\n * user prompt to its reply run(s).\n * @param inputCodecMessageId - The input node's codec-message-id.\n * @returns The reply runs parented at that input.\n */\n getReplyRuns(inputCodecMessageId: string): RunNode<TProjection>[];\n\n /**\n * Apply an inbound channel message to the tree.\n *\n * Classifies the message and routes it to the owning node:\n * 1. Run-less user input (no run-id, a `user`-role message carrying a\n * codec-message-id and input events): creates or promotes the input node\n * keyed by that codec-message-id, folds the input events.\n * 2. Run-bearing wire (assistant output, continuation tool-resolution, or a\n * fresh agent-minted run): routes to the reply run by run-id (reconciling\n * an optimistic insert by codec-message-id), folds events.\n * @param events - Decoded codec events, split by wire direction. Both are\n * folded into the node's projection, inputs first.\n * @param events.inputs - Client-published events (`ai-input` wire).\n * @param events.outputs - Agent-published events (`ai-output` wire).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for optimistic inserts.\n * @param timestamp - Ably server timestamp (epoch ms) of the message —\n * top-level `Message.timestamp`, the message's create time on every\n * delivery (an append's own receive time lives in `version.timestamp`) —\n * or undefined for optimistic inserts. Advances the Tree's event-log\n * retention clock and the owning node's last-activity time.\n * @param version - The delivery's `Message.version.serial`, or undefined\n * when the delivery carried none (optimistic inserts, never-mutated\n * deliveries from sources that omit it). Guards the node's event log\n * against whole-wire replays: a delivery at or below the version already\n * decoded into its log entry is dropped.\n */\n applyMessage(\n events: { inputs: TInput[]; outputs: TOutput[] },\n headers: Record<string, string>,\n serial?: string,\n timestamp?: number,\n version?: string,\n ): void;\n\n /**\n * Apply a run-lifecycle event.\n *\n * - `start`: creates the reply run (if missing) or, for an existing run,\n * sets RunNode.state to 'active', promotes startSerial, and backfills\n * structural metadata (parent / forkOf / regenerates / invocationId).\n * - `suspend`: sets RunNode.state to 'suspended' and records `endSerial`.\n * The run stays live so a resume under the same `runId` picks up where it\n * left off.\n * - `resume`: re-activates an existing suspended Run (state back to\n * 'active') without touching its structure or serials — a pure re-entry\n * signal. A no-op if the Run is not yet known.\n * - `end`: sets RunNode.state to the terminal reason and records\n * `endSerial`.\n *\n * Always emits a 'run' event to subscribers.\n * @param event - Lifecycle event payload, including the channel serial.\n */\n applyRunLifecycle(event: RunLifecycleEvent): void;\n\n /**\n * Get the node keyed by `key`, or undefined if `key` names no node. The\n * key is a {@link nodeKey} — a runId (reply run) or an input node's\n * codec-message-id — so the result is a {@link ConversationNode} union:\n * narrow on `kind` before reading kind-specific fields. Pairs with\n * {@link getNodeByCodecMessageId}, which resolves an arbitrary owned\n * codec-message-id (including an assistant message's) to its node.\n * @param key - The node key to look up.\n * @returns The node, or undefined if not found.\n */\n getNode(key: string): ConversationNode<TProjection> | undefined;\n\n /**\n * Remove a node from the tree by its key ({@link nodeKey} — a runId or an\n * input node's codec-message-id). Children become unreachable because their\n * parent is no longer on the active path.\n * @param key - The node key to remove.\n */\n delete(key: string): void;\n\n /** Forward a raw Ably message event to tree subscribers. */\n emitAblyMessage(msg: Ably.InboundMessage): void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/** EventEmitter events map for the tree. */\ninterface TreeEventsMap<TOutput extends CodecOutputEvent> {\n update: undefined;\n 'ably-message': Ably.InboundMessage;\n run: RunLifecycleEvent;\n output: OutputEvent<TOutput>;\n}\n\n// Spec: AIT-CT13\nexport class DefaultTree<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n> implements TreeInternal<TInput, TOutput, TProjection> {\n private readonly _codec: Reducer<CodecEvent<TInput, TOutput>, TProjection>;\n private readonly _logger: Logger;\n private readonly _emitter: EventEmitter<TreeEventsMap<TOutput>>;\n\n /**\n * All nodes indexed by their primary key ({@link nodeKey}): a reply run's\n * runId, or an input node's codec-message-id.\n */\n private readonly _nodeIndex = new Map<string, InternalNode<TInput, TOutput, TProjection>>();\n\n /**\n * Maps every observed `codec-message-id` to its owning node's key\n * ({@link nodeKey}). For a reply run that is the runId of every message the\n * run published; for an input node it is the input's own codec-message-id.\n * Resolves fork-of / parent codec-message-ids to node keys, routes\n * continuation amend wires to existing nodes, and backs UI lookups that hold\n * a codec-message-id.\n */\n private readonly _codecMessageIdToNodeKey = new Map<string, string>();\n\n /**\n * All nodes sorted by their sort serial ({@link sortSerial}: `startSerial`\n * for runs, `serial` for input nodes), lexicographically. Nodes with no sort\n * serial (optimistic) sort after all serial-bearing nodes, ordered among\n * themselves by insertion sequence.\n */\n private readonly _sortedNodes: InternalNode<TInput, TOutput, TProjection>[] = [];\n\n /**\n * Parent index: parent node key (the key its children's\n * `parentCodecMessageId` resolves to) to the set of child node keys. Root\n * nodes (no parent) are indexed under the key `undefined`. Kind-blind — a\n * reply run and an input node parent off each other through the same index.\n */\n private readonly _parentIndex = new Map<string | undefined, Set<string>>();\n\n /**\n * Reverse edge: an input node's codec-message-id to the set of reply-run ids\n * parented at it. Lets the View resolve a user prompt to its (selected) reply\n * run, and groups regenerate siblings (which all parent at the same input\n * node).\n */\n private readonly _replyRunsByInput = new Map<string, Set<string>>();\n\n /** Monotonically increasing counter for insertion sequence. */\n private _seqCounter = 0;\n\n /** Incremented on structural changes; unchanged on projection-only updates. */\n private _structuralVersion = 0;\n\n /**\n * Cached sibling-group lookups keyed by node key. The walk over forkOf\n * chains and the per-parent fan-out are pure functions of the node\n * graph, so the cache is keyed against {@link _structuralVersion}:\n * any topology mutation drops the cache and the next lookup\n * recomputes. Hits matter most during a single render pass where\n * the View calls `getSiblingNodes` once per visible node plus extra\n * per-message branch-anchor probes from React components.\n */\n private _siblingCache = new Map<string, InternalNode<TInput, TOutput, TProjection>[]>();\n private _siblingCacheVersion = -1;\n\n /**\n * Index from `event-id` header to the raw Ably message that carried it.\n * Populated incrementally as messages arrive via {@link emitAblyMessage};\n * reads back the raw message for the agent's input-event lookup\n * ({@link findAblyMessageByEventId}). Bounded by the Tree's lifetime — cleared\n * when the Tree is replaced on continuity loss / session close.\n */\n private readonly _eventIdIndex = new Map<string, Ably.InboundMessage>();\n\n /**\n * Event-log retention logical clock: the max Ably message timestamp (epoch\n * ms) observed across every apply, 0 until the first timestamped one. Only\n * ever advances — older-page history application carries smaller timestamps\n * and leaves it (and therefore the sweep) untouched.\n */\n private _clock = 0;\n\n /**\n * Keys of structurally complete run nodes (run-start and run-end both\n * observed) whose event logs await the retention window, in completion\n * order. Drained from the front whenever {@link _clock} advances; sweeping\n * only at clock advances keeps a history page's batch atomic — applying an\n * older page can never advance the clock, so a node cannot be swept between\n * its run-start and the rest of its wires in the same page.\n */\n private readonly _sweepQueue: string[] = [];\n\n constructor(codec: Reducer<CodecEvent<TInput, TOutput>, TProjection>, logger: Logger) {\n this._codec = codec;\n this._logger = logger;\n this._emitter = new EventEmitter<TreeEventsMap<TOutput>>(logger);\n }\n\n // -------------------------------------------------------------------------\n // Sorted list maintenance\n // -------------------------------------------------------------------------\n\n /**\n * Compare two nodes (Run or input) for sorted list ordering.\n * Serial-bearing nodes sort by their sort serial (`startSerial` for runs,\n * `serial` for input nodes), lexicographically.\n * Nodes with no sort serial sort after all serial-bearing nodes.\n * Among them, sort by insertion sequence.\n *\n * Optimistic (null-serial) nodes intentionally tail-sort so they reorder\n * into place when the server relay arrives and `applyMessage` promotes\n * startSerial — see {@link applyMessage}'s `_removeSortedNode` /\n * `_insertSortedNode` pair on the promotion path.\n * @param a - First node to compare.\n * @param b - Second node to compare.\n * @returns Negative if a sorts before b, positive if after, zero if equal.\n */\n // Spec: AIT-CT13a\n private _compareNodes(\n a: InternalNode<TInput, TOutput, TProjection>,\n b: InternalNode<TInput, TOutput, TProjection>,\n ): number {\n const sa = sortSerial(a.node);\n const sb = sortSerial(b.node);\n if (sa === undefined && sb === undefined) return a.insertSeq - b.insertSeq;\n if (sa === undefined) return 1;\n if (sb === undefined) return -1;\n if (sa < sb) return -1;\n if (sa > sb) return 1;\n return a.insertSeq - b.insertSeq;\n }\n\n /**\n * Insert a node into the sorted list at the correct position via binary search.\n * @param internal - The node to insert.\n */\n private _insertSortedNode(internal: InternalNode<TInput, TOutput, TProjection>): void {\n const startSerial = sortSerial(internal.node);\n\n // Fast path: null-startSerial always appends to end.\n if (startSerial === undefined) {\n this._sortedNodes.push(internal);\n return;\n }\n\n let lo = 0;\n let hi = this._sortedNodes.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const midNode = this._sortedNodes[mid];\n if (!midNode) break; // unreachable\n if (this._compareNodes(midNode, internal) <= 0) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n this._sortedNodes.splice(lo, 0, internal);\n }\n\n /**\n * Remove a node from the sorted list.\n * @param internal - The node to remove.\n */\n private _removeSortedNode(internal: InternalNode<TInput, TOutput, TProjection>): void {\n const idx = this._sortedNodes.indexOf(internal);\n if (idx !== -1) this._sortedNodes.splice(idx, 1);\n }\n\n /**\n * Insert a freshly-created node into the primary store, the parent index, and\n * the sorted list, then bump the structural version. Kind-specific secondary\n * indexing — the codec-message-id map for input nodes, the reply→input edge\n * for reply runs — is the caller's responsibility.\n * @param key - The node's primary key ({@link nodeKey}).\n * @param entry - The internal node to insert.\n * @param parentCodecMessageId - The node's structural parent, or undefined for a root.\n */\n private _insertNode(\n key: string,\n entry: InternalNode<TInput, TOutput, TProjection>,\n parentCodecMessageId: string | undefined,\n ): void {\n this._nodeIndex.set(key, entry);\n this._addToParentIndex(parentCodecMessageId, key);\n this._insertSortedNode(entry);\n this._structuralVersion++;\n }\n\n /**\n * Re-sort a node whose sort key just changed and bump the structural version.\n * The caller mutates the serial field (`serial` for input nodes, `startSerial`\n * for runs); this keeps the sorted list and version in step. Used on the\n * optimistic-serial promotion paths when the server relay/echo arrives.\n * @param entry - The internal node whose serial was just promoted.\n */\n private _promoteSerial(entry: InternalNode<TInput, TOutput, TProjection>): void {\n this._removeSortedNode(entry);\n this._insertSortedNode(entry);\n this._structuralVersion++;\n }\n\n /**\n * Fold a batch of events into a node's projection in place, isolating each\n * fold in a try/catch so a throwing reducer can't abort the rest of the batch\n * or the surrounding apply.\n * @param entry - The internal node whose projection is folded in place.\n * @param events - The decoded events to fold, in wire order.\n * @param serial - Ably channel serial; coerced to '' for an optimistic insert.\n * @param messageId - The reducer routing key (codec-message-id), or undefined.\n */\n private _foldInto(\n entry: InternalNode<TInput, TOutput, TProjection>,\n events: CodecEvent<TInput, TOutput>[],\n serial: string | undefined,\n messageId: string | undefined,\n ): void {\n for (const event of events) {\n try {\n entry.node.projection = this._codec.fold(entry.node.projection, event, { serial: serial ?? '', messageId });\n } catch (error) {\n this._logger.error('Tree._foldInto(); fold threw', { key: nodeKey(entry.node), messageId, err: error });\n }\n }\n }\n\n /**\n * Record a serial-bearing wire in the node's event log and fold it. Events\n * extending the log tail (the common case — in-order live delivery) fold\n * incrementally onto the existing projection, identical to a bare\n * {@link _foldInto}. Events that land earlier in the log (an earlier-serial\n * wire delivered late — cross-publisher reorder, or a history page applying\n * an older message after a newer one) cannot be folded incrementally without\n * corrupting serial order, so the node is refolded from the whole log via\n * {@link _refold}.\n *\n * Optimistic (serial-less) applies and empty event batches are not logged;\n * an optimistic seed folds into the projection but never into the log, and\n * marks the node `optimistic`. The first serial-bearing wire (the echo of\n * the optimistic input, which re-delivers the seeded content) refolds the\n * node from the log alone — rebuilding the projection without the seed\n * rather than folding the echo on top of it. The codec therefore never sees\n * the seed and its echo in one projection, and needs no seed-replacement\n * logic. The seed must be a faithful preview of the echo, since the echo's\n * content is what survives.\n *\n * Whole-wire replays are dropped at the log: each entry records the highest\n * `Message.version.serial` decoded into it (`decodedThrough`), so a\n * version-bearing delivery the entry has already incorporated — a second\n * hydration over a populated Tree, a remounted View's re-fetch, an agent\n * re-walk — records nothing and folds nothing. A newer version of a\n * discrete wire (an edited discrete) is likewise dropped; propagating edits\n * into projections is deliberately out of scope.\n * @param entry - The internal node whose log and projection are updated.\n * @param events - The decoded events to fold, in wire order.\n * @param serial - Ably channel serial; undefined for an optimistic insert.\n * @param messageId - The reducer routing key (codec-message-id), or undefined.\n * @param version - The delivery's `Message.version.serial`, or undefined.\n * @param streamed - Whether the delivery is part of a streamed wire.\n */\n private _recordAndFold(\n entry: InternalNode<TInput, TOutput, TProjection>,\n events: CodecEvent<TInput, TOutput>[],\n serial: string | undefined,\n messageId: string | undefined,\n version: string | undefined,\n streamed: boolean,\n ): void {\n // A serial-less optimistic seed (or an empty batch) is not logged. Fold it\n // in; a non-empty seed marks the node so its echo refolds the seed away.\n if (serial === undefined || events.length === 0) {\n if (serial === undefined && events.length > 0) entry.optimistic = true;\n this._foldInto(entry, events, serial, messageId);\n return;\n }\n\n const fold = entry.log.record(serial, messageId, events, version, streamed);\n if (fold === 'dropped') {\n // The version guard rejected a re-delivery the log already incorporated —\n // a whole-wire replay (second hydration, remount, agent re-walk, or a\n // `loadOlder()` re-applying a swept run's history) or an edit to a\n // discrete. Nothing to fold.\n this._logger.debug('Tree._recordAndFold(); version guard dropped re-delivered wire', {\n key: nodeKey(entry.node),\n serial,\n version,\n swept: entry.log.swept,\n });\n return;\n }\n if (entry.optimistic && !entry.log.swept) {\n // First serial-bearing wire (the echo) on a node that carries an\n // optimistic seed. The seed is in the projection but not the log, so\n // refold from the log alone — the echo re-delivers the seeded content —\n // rebuilding the projection without the seed instead of folding the echo\n // on top of it.\n entry.optimistic = false;\n this._refold(entry);\n return;\n }\n if (fold === 'refold') {\n this._refold(entry);\n return;\n }\n // 'incremental'. On a swept log this is a genuinely-new wire outside the\n // reorder window (it should not occur) folding in arrival order — the log\n // could not refold it.\n if (entry.log.swept) {\n this._logger.warn('Tree._recordAndFold(); late wire after log retention window; folding in arrival order', {\n key: nodeKey(entry.node),\n serial,\n });\n }\n this._foldInto(entry, events, serial, messageId);\n }\n\n /**\n * Rebuild a node's projection from its event log in canonical serial order:\n * a fresh {@link Reducer.init} folded through every logged event, each with\n * its own wire's serial and messageId. Used when a late, earlier-serial wire\n * makes incremental folding unsound. Reducer purity (a fold is a function of\n * its inputs alone) is what makes the rebuild faithful; the per-fold\n * try/catch mirrors {@link _foldInto} so one throwing event can't abort the\n * rebuild.\n *\n * Rebuilds the projection only; the surrounding apply emits its usual\n * `output` event carrying just the triggering wire's events. Consumers read\n * the rebuilt state from `node.projection` (the View recomputes its message\n * list from it), so on the refold path the event's `events` payload is not a\n * delta of the full projection change.\n * @param entry - The internal node whose projection is rebuilt in place.\n */\n private _refold(entry: InternalNode<TInput, TOutput, TProjection>): void {\n let projection = this._codec.init();\n entry.log.replay((event, serial, messageId) => {\n try {\n projection = this._codec.fold(projection, event, { serial, messageId });\n } catch (error) {\n this._logger.error('Tree._refold(); fold threw', { key: nodeKey(entry.node), messageId, err: error });\n }\n });\n entry.node.projection = projection;\n }\n\n // -------------------------------------------------------------------------\n // Event-log retention\n // -------------------------------------------------------------------------\n\n /**\n * Record activity on a node and advance the retention clock. Updates the\n * node's `lastActivityTs` and the Tree-wide `_clock` to the given timestamp\n * when it is newer; a clock advance drains the sweep queue. `undefined`\n * (an optimistic local apply) advances nothing.\n * @param entry - The node the activity belongs to.\n * @param timestamp - Ably message timestamp (epoch ms), or undefined.\n */\n private _recordActivity(entry: InternalNode<TInput, TOutput, TProjection>, timestamp: number | undefined): void {\n if (timestamp === undefined) return;\n if (timestamp > entry.lastActivityTs) entry.lastActivityTs = timestamp;\n if (timestamp > this._clock) {\n this._clock = timestamp;\n this._drainSweepQueue();\n }\n }\n\n /**\n * Queue a run node's event log for retention sweeping once the node is\n * structurally complete: its run-start (serial floor — no older history page\n * can add to it) and its run-end (no further agent output) have both been\n * observed. The actual drop happens in {@link _drainSweepQueue} once the\n * reorder window has also lapsed. No-op for input nodes (never swept — no\n * floor marker, and their logs are bounded by one user message), for nodes\n * already queued or swept, and while either marker is missing.\n * @param entry - The node to consider for sweeping.\n */\n private _maybeQueueSweep(entry: InternalNode<TInput, TOutput, TProjection>): void {\n const node = entry.node;\n if (node.kind !== 'run') return;\n if (entry.log.swept || entry.sweepQueued) return;\n if (!entry.runStartSeen) return;\n if (node.state.status === 'active' || node.state.status === 'suspended') return;\n entry.sweepQueued = true;\n this._sweepQueue.push(node.runId);\n }\n\n /**\n * Drop the event logs of queued nodes whose retention window has lapsed:\n * `lastActivityTs + REORDER_WINDOW_MS < _clock`. Drains from the front and\n * stops at the first node still inside the window — completion order is\n * time-ordered for live traffic, so this is amortised O(1) per apply, and\n * stopping early only ever over-retains (memory, never correctness). Called\n * only when the clock advances, so applying an older history page (smaller\n * timestamps) can never sweep mid-batch. Deleted nodes are skipped.\n */\n private _drainSweepQueue(): void {\n while (this._sweepQueue.length > 0) {\n const key = this._sweepQueue[0];\n const entry = key === undefined ? undefined : this._nodeIndex.get(key);\n if (!entry || entry.log.swept) {\n this._sweepQueue.shift();\n continue;\n }\n if (entry.lastActivityTs + REORDER_WINDOW_MS >= this._clock) return;\n this._sweepQueue.shift();\n entry.sweepQueued = false;\n // Drop the decoded payloads (the unbounded cost) but keep each entry's\n // replay key, so a post-sweep whole-wire replay is still recognised and\n // dropped rather than re-folded (a refold can no longer rebuild them).\n entry.log.sweep();\n this._logger.debug('Tree._drainSweepQueue(); dropped event-log payloads, kept replay keys', {\n key,\n lastActivityTs: entry.lastActivityTs,\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Parent index maintenance\n // -------------------------------------------------------------------------\n\n private _addToParentIndex(parentNodeKey: string | undefined, childKey: string): void {\n addToSetMap(this._parentIndex, parentNodeKey, childKey);\n }\n\n private _removeFromParentIndex(parentNodeKey: string | undefined, childKey: string): void {\n deleteFromSetMap(this._parentIndex, parentNodeKey, childKey);\n }\n\n /**\n * Resolve a node's structural parent to the parent node's key\n * ({@link nodeKey}), or undefined for a root. The parent is named by a\n * codec-message-id (`parentCodecMessageId`); this maps it through the\n * codec-message-id index to the owning node's key (a runId for a reply run,\n * a codec-message-id for an input node). Returns undefined when the parent\n * hasn't been observed yet (the node is treated as a root until it arrives).\n * @param node - The node whose parent to resolve.\n * @returns The parent node's key, or undefined.\n */\n private _parentKeyOf(node: ConversationNode<TProjection>): string | undefined {\n const parentCodecMessageId = node.parentCodecMessageId;\n return parentCodecMessageId === undefined ? undefined : this._codecMessageIdToNodeKey.get(parentCodecMessageId);\n }\n\n // -------------------------------------------------------------------------\n // Sibling grouping\n // -------------------------------------------------------------------------\n\n /**\n * Walk an input node's `forkOf` chain to the group root — the earliest edit\n * version sharing the same structural parent. Stops at a missing target, a\n * non-input target, a parent mismatch, or a cycle.\n * @param node - The input node to walk from.\n * @returns The group-root input node (the node itself when it is the root).\n */\n private _inputGroupRoot(node: InputNode<TProjection>): InputNode<TProjection> {\n let current = node;\n const visited = new Set<string>([nodeKey(current)]);\n while (current.forkOf !== undefined) {\n if (visited.has(current.forkOf)) break;\n const forkTarget = this._nodeIndex.get(current.forkOf);\n if (forkTarget?.node.kind !== 'input' || forkTarget.node.parentCodecMessageId !== current.parentCodecMessageId) {\n break;\n }\n current = forkTarget.node;\n visited.add(nodeKey(current));\n }\n return current;\n }\n\n /**\n * Get the sibling group that the node keyed by `key` belongs to. Kind-split:\n *\n * - **Reply runs** — every reply run sharing the same input-node parent is a\n * sibling (the original reply + its regenerators all parent at the same\n * input node M_user). No fork-of involved.\n * - **Input nodes** — edit versions: nodes sharing a parent AND linked by a\n * `forkOf` chain to the group root.\n *\n * Returned ordered by startSerial (original/oldest first). A group of one is\n * returned as a single-element array (no branching).\n * @param key - The node key ({@link nodeKey}) to look up the group for.\n * @returns The ordered list of sibling nodes.\n */\n // Spec: AIT-CT13b\n private _getSiblingGroup(key: string): InternalNode<TInput, TOutput, TProjection>[] {\n if (this._siblingCacheVersion !== this._structuralVersion) {\n this._siblingCache.clear();\n this._siblingCacheVersion = this._structuralVersion;\n }\n const cached = this._siblingCache.get(key);\n if (cached) return cached;\n\n const entry = this._nodeIndex.get(key);\n if (!entry) return [];\n\n // The \"original\" anchors the group's parent + kind. For an input node,\n // walk the forkOf chain to the earliest version sharing the parent; for a\n // reply run the node itself anchors (all same-parent runs are siblings).\n let original = entry.node;\n if (original.kind === 'input') {\n original = this._inputGroupRoot(original);\n }\n\n // `_parentIndex` is keyed by the raw structural `parentCodecMessageId` (not\n // the resolved parent node key) so a run observed before its input node\n // still files/groups correctly — the parent codec-message-id is known at\n // creation, the resolved key may not be.\n const parentKey = original.parentCodecMessageId;\n const siblings: InternalNode<TInput, TOutput, TProjection>[] = [];\n const candidateKeys = this._parentIndex.get(parentKey);\n if (candidateKeys) {\n for (const childKey of candidateKeys) {\n const childEntry = this._nodeIndex.get(childKey);\n if (childEntry && this._isSiblingOf(childEntry.node, original)) {\n siblings.push(childEntry);\n }\n }\n }\n\n siblings.sort((a, b) => this._compareNodes(a, b));\n // Cache against the queried key AND every member of the group: a single\n // group is the same array regardless of which member triggered the lookup,\n // so subsequent queries against any member hit without recomputing.\n for (const sib of siblings) {\n this._siblingCache.set(nodeKey(sib.node), siblings);\n }\n this._siblingCache.set(key, siblings);\n return siblings;\n }\n\n /**\n * Whether `node` belongs to the sibling group anchored at `original`.\n * Requires the same kind and the same structural parent; reply runs need\n * nothing more (same-parent runs are regenerate siblings), input nodes must\n * additionally be forkOf-linked to the original (edit versions).\n * @param node - The candidate node.\n * @param original - The group's anchor node.\n * @returns True if `node` is a sibling of `original`.\n */\n private _isSiblingOf(node: ConversationNode<TProjection>, original: ConversationNode<TProjection>): boolean {\n if (node.kind !== original.kind) return false;\n if (node.parentCodecMessageId !== original.parentCodecMessageId) return false;\n // Same-parent reply runs are regenerate siblings — no fork-of needed.\n if (node.kind === 'run') return true;\n // Input nodes: must be forkOf-linked to the original (edit versions).\n const originalKey = nodeKey(original);\n if (nodeKey(node) === originalKey) return true;\n let current: ConversationNode<TProjection> = node;\n const visited = new Set<string>([nodeKey(current)]);\n while (current.kind === 'input' && current.forkOf !== undefined) {\n if (current.forkOf === originalKey) return true;\n if (visited.has(current.forkOf)) break;\n const target = this._nodeIndex.get(current.forkOf);\n if (!target) break;\n current = target.node;\n visited.add(nodeKey(current));\n }\n return false;\n }\n\n /**\n * Get the \"group root\" key for a sibling group — the stable key the\n * selection map is keyed by. For an input node (edit versions) that is the\n * earliest fork-of ancestor; for a reply run (regenerate group) it is the\n * oldest same-parent run (the original reply).\n * @param key - Any node key in the sibling group.\n * @returns The group root's key.\n */\n getGroupRoot(key: string): string {\n const entry = this._nodeIndex.get(key);\n if (!entry) return key;\n\n if (entry.node.kind === 'input') {\n return nodeKey(this._inputGroupRoot(entry.node));\n }\n\n // Reply run: the oldest same-parent run is the original reply.\n const group = this._getSiblingGroup(key);\n const root = group[0]?.node;\n return root ? nodeKey(root) : key;\n }\n\n // -------------------------------------------------------------------------\n // Public query methods\n // -------------------------------------------------------------------------\n\n /**\n * Walk the visible node chain along the selected branches, kind-blind. An\n * input node and a reply run reach each other through the same\n * parent-membership check, so seed-only user→user chains and the\n * input→reply→input weave both resolve here. Sibling groups (edit versions /\n * regenerate runs) collapse to the selected member.\n * @param selections - Per-group selected member key, keyed by group root.\n * @returns The visible nodes (both kinds) in chronological order.\n */\n visibleNodes(selections: Map<string, string> = new Map<string, string>()): ConversationNode<TProjection>[] {\n this._logger.trace('DefaultTree.visibleNodes();');\n const result: ConversationNode<TProjection>[] = [];\n const currentPath = new Set<string>();\n const resolvedGroups = new Map<string, string>(); // groupRootKey -> selected key\n\n for (const internal of this._sortedNodes) {\n const node = internal.node;\n const key = nodeKey(node);\n\n // Step 1: Parent reachability (kind-blind — the parent may be an input\n // node or a reply run; resolve its key and check the active path).\n const parentKey = this._parentKeyOf(node);\n if (parentKey !== undefined && !currentPath.has(parentKey)) {\n continue;\n }\n\n // Step 2: Sibling selection.\n const group = this._getSiblingGroup(key);\n if (group.length > 1) {\n const groupRootKey = this.getGroupRoot(key);\n let selectedKey = resolvedGroups.get(groupRootKey);\n if (selectedKey === undefined) {\n const preferredKey = selections.get(groupRootKey);\n if (preferredKey !== undefined && group.some((n) => nodeKey(n.node) === preferredKey)) {\n selectedKey = preferredKey;\n } else {\n const latest = group.at(-1);\n if (!latest) break; // unreachable: group.length > 1\n selectedKey = nodeKey(latest.node);\n }\n resolvedGroups.set(groupRootKey, selectedKey);\n }\n if (key !== selectedKey) {\n continue;\n }\n }\n\n currentPath.add(key);\n result.push(node);\n }\n\n return result;\n }\n\n getRunNode(runId: string): RunNode<TProjection> | undefined {\n this._logger.trace('DefaultTree.getRunNode();', { runId });\n const node = this._nodeIndex.get(runId)?.node;\n return node?.kind === 'run' ? node : undefined;\n }\n\n getNode(key: string): ConversationNode<TProjection> | undefined {\n this._logger.trace('DefaultTree.getNode();', { key });\n return this._nodeIndex.get(key)?.node;\n }\n\n getNodeByCodecMessageId(codecMessageId: string): ConversationNode<TProjection> | undefined {\n this._logger.trace('DefaultTree.getNodeByCodecMessageId();', { codecMessageId });\n const key = this._codecMessageIdToNodeKey.get(codecMessageId);\n return key === undefined ? undefined : this._nodeIndex.get(key)?.node;\n }\n\n getReplyRuns(inputCodecMessageId: string): RunNode<TProjection>[] {\n const runIds = this._replyRunsByInput.get(inputCodecMessageId);\n if (!runIds) return [];\n const result: RunNode<TProjection>[] = [];\n for (const runId of runIds) {\n const node = this._nodeIndex.get(runId)?.node;\n if (node?.kind === 'run') result.push(node);\n }\n return result;\n }\n\n getSiblingNodes(key: string): ConversationNode<TProjection>[] {\n this._logger.trace('DefaultTree.getSiblingNodes();', { key });\n return this._getSiblingGroup(key).map((n) => n.node);\n }\n\n // -------------------------------------------------------------------------\n // Mutation\n // -------------------------------------------------------------------------\n\n applyMessage(\n events: { inputs: TInput[]; outputs: TOutput[] },\n headers: Record<string, string>,\n serial?: string,\n timestamp?: number,\n version?: string,\n ): void {\n const wireRunId = headers[HEADER_RUN_ID];\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n\n // Classify: with NO run-id, a user message carrying a codec-message-id and\n // at least one input event forms an INPUT node keyed by that\n // codec-message-id — the client owns it; the agent mints the reply run-id\n // separately. Everything else needs a run-id to route to a reply run.\n // Capturing the id (not a boolean) narrows it to `string` for the input path.\n const inputNodeCodecMessageId =\n wireRunId === undefined &&\n codecMessageId !== undefined &&\n headers[HEADER_ROLE] === 'user' &&\n events.inputs.length > 0\n ? codecMessageId\n : undefined;\n\n if (wireRunId === undefined && inputNodeCodecMessageId === undefined) {\n this._logger.warn('Tree.applyMessage(); message has no run-id and is not a user input; skipping');\n return;\n }\n\n // Fold inputs first, then outputs, preserving wire order.\n const all: CodecEvent<TInput, TOutput>[] = toCodecEvents(events);\n\n // Wire-only metadata-carrier messages (e.g. `ait-regenerate`) decode to\n // zero events and don't need a node at the tree level — the eventual reply\n // run is created later by run-start, and any regenerate / parent\n // information the wire carried is reread from the run-start headers.\n // Skipping here avoids a phantom node that would inflate sibling counts.\n const existingKey = inputNodeCodecMessageId ?? wireRunId;\n if (all.length === 0 && existingKey !== undefined && !this._nodeIndex.has(existingKey)) {\n return;\n }\n\n // `update` is the structural channel: emit it only when this apply\n // actually changes the tree shape (new node, startSerial promotion).\n // Content-only folds (streaming chunks into an existing node) flow through\n // `output` instead, so they leave `_structuralVersion` untouched.\n const structuralBefore = this._structuralVersion;\n\n if (inputNodeCodecMessageId !== undefined) {\n this._applyInputMessage(inputNodeCodecMessageId, headers, serial, timestamp, version, all);\n } else if (wireRunId !== undefined) {\n this._applyRunMessage(wireRunId, events, headers, serial, timestamp, version);\n }\n\n if (this._structuralVersion !== structuralBefore) this._emitter.emit('update');\n }\n\n /**\n * Apply a run-less user input wire: create (or promote the serial of) the\n * input node keyed by its codec-message-id, fold the input events into its\n * own projection, and emit an `output` event (with empty outputs — input\n * folds carry none) so the View observes the optimistic insert.\n * @param codecMessageId - The input node's codec-message-id (its primary key).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for an optimistic insert.\n * @param timestamp - Ably server timestamp (epoch ms); undefined for an optimistic insert.\n * @param version - The delivery's `Message.version.serial`, or undefined.\n * @param all - The direction-tagged input events to fold, in wire order.\n */\n private _applyInputMessage(\n codecMessageId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n timestamp: number | undefined,\n version: string | undefined,\n all: CodecEvent<TInput, TOutput>[],\n ): void {\n let entry = this._nodeIndex.get(codecMessageId);\n if (!entry) {\n entry = this._createInputNodeFromHeaders(codecMessageId, headers, serial);\n this._insertNode(codecMessageId, entry, entry.node.parentCodecMessageId);\n this._codecMessageIdToNodeKey.set(codecMessageId, codecMessageId);\n this._logger.debug('Tree.applyMessage(); created input node', { codecMessageId });\n } else if (entry.node.kind === 'input' && serial && !entry.node.serial) {\n // Promote optimistic serial when the relay/echo arrives.\n this._logger.debug('Tree.applyMessage(); promoting input serial', { codecMessageId, serial });\n entry.node.serial = serial;\n this._promoteSerial(entry);\n }\n\n this._recordActivity(entry, timestamp);\n\n // Log the wire and fold it — incrementally onto the tail in the common\n // case, or by refolding the node if this wire arrived out of serial order.\n this._recordAndFold(entry, all, serial, codecMessageId, version, headers[HEADER_STREAM] === 'true');\n\n // An input node owns no agent outputs; the event still fires (empty\n // outputs) so consumers observe the projection change. It has no run-id —\n // the causal routing key is the input's own codec-message-id.\n this._emitter.emit('output', {\n runId: undefined,\n inputCodecMessageId: codecMessageId,\n codecMessageId,\n serial,\n events: [],\n });\n }\n\n /**\n * Apply a reply-run wire (assistant output, continuation tool-resolution, or\n * a fresh run keyed by the agent-minted run-id): create or reconcile the run\n * node, fold its events, maintain the codec-message-id and reply→input\n * indices, and emit the `output` event. Derives the codec-message-id,\n * triggering-input id, fold list, and outputs from `events`/`headers`,\n * mirroring `applyMessage`.\n * @param wireRunId - The run-id from the inbound wire (the node's primary key).\n * @param events - The decoded inputs and outputs from the wire.\n * @param events.inputs - Client-published events (`ai-input` wire).\n * @param events.outputs - Agent-published events (`ai-output` wire).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for an optimistic insert.\n * @param timestamp - Ably server timestamp (epoch ms); undefined for an optimistic insert.\n * @param version - The delivery's `Message.version.serial`, or undefined.\n */\n private _applyRunMessage(\n wireRunId: string,\n events: { inputs: TInput[]; outputs: TOutput[] },\n headers: Record<string, string>,\n serial: string | undefined,\n timestamp: number | undefined,\n version: string | undefined,\n ): void {\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n // The triggering input's codec-message-id (the agent's echo), surfaced on\n // the `output` event as the stream's causal routing key.\n const inputCodecMessageId = headers[HEADER_INPUT_CODEC_MESSAGE_ID];\n // Fold inputs first, then outputs, preserving wire order.\n const all: CodecEvent<TInput, TOutput>[] = toCodecEvents(events);\n const outputs = events.outputs;\n\n let run = this._nodeIndex.get(wireRunId);\n\n // Reconcile an optimistic insert with its serial-bearing echo by\n // codec-message-id rather than the wire run-id — covers assistant content\n // that pins a codec-message-id before its run-id is indexed.\n if (!run && codecMessageId !== undefined) {\n const indexedKey = this._codecMessageIdToNodeKey.get(codecMessageId);\n const indexed = indexedKey === undefined ? undefined : this._nodeIndex.get(indexedKey);\n if (indexed?.node.kind === 'run' && indexed.node.startSerial === undefined) run = indexed;\n }\n\n if (!run) {\n run = this._createRunFromHeaders(wireRunId, headers, serial);\n this._insertNode(wireRunId, run, run.node.parentCodecMessageId);\n this._indexReplyRun(run.node, wireRunId);\n this._logger.debug('Tree.applyMessage(); created new Run', { runId: wireRunId });\n } else if (serial && run.node.kind === 'run' && !run.node.startSerial) {\n // Promote optimistic startSerial when the relay/echo arrives.\n this._logger.debug('Tree.applyMessage(); promoting startSerial', { runId: wireRunId, serial });\n run.node.startSerial = serial;\n this._promoteSerial(run);\n }\n\n // Index the codec-message-id against the node that actually owns it.\n const ownerKey = nodeKey(run.node);\n if (codecMessageId) this._codecMessageIdToNodeKey.set(codecMessageId, ownerKey);\n\n this._recordActivity(run, timestamp);\n\n // Log the wire and fold it — incrementally onto the tail in the common\n // case, or by refolding the node if this wire arrived out of serial order.\n // `run` may be a reconciled optimistic node: record on whichever entry\n // owns the fold.\n this._recordAndFold(run, all, serial, codecMessageId, version, headers[HEADER_STREAM] === 'true');\n\n this._emitter.emit('output', { runId: ownerKey, inputCodecMessageId, codecMessageId, serial, events: outputs });\n }\n\n /**\n * Record a reply run against its input-node parent (the reverse edge powering\n * `getReplyRuns` and regenerate sibling grouping). A reply run's\n * `parentCodecMessageId` is its input node's codec-message-id (the master\n * invariant), so no resolution is needed.\n * @param node - The reply run node.\n * @param runId - The run's id.\n */\n private _indexReplyRun(node: ConversationNode<TProjection>, runId: string): void {\n if (node.parentCodecMessageId === undefined) return;\n addToSetMap(this._replyRunsByInput, node.parentCodecMessageId, runId);\n }\n\n applyRunLifecycle(event: RunLifecycleEvent): void {\n this._logger.trace('DefaultTree.applyRunLifecycle();', { type: event.type, runId: event.runId });\n // Structural channel: emit `update` only when the lifecycle event changes\n // the tree shape. Only run-start can do that (a new Run, startSerial\n // promotion, or structural-metadata backfill); suspend/resume/end mutate\n // status/endSerial on an existing node — content, not structure — so the\n // conditional naturally never fires for them.\n const structuralBefore = this._structuralVersion;\n switch (event.type) {\n case 'start': {\n this._applyRunStart(event);\n break;\n }\n case 'suspend': {\n this._applyRunSuspend(event);\n break;\n }\n case 'resume': {\n this._applyRunResume(event);\n break;\n }\n case 'end': {\n this._applyRunEnd(event);\n break;\n }\n }\n this._emitter.emit('run', event);\n if (this._structuralVersion !== structuralBefore) this._emitter.emit('update');\n }\n\n /**\n * Apply a run-start lifecycle event's structural effect: create the reply\n * run if it doesn't exist yet, or backfill an optimistic / wire-created\n * node's structure and metadata from the canonical run-start. Mutates\n * `_structuralVersion` when the tree shape changes; the caller owns the\n * `run`/`update` emits.\n * @param event - The run-start lifecycle event.\n */\n private _applyRunStart(event: RunLifecycleEvent & { type: 'start' }): void {\n const existing = this._nodeIndex.get(event.runId);\n if (existing?.node.kind === 'run') {\n const node = existing.node;\n // Activate only a suspended run. A run-start can be observed AFTER the\n // run's terminal event (history pages replay newest-first, so an older\n // page delivers the start last) — like a stray resume, it must never\n // resurrect a run that has ended.\n if (node.state.status === 'suspended') {\n node.state = { status: 'active' };\n }\n if (event.serial && !node.startSerial) {\n node.startSerial = event.serial;\n this._promoteSerial(existing);\n }\n // Backfill structural metadata if the Run was created from an\n // assistant wire that arrived before run-start (history pagination\n // boundary or out-of-order delivery). The run-start lifecycle event is\n // the canonical source for parent/forkOf/regenerates; only fill in\n // fields the wire didn't already populate. A run-start is always a\n // first start (continuations re-enter via `ai-run-resume`, which\n // carries no structural metadata), so it is unconditionally\n // authoritative here. `parent` is the run's STRUCTURAL parent (its\n // input node) — reachability and the reply→input edge read it.\n if (node.parentCodecMessageId === undefined && event.parent !== undefined) {\n node.parentCodecMessageId = event.parent;\n this._removeFromParentIndex(undefined, event.runId);\n this._addToParentIndex(node.parentCodecMessageId, event.runId);\n this._indexReplyRun(node, event.runId);\n this._structuralVersion++;\n }\n if (node.forkOf === undefined && event.forkOf !== undefined) {\n const forkOfKey = this._codecMessageIdToNodeKey.get(event.forkOf);\n if (forkOfKey !== undefined && forkOfKey !== event.runId) {\n node.forkOf = forkOfKey;\n this._structuralVersion++;\n }\n }\n if (node.regeneratesCodecMessageId === undefined && event.regenerates !== undefined) {\n node.regeneratesCodecMessageId = event.regenerates;\n this._structuralVersion++;\n }\n // Adopt the agent-minted invocation-id onto the optimistic node. The\n // agent mints it, so a node created from an optimistic insert (or an\n // assistant wire that arrived before run-start) carries an empty id\n // until the agent's run-start delivers it. Metadata, not structure —\n // consumers re-read it on the `run` emit, so no structural-version\n // bump.\n if (node.invocationId === '' && event.invocationId !== '') {\n node.invocationId = event.invocationId;\n }\n // The run's serial floor is now observed: no older history page can\n // deliver further wires for this node. With a terminal status this\n // makes the node structurally complete — eligible for log retention\n // sweeping once the reorder window lapses.\n existing.runStartSeen = true;\n this._recordActivity(existing, event.timestamp);\n this._maybeQueueSweep(existing);\n } else if (!existing) {\n const run = this._createRunFromLifecycle(event);\n this._insertNode(event.runId, run, run.node.parentCodecMessageId);\n this._indexReplyRun(run.node, event.runId);\n this._recordActivity(run, event.timestamp);\n }\n }\n\n /**\n * Apply a run-suspend lifecycle event: pause the run without ending it —\n * mark the node 'suspended' and record the serial it paused at, but keep the\n * Run live so a resume under the same runId resumes it. Status/endSerial are\n * content, not structure, so this never mutates `_structuralVersion`; the\n * caller owns the emits.\n * @param event - The run-suspend lifecycle event.\n */\n private _applyRunSuspend(event: RunLifecycleEvent & { type: 'suspend' }): void {\n const run = this._nodeIndex.get(event.runId);\n if (run?.node.kind === 'run') {\n run.node.state = { status: 'suspended' };\n run.node.endSerial = event.serial;\n this._recordActivity(run, event.timestamp);\n }\n }\n\n /**\n * Apply a run-resume lifecycle event: re-enter an already-started run by\n * flipping a suspended run back to 'active'. Pure re-entry — it carries no\n * parent/forkOf and does not promote startSerial (the original run-start owns\n * the run's structure). Only a suspended run resumes: a no-op when the run\n * isn't known (e.g. a resume replayed from a newer history page before its\n * run-start) and a no-op for an already-active or terminal\n * (complete/cancelled/error) run — a stray resume must never resurrect a run\n * that has ended. The caller owns the emits.\n * @param event - The run-resume lifecycle event.\n */\n private _applyRunResume(event: RunLifecycleEvent & { type: 'resume' }): void {\n const run = this._nodeIndex.get(event.runId);\n if (run?.node.kind === 'run' && run.node.state.status === 'suspended') {\n run.node.state = { status: 'active' };\n this._recordActivity(run, event.timestamp);\n }\n }\n\n /**\n * Apply a run-end lifecycle event: record the terminal reason (and, for an\n * error end, the error) as the node's state, plus the serial it ended at.\n * State/endSerial are content, not structure, so this never mutates\n * `_structuralVersion`; the caller owns the emits.\n *\n * A run-end for an unknown runId is a no-op: nothing else is known about the\n * run yet, so there is no node to mark. When that happens during history\n * replay (a page boundary falling just before the run-end, so the run's\n * other wires arrive in later pages), the run is never marked terminal and\n * its event log is retained for the Tree's lifetime — over-retention, never\n * corruption.\n * @param event - The run-end lifecycle event.\n */\n private _applyRunEnd(event: RunLifecycleEvent & { type: 'end' }): void {\n const run = this._nodeIndex.get(event.runId);\n if (run?.node.kind === 'run') {\n run.node.state = event.reason === 'error' ? { status: 'error', error: event.error } : { status: event.reason };\n run.node.endSerial = event.serial;\n this._recordActivity(run, event.timestamp);\n this._maybeQueueSweep(run);\n }\n }\n\n delete(key: string): void {\n const entry = this._nodeIndex.get(key);\n if (!entry) return;\n\n this._logger.debug('Tree.delete();', { key });\n\n this._removeFromParentIndex(entry.node.parentCodecMessageId, key);\n this._removeSortedNode(entry);\n this._nodeIndex.delete(key);\n // Drop the reply→input reverse edge.\n if (entry.node.kind === 'run' && entry.node.parentCodecMessageId !== undefined) {\n deleteFromSetMap(this._replyRunsByInput, entry.node.parentCodecMessageId, key);\n }\n // _codecMessageIdToNodeKey entries pointing at this node linger but are\n // harmless; they'll be overwritten if the node is re-created and remain\n // dangling otherwise. Cleanup not worth the index walk.\n\n this._structuralVersion++;\n this._emitter.emit('update');\n }\n\n // -------------------------------------------------------------------------\n // Internal helpers\n // -------------------------------------------------------------------------\n\n /**\n * Build a fresh RunNode from a wire message's headers. Used when an\n * inbound message arrives before any run-start event for its runId.\n * @param runId - The run-id from the inbound wire.\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for optimistic inserts.\n * @returns A newly-allocated internal run node ready for insertion.\n */\n private _createRunFromHeaders(\n runId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n ): InternalNode<TInput, TOutput, TProjection> {\n const forkOfMsgId = headers[HEADER_FORK_OF];\n return this._buildRunNode({\n runId,\n parentCodecMessageId: headers[HEADER_PARENT],\n // forkOf is resolved to the fork target's node key (an input node's\n // codec-message-id, or a run's id) — the same space `_isSiblingOf` walks.\n forkOf: forkOfMsgId ? this._codecMessageIdToNodeKey.get(forkOfMsgId) : undefined,\n regeneratesCodecMessageId: headers[HEADER_MSG_REGENERATE],\n clientId: headers[HEADER_RUN_CLIENT_ID] ?? '',\n invocationId: headers[HEADER_INVOCATION_ID] ?? '',\n startSerial: serial,\n // Created from a content wire — the run's ai-run-start has not been\n // observed (it may still be in an unloaded older history page).\n runStartSeen: false,\n });\n }\n\n /**\n * Wrap a freshly-built conversation node in its internal envelope — sort\n * sequence, event log, and retention/promotion state. The single home for\n * those per-node fields, so a new field is added in one place rather than at\n * every node-construction site.\n * @param node - The conversation node to wrap.\n * @param runStartSeen - Whether the run's ai-run-start has been observed\n * (run nodes only; always false for input nodes).\n * @returns A newly-allocated internal node ready for insertion.\n */\n private _wrapNode(\n node: ConversationNode<TProjection>,\n runStartSeen = false,\n ): InternalNode<TInput, TOutput, TProjection> {\n return {\n node,\n insertSeq: this._seqCounter++,\n log: new WireLog<CodecEvent<TInput, TOutput>>(),\n lastActivityTs: 0,\n runStartSeen,\n sweepQueued: false,\n optimistic: false,\n };\n }\n\n /**\n * Allocate a RunNode from already-resolved fields. Shared by the\n * header-driven and lifecycle-driven run creators: both build the identical\n * RunNode literal and stamp an insert sequence.\n * @param params - The resolved run fields.\n * @param params.runId - The run's id (its primary key).\n * @param params.parentCodecMessageId - Structural parent codec-message-id, or undefined for a root.\n * @param params.forkOf - The resolved fork target's node key (already mapped through the codec-message-id index), or undefined.\n * @param params.regeneratesCodecMessageId - The codec-message-id this run regenerates, or undefined.\n * @param params.clientId - The publishing client's id.\n * @param params.invocationId - The agent invocation id.\n * @param params.startSerial - Ably channel serial; undefined for optimistic inserts.\n * @param params.runStartSeen - Whether the run's ai-run-start has been observed (true only for lifecycle-created runs).\n * @returns A newly-allocated internal run node ready for insertion.\n */\n private _buildRunNode(params: {\n runId: string;\n parentCodecMessageId: string | undefined;\n forkOf: string | undefined;\n regeneratesCodecMessageId: string | undefined;\n clientId: string;\n invocationId: string;\n startSerial: string | undefined;\n runStartSeen: boolean;\n }): InternalNode<TInput, TOutput, TProjection> {\n const node: RunNode<TProjection> = {\n kind: 'run',\n runId: params.runId,\n parentCodecMessageId: params.parentCodecMessageId,\n forkOf: params.forkOf,\n regeneratesCodecMessageId: params.regeneratesCodecMessageId,\n clientId: params.clientId,\n invocationId: params.invocationId,\n state: { status: 'active' },\n projection: this._codec.init(),\n startSerial: params.startSerial,\n endSerial: undefined,\n };\n\n return this._wrapNode(node, params.runStartSeen);\n }\n\n /**\n * Build a fresh InputNode from a run-less user input wire's headers.\n * @param codecMessageId - The input's codec-message-id (its primary key).\n * @param headers - Transport headers from the inbound Ably message.\n * @param serial - Ably channel serial; undefined for optimistic inserts.\n * @returns A newly-allocated internal input node ready for insertion.\n */\n private _createInputNodeFromHeaders(\n codecMessageId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n ): InternalNode<TInput, TOutput, TProjection> {\n const forkOfMsgId = headers[HEADER_FORK_OF];\n const node: InputNode<TProjection> = {\n kind: 'input',\n codecMessageId,\n parentCodecMessageId: headers[HEADER_PARENT],\n // An edit's fork-of names the original prompt's codec-message-id, which\n // IS that input node's key — no resolution needed.\n forkOf: forkOfMsgId,\n projection: this._codec.init(),\n serial,\n };\n return this._wrapNode(node);\n }\n\n /**\n * Build a fresh RunNode from a run-start lifecycle event. Used when a\n * run-start event arrives before any message for its runId.\n * @param event - The run-start lifecycle event from the agent, including\n * its channel serial.\n * @returns A newly-allocated internal run node ready for insertion.\n */\n private _createRunFromLifecycle(\n event: RunLifecycleEvent & { type: 'start' },\n ): InternalNode<TInput, TOutput, TProjection> {\n const forkOfMsgId = event.forkOf;\n return this._buildRunNode({\n runId: event.runId,\n parentCodecMessageId: event.parent,\n forkOf: forkOfMsgId ? this._codecMessageIdToNodeKey.get(forkOfMsgId) : undefined,\n regeneratesCodecMessageId: event.regenerates,\n clientId: event.clientId,\n invocationId: event.invocationId,\n startSerial: event.serial,\n // Created from the run-start itself — the serial floor is observed.\n runStartSeen: true,\n });\n }\n\n // -------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT8b, AIT-CT8e\n on(event: 'update', handler: () => void): () => void;\n on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;\n on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;\n on(event: 'output', handler: (event: OutputEvent<TOutput>) => void): () => void;\n on(\n event: 'update' | 'ably-message' | 'run' | 'output',\n handler:\n | (() => void)\n | ((msg: Ably.InboundMessage) => void)\n | ((event: RunLifecycleEvent) => void)\n | ((event: OutputEvent<TOutput>) => void),\n ): () => void {\n // CAST: overload signatures enforce correct handler types per event name.\n const cb = handler as (arg: TreeEventsMap<TOutput>[keyof TreeEventsMap<TOutput>]) => void;\n this._emitter.on(event, cb);\n return () => {\n this._emitter.off(event, cb);\n };\n }\n\n /**\n * Forward a raw Ably message event to tree subscribers. Also indexes the\n * Ably message by `event-id` header (if present) for\n * {@link findAblyMessageByEventId} lookups.\n * @param msg - The raw Ably message to emit.\n */\n emitAblyMessage(msg: Ably.InboundMessage): void {\n this._logger.trace('DefaultTree.emitAblyMessage();');\n const headers = getTransportHeaders(msg);\n const eventId = headers[HEADER_EVENT_ID];\n if (eventId !== undefined && !this._eventIdIndex.has(eventId)) {\n this._eventIdIndex.set(eventId, msg);\n }\n this._emitter.emit('ably-message', msg);\n }\n\n findAblyMessageByEventId(eventId: string): Ably.InboundMessage | undefined {\n return this._eventIdIndex.get(eventId);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Tree that materializes branching conversation history from a flat\n * oplog of Ably messages as a two-node-per-turn forest (input node + reply run).\n * @param codec - Codec used to fold inbound events into per-Run projections.\n * @param logger - Logger for diagnostic output.\n * @returns A new {@link DefaultTree} instance. The session uses DefaultTree\n * directly for internal methods (applyMessage, applyRunLifecycle,\n * emitAblyMessage). Public consumers see the narrower {@link Tree} interface.\n */\nexport const createTree = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(\n codec: Reducer<CodecEvent<TInput, TOutput>, TProjection>,\n logger: Logger,\n): DefaultTree<TInput, TOutput, TProjection> => new DefaultTree<TInput, TOutput, TProjection>(codec, logger);\n","/**\n * Core agent (server-side) session, parameterized by codec.\n *\n * Composes RunManager and pipeStream to handle the full server-side run\n * lifecycle. Cancel message routing is handled directly by the session's\n * single channel subscription — no separate cancel manager needed.\n *\n * The session exposes a single factory method — `createRun()` — which returns\n * a Run object with explicit lifecycle methods: start(), pipe(), suspend(),\n * and end() (suspend() and end() are both terminal).\n */\n\nimport * as Ably from 'ably';\n// Also augments RealtimeChannel with `.object` (ably/liveobjects side-effect).\nimport type * as AblyObjects from 'ably/liveobjects';\n\nimport {\n EVENT_CANCEL,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_FORK_OF,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_MSG_REGENERATE,\n HEADER_PARENT,\n HEADER_RUN_CLIENT_ID,\n HEADER_RUN_ID,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport { type Logger, LogLevel, makeLogger } from '../../logger.js';\nimport { errorCause, errorMessage, getTransportHeaders } from '../../utils.js';\nimport { registerAgent } from '../agent.js';\nimport { resolveChannelModes } from '../channel-options.js';\nimport type { Codec, CodecInputEvent, CodecOutputEvent } from '../codec/types.js';\nimport { type AgentView, createAgentView } from './agent-view.js';\nimport { createWireApplier, type WireApplier } from './decode-fold.js';\nimport { buildTransportHeaders } from './headers.js';\nimport { evictOldestIfFull } from './internal/bounded-map.js';\nimport { Invocation } from './invocation.js';\nimport { pipeStream } from './pipe-stream.js';\nimport type { RunManager } from './run-manager.js';\nimport { createRunManager } from './run-manager.js';\nimport { bestEffortDetach, continuityLostError, isContinuityLost, requireConnected } from './session-support.js';\nimport { createTree, type DefaultTree } from './tree.js';\nimport type {\n AgentSession,\n AgentSessionOptions,\n CancelRequest,\n LoadConversationOptions,\n PipeOptions,\n Run,\n RunEndParams,\n RunRuntime,\n RunView,\n StreamResult,\n Tree,\n} from './types.js';\n\n/**\n * Upper bound on buffered deferred cancels. Deferred cancels are bounded so\n * a pathological burst can't grow the map without bound. 200 outstanding\n * fresh-send cancels in flight is ample — a typical agent process sees one\n * per HTTP request.\n */\nconst DEFERRED_CANCEL_LIMIT = 200;\n\n// ---------------------------------------------------------------------------\n// Internal run record for cancel routing\n// ---------------------------------------------------------------------------\n\ninterface RegisteredRun {\n runId: string;\n /** Invocation-id this run is associated with, minted by the agent at `createRun` (or the `runtime.invocationId` override). */\n invocationId: string;\n controller: AbortController;\n /** Composite signal that fires when either the internal controller or the external signal aborts. */\n signal: AbortSignal;\n onCancel?: (request: CancelRequest) => Promise<boolean>;\n onError?: (error: Ably.ErrorInfo) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal state machines\n// ---------------------------------------------------------------------------\n\nenum SessionState {\n READY = 'ready',\n CLOSED = 'closed',\n}\n\nenum RunState {\n INITIALIZED = 'initialized',\n STARTED = 'started',\n ENDED = 'ended',\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-ST1\nclass DefaultAgentSession<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> implements AgentSession<TOutput, TProjection, TMessage> {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: Codec<TInput, TOutput, TProjection, TMessage>;\n private readonly _logger: Logger | undefined;\n private readonly _onError: ((error: Ably.ErrorInfo) => void) | undefined;\n private readonly _runManager: RunManager;\n private readonly _registeredRuns = new Map<string, RegisteredRun>();\n /**\n * Reverse index from a run's triggering input codec-message-id to its\n * run-id, populated once `Run.start()`'s input-event lookup resolves the\n * triggering input. Lets `_handleCancelMessage` route a cancel keyed by the\n * input codec-message-id (a fresh send whose run-id the client doesn't know)\n * to the registered run. Entries are removed when the run ends / suspends /\n * the session closes, alongside `_registeredRuns`.\n */\n private readonly _runIdByInputCodecMessageId = new Map<string, string>();\n /**\n * Cancels buffered by triggering input codec-message-id when they arrived\n * before the run was known — i.e. before `Run.start()`'s input-event lookup\n * resolved that input to a run. A fresh run has no run-id at the client's\n * send time (the agent mints it at run-start), so an early cancel can only be\n * keyed by the input codec-message-id, and the `inputCodecMessageId → run`\n * linkage doesn't exist until the lookup completes. `Run.start()` consults\n * this buffer as a PULL once it resolves its `resolvedInputCodecMessageId`,\n * honouring any cancel that arrived first. Cleared on `close()`.\n */\n private readonly _deferredCancels = new Map<string, Ably.InboundMessage>();\n /**\n * Session-owned materialisation tree. Every message (live + history) folds\n * through `this._applier.apply(msg)`; conversation state is read by\n * walking parent pointers from the input node.\n *\n * Replaced (not cleared in place) on channel continuity loss so that the\n * fresh tree starts empty. The old tree is abandoned to GC once in-flight\n * lookups have aborted.\n */\n private _tree: DefaultTree<TInput, TOutput, TProjection>;\n /**\n * The Tree's single decode-and-apply engine, binding one inbound decoder\n * instance shared by every fold route (live + history). Streaming across\n * pages folds correctly because the decoder keeps stream-tracker state\n * across messages. Replaced alongside the Tree on continuity loss so the\n * fresh Tree gets a fresh decoder. Outbound encoders (used by `Run.pipe`)\n * manage their own decoders.\n */\n private _applier: WireApplier;\n /**\n * Internal server-side view: input-event lookup + conversation loading over\n * the session Tree. Holds the Tree/applier directly (like the client's\n * DefaultView), so it is RECREATED — not mutated — when the Tree is swapped\n * on continuity loss.\n */\n private _agentView: AgentView<TInput, TOutput, TProjection, TMessage>;\n private readonly _channelListener: (msg: Ably.InboundMessage) => void;\n private readonly _inputEventLookupTimeoutMs: number;\n /** Lookback bound passed to the AgentView's input-event scan (see {@link _createAgentView}). */\n private readonly _inputEventLookbackMs: number;\n\n private _state = SessionState.READY;\n private _connectPromise: Promise<void> | undefined;\n private _hasAttachedOnce: boolean;\n private readonly _onChannelStateChange: Ably.channelEventCallback;\n\n constructor(options: AgentSessionOptions<TInput, TOutput, TProjection, TMessage>) {\n this._codec = options.codec;\n // Spec: AIT-ST1a, AIT-ST1a2 — register this SDK on both the connection\n // (options.agents) and channel-attach (params.agent) paths. Idempotent\n // across sessions sharing one client.\n const registerOptions = registerAgent(options.client, options.codec);\n const channelOptions: Ably.ChannelOptions = { ...registerOptions };\n // Spec: AIT-ST16 — request object modes etc. when channelModes opts in.\n const modes = resolveChannelModes(options.channelModes);\n if (modes) channelOptions.modes = modes;\n this._channel = options.client.channels.get(options.channelName, channelOptions);\n this._logger = options.logger?.withContext({ component: 'AgentSession' });\n this._onError = options.onError;\n this._runManager = createRunManager(this._channel, this._logger);\n this._inputEventLookupTimeoutMs = options.inputEventLookupTimeoutMs ?? 30000;\n this._inputEventLookbackMs = options.inputEventLookbackMs ?? 120_000;\n this._tree = createTree<TInput, TOutput, TProjection>(\n this._codec,\n this._logger ?? makeLogger({ logLevel: LogLevel.Silent }),\n );\n this._applier = createWireApplier(this._tree, this._codec.createDecoder());\n this._agentView = this._createAgentView();\n\n this._channelListener = (msg: Ably.InboundMessage) => {\n this._handleChannelMessage(msg);\n };\n\n // Spec: AIT-ST12, AIT-ST12a\n // Listen for channel state changes that break message continuity. The\n // session only consumes cancel messages from the channel, so losing one\n // is survivable — but the developer needs to know so they can decide\n // whether to cancel in-flight work. _hasAttachedOnce is seeded from the\n // channel's current state so pre-attached channels are handled correctly;\n // it distinguishes the initial attach from a genuine discontinuity.\n this._hasAttachedOnce = this._channel.state === 'attached';\n this._onChannelStateChange = (stateChange: Ably.ChannelStateChange) => {\n this._handleChannelStateChange(stateChange);\n };\n this._channel.on(this._onChannelStateChange);\n\n this._logger?.debug('DefaultAgentSession(); session created');\n }\n\n /**\n * Build an AgentView bound to the session's CURRENT Tree + applier. Called at\n * construction and again after a continuity-loss swap — the AgentView holds\n * the Tree/applier directly (like DefaultView), so a swap recreates it rather\n * than mutating it in place.\n * @returns A fresh AgentView over the current Tree/applier.\n */\n private _createAgentView(): AgentView<TInput, TOutput, TProjection, TMessage> {\n return createAgentView<TInput, TOutput, TProjection, TMessage>({\n tree: this._tree,\n channel: this._channel,\n codec: this._codec,\n applier: this._applier,\n logger: this._logger,\n inputEventLookbackMs: this._inputEventLookbackMs,\n });\n }\n\n // -------------------------------------------------------------------------\n // Public accessors\n // -------------------------------------------------------------------------\n\n // Spec: AIT-ST14\n get presence(): Ably.RealtimePresence {\n return this._channel.presence;\n }\n\n // Spec: AIT-ST15\n get object(): AblyObjects.RealtimeObject {\n return this._channel.object;\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n\n // Spec: AIT-ST2\n // eslint-disable-next-line @typescript-eslint/promise-function-async -- preserve reference equality across calls\n connect(): Promise<void> {\n if (this._state === SessionState.CLOSED) {\n return Promise.reject(new Ably.ErrorInfo('unable to connect; session is closed', ErrorCode.SessionClosed, 400));\n }\n if (this._connectPromise) return this._connectPromise;\n\n this._logger?.trace('DefaultAgentSession.connect();');\n // Subscribe unfiltered (before attach, per RTL7g — subscribe implicitly\n // attaches the channel). Unfiltered so the Tree folds every post-attach\n // message regardless of name (cancel control messages are dispatched\n // separately by the channel listener after the Tree fold).\n this._connectPromise = this._channel.subscribe(this._channelListener).then(\n () => {\n this._logger?.debug('DefaultAgentSession.connect(); subscribed and attached');\n },\n (error: unknown) => {\n const errInfo = new Ably.ErrorInfo(\n `unable to subscribe to channel; ${errorMessage(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n errorCause(error),\n );\n this._logger?.error('DefaultAgentSession.connect(); subscribe failed');\n this._onError?.(errInfo);\n throw errInfo;\n },\n );\n return this._connectPromise;\n }\n\n /**\n * The session-owned materialisation tree. Mirrors `ClientSession.tree`\n * for observability and parity.\n * @returns The session's Tree.\n */\n get tree(): Tree<TOutput, TProjection> {\n return this._tree;\n }\n\n // Spec: AIT-ST3\n createRun(invocation: Invocation, runtime?: RunRuntime<TOutput>): Run<TOutput, TProjection, TMessage> {\n this._logger?.trace('DefaultAgentSession.createRun();', { inputEventId: invocation.inputEventId });\n return this._createRun(invocation, runtime ?? {});\n }\n\n // Spec: AIT-ST11\n async close(): Promise<void> {\n if (this._state === SessionState.CLOSED) return;\n this._state = SessionState.CLOSED;\n this._logger?.trace('DefaultAgentSession.close();');\n if (this._connectPromise) {\n this._channel.unsubscribe(this._channelListener);\n }\n this._channel.off(this._onChannelStateChange);\n for (const reg of this._registeredRuns.values()) {\n reg.controller.abort();\n }\n this._registeredRuns.clear();\n this._runIdByInputCodecMessageId.clear();\n this._deferredCancels.clear();\n this._runManager.close();\n\n await bestEffortDetach(this._channel, this._connectPromise, this._logger, 'DefaultAgentSession');\n\n this._logger?.debug('DefaultAgentSession.close(); session closed');\n }\n\n // -------------------------------------------------------------------------\n // Cancel message routing\n // -------------------------------------------------------------------------\n\n private async _handleCancelMessage(msg: Ably.InboundMessage): Promise<void> {\n const headers = getTransportHeaders(msg);\n const runId = headers[HEADER_RUN_ID];\n const inputCodecMessageId = headers[HEADER_INPUT_CODEC_MESSAGE_ID];\n\n // Malformed cancel: drop with warn. A cancel must identify its target by\n // `run-id` (a continuation, whose run-id the client knows) and/or by\n // `input-codec-message-id` (a fresh send, before the agent minted the\n // run-id). Neither present means there is nothing to route to.\n if (!runId && !inputCodecMessageId) {\n this._logger?.warn('DefaultAgentSession._handleCancelMessage(); missing run-id and input-codec-message-id', {\n serial: msg.serial,\n });\n return;\n }\n\n // Primary path — match by run-id (continuations, whose run-id the client\n // already knows). Resolve the input-codec-message-id to a run-id when the\n // run-id wasn't supplied (a fresh-send cancel that arrived after the run's\n // input-event lookup resolved, so the linkage already exists).\n const resolvedRunId =\n runId ?? (inputCodecMessageId ? this._runIdByInputCodecMessageId.get(inputCodecMessageId) : undefined);\n const reg = resolvedRunId ? this._registeredRuns.get(resolvedRunId) : undefined;\n\n if (!reg) {\n // The run isn't known yet. A fresh-send cancel can race ahead of the\n // run's input-event lookup (which is what establishes the\n // input-codec-message-id → run linkage). Buffer it by\n // input-codec-message-id so `Run.start()` can pull and honour it once it\n // resolves the triggering input. A bare run-id cancel for an unknown run\n // is a no-op (the run never existed here, or already ended).\n if (inputCodecMessageId !== undefined) {\n this._bufferDeferredCancel(inputCodecMessageId, msg);\n }\n return;\n }\n\n await this._cancelRegistration(reg, msg);\n }\n\n /**\n * Buffer a cancel that arrived before its target run was known, keyed by the\n * triggering input's codec-message-id. FIFO-evicts the oldest entry at\n * {@link DEFERRED_CANCEL_LIMIT}. A later cancel for the same input replaces the earlier\n * one — the intent is identical.\n * @param inputCodecMessageId - The triggering input's codec-message-id.\n * @param msg - The raw cancel message (passed to `onCancel`).\n */\n private _bufferDeferredCancel(inputCodecMessageId: string, msg: Ably.InboundMessage): void {\n const evicted = evictOldestIfFull(this._deferredCancels, inputCodecMessageId, DEFERRED_CANCEL_LIMIT);\n if (evicted !== undefined) {\n this._logger?.warn('DefaultAgentSession._bufferDeferredCancel(); deferred-cancel buffer full, dropping oldest', {\n evictedInputCodecMessageId: evicted,\n limit: DEFERRED_CANCEL_LIMIT,\n });\n }\n this._deferredCancels.set(inputCodecMessageId, msg);\n this._logger?.debug('DefaultAgentSession._bufferDeferredCancel(); buffered early cancel', {\n inputCodecMessageId,\n serial: msg.serial,\n });\n }\n\n /**\n * Pull and honour a cancel buffered before this run was known. Called from\n * `Run.start()` once the input-event lookup resolves the run's triggering\n * input codec-message-id — the point at which the\n * `input-codec-message-id → run` linkage first exists. No-op when no cancel\n * was buffered for that input.\n * @param reg - The now-known run registration.\n * @param inputCodecMessageId - The run's resolved triggering input codec-message-id.\n */\n private async _pullDeferredCancel(reg: RegisteredRun, inputCodecMessageId: string): Promise<void> {\n const buffered = this._deferredCancels.get(inputCodecMessageId);\n if (buffered === undefined) return;\n this._deferredCancels.delete(inputCodecMessageId);\n this._logger?.debug('DefaultAgentSession._pullDeferredCancel(); honouring buffered cancel', {\n runId: reg.runId,\n inputCodecMessageId,\n });\n await this._cancelRegistration(reg, buffered);\n }\n\n /**\n * Fire a cancel against a known run: consult its `onCancel` authorization\n * hook (if any), then abort the run's controller. Shared by the run-id match,\n * the input-codec-message-id match, and the buffered-cancel pull so all three\n * honour `onCancel` and surface handler errors identically.\n * @param reg - The target run registration.\n * @param msg - The raw cancel message (passed to `onCancel`).\n */\n private async _cancelRegistration(reg: RegisteredRun, msg: Ably.InboundMessage): Promise<void> {\n const { runId } = reg;\n this._logger?.debug('DefaultAgentSession._cancelRegistration(); matched run', { runId });\n\n const request: CancelRequest = { message: msg, runId };\n\n try {\n if (reg.onCancel) {\n const allowed = await reg.onCancel(request);\n if (!allowed) {\n this._logger?.debug('DefaultAgentSession._cancelRegistration(); cancel rejected by onCancel', {\n runId,\n });\n return;\n }\n }\n reg.controller.abort();\n this._logger?.debug('DefaultAgentSession._cancelRegistration(); run cancelled', { runId });\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to process cancel for run ${runId}; onCancel handler threw: ${errorMessage(error)}`,\n ErrorCode.CancelListenerError,\n 500,\n errorCause(error),\n );\n this._logger?.error('DefaultAgentSession._cancelRegistration(); onCancel threw', { runId });\n (reg.onError ?? this._onError)?.(errInfo);\n }\n }\n\n // -------------------------------------------------------------------------\n // Channel state change handler\n // -------------------------------------------------------------------------\n\n // Spec: AIT-ST12, AIT-ST12a\n private _handleChannelStateChange(stateChange: Ably.ChannelStateChange): void {\n if (this._state === SessionState.CLOSED) return;\n\n const { current, resumed } = stateChange;\n\n // Track the initial attach so we don't treat it as a discontinuity.\n if (current === 'attached' && !this._hasAttachedOnce) {\n this._hasAttachedOnce = true;\n return;\n }\n\n if (!isContinuityLost(stateChange)) return;\n\n this._logger?.error('DefaultAgentSession._handleChannelStateChange(); channel continuity lost', {\n current,\n resumed,\n previous: stateChange.previous,\n });\n\n const continuityErr = continuityLostError(stateChange, 'continue');\n\n // Abort every active run's controller FIRST so in-flight\n // `loadConversation` / `findInputEvent` calls observe the abort before\n // the Tree changes underneath them and reject (InvalidArgument from their\n // signal checks; the session-level onError carries ChannelContinuityLost).\n for (const reg of this._registeredRuns.values()) {\n reg.controller.abort();\n }\n\n // Then swap the Tree for a fresh empty instance — abandons the old\n // Tree's projections, indices, and ably-message listeners to GC. New\n // runs use the fresh Tree; lingering closures on the old Tree from\n // in-flight (now-aborted) lookups are bounded by the abort propagation.\n this._tree = createTree<TInput, TOutput, TProjection>(\n this._codec,\n this._logger ?? makeLogger({ logLevel: LogLevel.Silent }),\n );\n this._applier = createWireApplier(this._tree, this._codec.createDecoder());\n // The AgentView holds the Tree/applier directly, so rebuild it against the\n // fresh pair — this also resets its cursor and exhaustion state.\n this._agentView = this._createAgentView();\n\n // Session-level notification: continuity loss is not scoped to any one\n // run. Per-run onError handlers are reserved for errors from that run's\n // own operations (publish failures, encoder errors).\n this._onError?.(continuityErr);\n }\n\n // -------------------------------------------------------------------------\n // Wire fold\n // -------------------------------------------------------------------------\n\n /**\n * Fold a single wire message into the session-owned Tree. Mirrors the\n * ClientSession's live decode loop — same engine, same fold path. The\n * applier decodes the message and applies the result to the Tree (or\n * routes lifecycle messages through `applyRunLifecycle`);\n * `emitAblyMessage` notifies Tree subscribers AND populates the event-id\n * index used by the AgentView's input-event lookup.\n *\n * A message that surfaces via more than one path (the live listener and\n * the AgentView's history walk) does not\n * double-fold: the shared decoder's version-guarded trackers drop\n * re-delivered stream content, and the Tree's per-entry `decodedThrough`\n * high-water-mark drops whole-wire replays (including stateless discrete\n * re-decodes) at the correct per-delivery granularity — same-serial live\n * appends each carry their own version and fold exactly once.\n * @param wire - The inbound Ably message to fold.\n */\n private _foldWire(wire: Ably.InboundMessage): void {\n this._applier.apply(wire);\n this._tree.emitAblyMessage(wire);\n }\n\n // -------------------------------------------------------------------------\n // Channel subscription handler\n // -------------------------------------------------------------------------\n\n private _handleChannelMessage(msg: Ably.InboundMessage): void {\n try {\n // Fold first (re-delivered content is dropped by the shared decoder's\n // version guard and the Tree's replay guard), then dispatch cancel\n // control messages.\n this._foldWire(msg);\n\n if (msg.name === EVENT_CANCEL) {\n // Fire-and-forget async handler — errors are caught internally.\n this._handleCancelMessage(msg).catch((error: unknown) => {\n const errInfo = new Ably.ErrorInfo(\n `unable to route cancel message; ${errorMessage(error)}`,\n ErrorCode.CancelListenerError,\n 500,\n errorCause(error),\n );\n this._logger?.error('DefaultAgentSession._handleChannelMessage(); cancel routing error');\n this._onError?.(errInfo);\n });\n return;\n }\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to process channel message; ${errorMessage(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n errorCause(error),\n );\n this._logger?.error('DefaultAgentSession._handleChannelMessage(); subscription error');\n this._onError?.(errInfo);\n }\n }\n\n // -------------------------------------------------------------------------\n // Connection guard\n // -------------------------------------------------------------------------\n\n private async _requireConnected(method: string): Promise<void> {\n return requireConnected(this._connectPromise, method);\n }\n\n // -------------------------------------------------------------------------\n // Run creation\n // -------------------------------------------------------------------------\n\n private _createRun(invocation: Invocation, runtime: RunRuntime<TOutput>): Run<TOutput, TProjection, TMessage> {\n // The run-id is not carried in the invocation body — the agent mints it.\n // Mint a provisional id now (or take the `runtime.runId` override for\n // tests / in-process drivers) — this IS the id for a fresh run. A\n // continuation overrides it in `Run.start()` with the existing run-id read\n // off the triggering input event's message headers (the run it re-enters).\n // Mirrors the invocationId mint below.\n let runId = runtime.runId ?? crypto.randomUUID();\n // Whether the run-id was supplied via the runtime override. Together with\n // `resolvedContinuation` (set in start() when the triggering input carries\n // a wire run-id) this decides whether the id is \"adopted\" — an adopted id\n // can name a run that already exists in channel history; a freshly-minted\n // UUID cannot, so hydration must not demand its node from history.\n const runIdOverridden = runtime.runId !== undefined;\n // The agent mints the invocation id — one per HTTP request that invokes\n // it. A per-run override (runtime.invocationId) supports deterministic ids\n // in tests and in-process drivers.\n const invocationId = runtime.invocationId ?? crypto.randomUUID();\n const inputEventLookupTimeoutMs = this._inputEventLookupTimeoutMs;\n const { onMessage, onCancelled, onCancel, onError: runOnError, signal: externalSignal } = runtime;\n\n const controller = new AbortController();\n let state = RunState.INITIALIZED;\n\n // Compose the internal controller signal with the external signal (e.g.\n // req.signal) so platform-level cancellation (request cancellation, function\n // timeout) cancels the run through the same path as Ably cancel messages.\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n\n // Spec: AIT-ST3a — register immediately so `close()` aborts an in-flight\n // start() and a post-lookup cancel can fire the AbortSignal. Keyed by the\n // provisional run-id; a continuation re-keys to the real id in start()\n // once the triggering input reveals it.\n const registration: RegisteredRun = {\n runId,\n invocationId,\n controller,\n signal,\n onCancel,\n onError: runOnError,\n };\n this._registeredRuns.set(runId, registration);\n\n // Capture instance members as locals so arrow functions close over them\n // without needing `this` (avoids unicorn/no-this-assignment).\n const logger = this._logger;\n const runManager = this._runManager;\n const codec = this._codec;\n const channel = this._channel;\n const registeredRuns = this._registeredRuns;\n const runIdByInputCodecMessageId = this._runIdByInputCodecMessageId;\n const deferredCancels = this._deferredCancels;\n const requireConnected = this._requireConnected.bind(this);\n // Live accessor (not a captured ref): a continuity-loss swap recreates the\n // AgentView, and reads after the swap must observe the fresh instance.\n const getAgentView = (): AgentView<TInput, TOutput, TProjection, TMessage> => this._agentView;\n const pullDeferredCancel = this._pullDeferredCancel.bind(this);\n const inputEventId = invocation.inputEventId;\n\n // Per-run metadata resolved from the input-event lookup result. The first\n // matched message message's headers carry the run's `clientId`, `parent`, and\n // `forkOf`, and — for a continuation — the `run-id` it re-enters (a fresh\n // input carries none; the client stamps a run-id only when re-entering a\n // run it already knows). Its Ably-level publisher `clientId` becomes the\n // `inputClientId` re-stamped on the agent's own publishes.\n let resolvedClientId: string | undefined;\n let resolvedInputClientId: string | undefined;\n let resolvedParent: string | undefined;\n let resolvedForkOf: string | undefined;\n let resolvedRegenerates: string | undefined;\n let resolvedInputCodecMessageId: string | undefined;\n let resolvedContinuation = false;\n let firstLookupHeaders: Record<string, string> | undefined;\n\n // `Run.view.messages` is a LIVE read against the session's Tree:\n // returns the trigger node's currently-folded messages, reflecting any\n // amendments (tool resolutions etc.) that have arrived since\n // `Run.start()`. No internal `viewMessages` array — the Tree is the\n // single source of truth. The trigger node may be an input node (fresh\n // send) or a reply run (continuation re-entry with run-id on the\n // triggering message); both expose a projection the codec can read.\n //\n // Resolved via an arrow accessor so the closure picks up `this._tree`\n // after a continuity-loss swap; capturing `this._tree` into a local at\n // run-creation time would silently keep returning data from the\n // abandoned Tree.\n const getTree = (): DefaultTree<TInput, TOutput, TProjection> => this._tree;\n const view: RunView<TMessage> = {\n get messages() {\n if (resolvedInputCodecMessageId === undefined) return [];\n const node = getTree().getNodeByCodecMessageId(resolvedInputCodecMessageId);\n if (!node) return [];\n const sourceSerial = node.kind === 'input' ? node.serial : node.startSerial;\n const sourceForkOf = node.kind === 'input' ? node.forkOf : undefined;\n return codec.getMessages(node.projection).map((m) => ({\n kind: 'message' as const,\n message: m.message,\n codecMessageId: m.codecMessageId,\n parentId: node.parentCodecMessageId,\n forkOf: sourceForkOf,\n headers: {},\n serial: sourceSerial,\n }));\n },\n };\n /**\n * The reply run's structural-parent fallback, computed once in\n * `Run.start()` once the input-event lookup resolves the triggering\n * input's codec-message-id, and consumed by every `Run.pipe()` publish.\n * A per-stream `streamOpts.parent` still overrides it. Storing it here\n * keeps it stable across pipes and decouples the assistant's structural\n * parent from the run-start message's own `parent`.\n */\n let assistantParentFallback: string | undefined;\n /**\n * Remove this run from the session's routing maps. Drops the\n * `_registeredRuns` entry plus the `input-codec-message-id → run-id`\n * reverse index (and any stale deferred cancel still buffered for that\n * input), keeping the cancel-routing state consistent when the run ends,\n * suspends, or its start fails.\n */\n const deregisterRun = (): void => {\n registeredRuns.delete(runId);\n if (resolvedInputCodecMessageId !== undefined) {\n runIdByInputCodecMessageId.delete(resolvedInputCodecMessageId);\n deferredCancels.delete(resolvedInputCodecMessageId);\n }\n };\n\n /**\n * Run a run-lifecycle publish (run-start / run-suspend / run-end) and wrap\n * any failure as a `RunLifecycleError`, logging at error and rethrowing.\n * Shared by start(), suspend(), and end() so the three publishes can't\n * drift on the error code, message shape, or cause preservation.\n * @param phase - The lifecycle wire phase, used in the error message.\n * @param method - The Run method name, used in the log prefix.\n * @param publish - The RunManager publish to run.\n */\n const publishLifecycle = async (\n phase: 'run-start' | 'run-suspend' | 'run-end',\n method: 'start' | 'suspend' | 'end',\n publish: () => Promise<void>,\n ): Promise<void> => {\n try {\n await publish();\n } catch (error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to publish ${phase} for run ${runId}; ${errorMessage(error)}`,\n ErrorCode.RunLifecycleError,\n 500,\n errorCause(error),\n );\n logger?.error(`Run.${method}(); failed to publish ${phase}`, { runId });\n throw errInfo;\n }\n };\n\n const run: Run<TOutput, TProjection, TMessage> = {\n get runId() {\n return runId;\n },\n get invocationId() {\n return invocationId;\n },\n get abortSignal() {\n return signal;\n },\n get view() {\n return view;\n },\n get messages() {\n // Always derive live from the Tree via the AgentView. Walks the parent\n // chain from the run's structural-parent anchor and concatenates each\n // ancestor's projection, then appends the current reply run's messages\n // at the tail. Uses `assistantParentFallback` (which falls back to the\n // input message's `parent` for regenerate carriers whose own\n // codec-message-id has no Tree node) — same anchor `loadConversation`\n // uses, and passes `resolvedRegenerates` so a regenerate's history\n // stops before the message being replaced. No cache: every read\n // reflects the latest folded state. `getAgentView()` dereferences the\n // live AgentView so a continuity-loss swap is observed instead of\n // returning stale data from the abandoned tree.\n return getAgentView().messages(runId, assistantParentFallback, resolvedRegenerates);\n },\n\n // Spec: AIT-ST4, AIT-ST4a, AIT-ST4b\n start: async (): Promise<void> => {\n logger?.trace('Run.start();', { runId, inputEventId });\n\n await requireConnected('start');\n\n // Spec: AIT-ST4a\n if (signal.aborted) {\n throw new Ably.ErrorInfo(\n `unable to start run; run ${runId} was cancelled before start()`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n if (state !== RunState.INITIALIZED) return;\n state = RunState.STARTED;\n\n // Look up the triggering input event on the channel so the agent\n // can read the user's message and per-run metadata (parent, forkOf,\n // continuation flag) before publishing run-start. Skip when\n // inputEventLookupTimeoutMs === 0 (tests and in-process drivers) or\n // when no inputEventId is set (invocation requires no channel lookup).\n if (inputEventId && inputEventLookupTimeoutMs > 0) {\n try {\n const found = await getAgentView().findInputEvent({\n invocationId,\n runId,\n expectedEventIds: [inputEventId],\n timeoutMs: inputEventLookupTimeoutMs,\n signal,\n });\n if (found.firstHeaders !== undefined) firstLookupHeaders = found.firstHeaders;\n if (found.firstClientId !== undefined) resolvedInputClientId = found.firstClientId;\n } catch (error) {\n const errInfo =\n error instanceof Ably.ErrorInfo\n ? error\n : new Ably.ErrorInfo(\n `unable to look up input event; ${errorMessage(error)}`,\n ErrorCode.InputEventNotFound,\n 504,\n );\n // The rejection bubbles up to the developer's HTTP handler,\n // which surfaces the failure as a non-2xx response — that is\n // the signal the client sees. No channel publish: an\n // `ai-run-end` without a preceding `ai-run-start` would break\n // the lifecycle invariant for other channel observers.\n deregisterRun();\n logger?.error('Run.start(); input-event lookup failed', { runId, invocationId });\n throw errInfo;\n }\n }\n\n // Resolve per-run metadata from the first matched message message's\n // headers — they carry `clientId`, `parent`, and `forkOf`.\n // Continuations of a suspended run pick up the suspended assistant's\n // parent in the same headers (the continuation message parents off\n // the assistant). A `run-id` on the triggering input marks a\n // continuation (re-entry via `ai-run-resume`); a fresh input carries\n // none and opens the run with `ai-run-start`.\n const sourceHeaders = firstLookupHeaders;\n if (sourceHeaders) {\n resolvedClientId = sourceHeaders[HEADER_RUN_CLIENT_ID];\n resolvedParent = sourceHeaders[HEADER_PARENT];\n resolvedForkOf = sourceHeaders[HEADER_FORK_OF];\n resolvedRegenerates = sourceHeaders[HEADER_MSG_REGENERATE];\n resolvedInputCodecMessageId = sourceHeaders[HEADER_CODEC_MESSAGE_ID];\n\n // The triggering input's run-id (if any) IS this run's identity.\n // Present → a continuation re-entering that run: adopt the id,\n // overriding the provisional one minted at construction, and re-key\n // the registration so cancel routing / deregistration resolve to the\n // real run. Absent → a fresh run: the provisional id stands and the\n // run opens with run-start.\n const wireRunId = sourceHeaders[HEADER_RUN_ID];\n resolvedContinuation = wireRunId !== undefined;\n if (wireRunId !== undefined && wireRunId !== runId) {\n registeredRuns.delete(runId);\n runId = wireRunId;\n registration.runId = runId;\n registeredRuns.set(runId, registration);\n }\n }\n\n // Compute the reply run's structural-parent fallback: the triggering\n // user message's codec-message-id ONLY if that codec-message-id is\n // backed by a real node in the Tree (i.e. the message decoded into at\n // least one input event); otherwise — for regenerate carriers that\n // are wire-only signals with no input events — fall back to the\n // input message's own `parent` header.\n assistantParentFallback =\n resolvedInputCodecMessageId !== undefined &&\n this._tree.getNodeByCodecMessageId(resolvedInputCodecMessageId) !== undefined\n ? resolvedInputCodecMessageId\n : resolvedParent;\n\n // The triggering input's codec-message-id is now resolved, so the\n // `input-codec-message-id → run` linkage exists: index it for live\n // cancels and pull any cancel that arrived before the run was known\n // (a fresh-send cancel published before the agent minted this run-id).\n // Honouring it here may abort the controller before run-start; that is\n // fine — the abort propagates through the same signal a normal cancel\n // would use.\n if (resolvedInputCodecMessageId !== undefined) {\n runIdByInputCodecMessageId.set(resolvedInputCodecMessageId, runId);\n await pullDeferredCancel(registration, resolvedInputCodecMessageId);\n }\n\n await publishLifecycle('run-start', 'start', async () =>\n runManager.startRun(runId, resolvedClientId, controller, {\n // Stamp the reply run's STRUCTURAL parent (its input node, M_user) —\n // the same value the output path stamps — not the input message's own\n // parent. Makes `parent` structural on every message so the Tree's two\n // creation paths agree regardless of arrival order. Valid only now\n // that M_user is a separate input node (the two-node flip).\n parent: assistantParentFallback,\n forkOf: resolvedForkOf,\n regenerates: resolvedRegenerates,\n invocationId,\n inputClientId: resolvedInputClientId,\n inputCodecMessageId: resolvedInputCodecMessageId,\n continuation: resolvedContinuation,\n }),\n );\n\n // Optimistically insert the fresh run's node into the session Tree so\n // reads that follow start() (loadConversation, Run.messages) see the\n // run immediately rather than depending on the channel echo of the\n // run-start just published. The echo (or a history fold) reconciles\n // through the Tree's run-start handling, promoting startSerial onto\n // this serial-less node. Continuations re-enter an existing run via\n // run-resume, which creates no structure — their node comes from\n // history hydration instead.\n if (!resolvedContinuation) {\n getTree().applyRunLifecycle({\n type: 'start',\n runId,\n clientId: resolvedClientId ?? '',\n serial: undefined,\n invocationId,\n ...(assistantParentFallback !== undefined && { parent: assistantParentFallback }),\n ...(resolvedForkOf !== undefined && { forkOf: resolvedForkOf }),\n ...(resolvedRegenerates !== undefined && { regenerates: resolvedRegenerates }),\n });\n }\n\n logger?.debug('Run.start(); run started', { runId, inputEventId });\n },\n\n loadConversation: async (options?: LoadConversationOptions): Promise<TMessage[]> => {\n logger?.trace('Run.loadConversation();', { runId });\n await requireConnected('loadConversation');\n // No cache. Drives Tree hydration via the AgentView's conversation walk\n // and computes a fresh snapshot of the parent-chain messages at\n // return time. After this call, `Run.messages` continues to work\n // as a live Tree read.\n const { messages } = await getAgentView().loadConversation(\n runId,\n assistantParentFallback,\n signal,\n options?.maxRuns,\n runIdOverridden || resolvedContinuation,\n resolvedRegenerates,\n );\n return messages;\n },\n\n // Spec: AIT-ST6, AIT-ST6a, AIT-ST6b, AIT-ST6b1, AIT-ST6b2, AIT-ST6b3, AIT-ST6c\n pipe: async (stream: ReadableStream<TOutput>, streamOpts?: PipeOptions<TOutput>): Promise<StreamResult> => {\n logger?.trace('Run.pipe();', { runId });\n\n await requireConnected('pipe');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to pipe stream; start() must be called before pipe() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n const runOwnerClientId = runManager.getClientId(runId);\n\n // The assistant message's parent: an explicit per-stream\n // `streamOpts.parent` from the caller, else the reply run's\n // structural-parent fallback computed once at run-start\n // (`assistantParentFallback` — the triggering user message, or the\n // input message's own parent for regenerate messages that produced no\n // MessageNodes). Owning the default here means agent routes don't have\n // to pass `{ parent: lastUserCodecMessageId }` to keep tree threading\n // correct; edit-then-regenerate sibling resolution relies on the\n // user→assistant chain being explicit.\n const assistantParent = streamOpts?.parent ?? assistantParentFallback;\n const assistantForkOf = streamOpts?.forkOf ?? resolvedForkOf;\n // Echo `msg-regenerate` on the assistant message so that a\n // client receiving the assistant chunk before `ai-run-start`\n // (e.g. via history pagination across a page boundary, or a lost\n // lifecycle publish) can still populate `RunNode.regeneratesCodecMessageId`\n // when creating the Run from headers. Mirrors the symmetric\n // behaviour for `assistantForkOf` on edit runs.\n const assistantRegenerates = resolvedRegenerates;\n\n const codecMessageId = crypto.randomUUID();\n const defaultHeaders = buildTransportHeaders({\n role: 'assistant',\n runId,\n codecMessageId,\n runClientId: runOwnerClientId,\n parent: assistantParent,\n forkOf: assistantForkOf,\n invocationId,\n inputClientId: resolvedInputClientId,\n inputCodecMessageId: resolvedInputCodecMessageId,\n regenerates: assistantRegenerates,\n });\n const encoder = codec.createEncoder(channel, {\n extras: { headers: defaultHeaders },\n onMessage,\n messageId: codecMessageId,\n });\n\n const result = await pipeStream(stream, encoder, signal, onCancelled, streamOpts?.resolveWriteOptions, logger);\n\n if (result.error) {\n const errInfo = new Ably.ErrorInfo(\n `unable to pipe response for run ${runId}; ${result.error.message}`,\n ErrorCode.StreamError,\n 500,\n errorCause(result.error),\n );\n logger?.error('Run.pipe(); stream error', { runId });\n runOnError?.(errInfo);\n }\n\n // Run cancellation is transport-tier: guarantee the run-end terminal so\n // every observer's stream closes even if the caller's handler omits\n // run.end(). Best-effort — pipe must still return the StreamResult; a\n // later run.end() is a no-op via the ENDED guard. The run is past\n // INITIALIZED here (pipe requires start()), so end()'s guards pass.\n if (result.reason === 'cancelled') {\n try {\n await run.end({ reason: 'cancelled' });\n } catch {\n logger?.error('Run.pipe(); run-end on cancel failed', { runId });\n }\n }\n\n logger?.debug('Run.pipe(); stream finished', { runId, reason: result.reason });\n return result;\n },\n\n suspend: async (): Promise<void> => {\n logger?.trace('Run.suspend();', { runId });\n\n await requireConnected('suspend');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to suspend run; start() must be called before suspend() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n // ENDED is the terminal state for either an end or a suspend on this\n // Run instance; a second terminal call is a no-op.\n if (state === RunState.ENDED) return;\n state = RunState.ENDED;\n\n try {\n await publishLifecycle('run-suspend', 'suspend', async () =>\n runManager.suspendRun(runId, invocationId, resolvedInputClientId, resolvedInputCodecMessageId),\n );\n } finally {\n deregisterRun();\n }\n\n logger?.debug('Run.suspend(); run suspended', { runId });\n },\n\n // Spec: AIT-ST7, AIT-ST7a, AIT-ST7b\n end: async (params: RunEndParams): Promise<void> => {\n const { reason } = params;\n const error = params.reason === 'error' ? params.error : undefined;\n logger?.trace('Run.end();', { runId, reason });\n\n await requireConnected('end');\n\n if (state === RunState.INITIALIZED) {\n throw new Ably.ErrorInfo(\n `unable to end run; start() must be called before end() (run ${runId})`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n if (state === RunState.ENDED) return;\n state = RunState.ENDED;\n\n try {\n await publishLifecycle('run-end', 'end', async () =>\n runManager.endRun(runId, reason, invocationId, resolvedInputClientId, resolvedInputCodecMessageId, error),\n );\n } finally {\n deregisterRun();\n }\n\n logger?.debug('Run.end(); run ended', { runId, reason });\n },\n };\n\n return run;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an agent (server-side) session bound to the given Realtime client\n * and channel name. The caller owns the client's lifecycle; the session\n * owns its channel.\n * @param options - Session configuration.\n * @returns A new {@link AgentSession} instance.\n */\nexport const createAgentSession = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n options: AgentSessionOptions<TInput, TOutput, TProjection, TMessage>,\n): AgentSession<TOutput, TProjection, TMessage> => new DefaultAgentSession(options);\n","/**\n * Invocation — value object wrapping the JSON body that a client sends to\n * an agent's HTTP endpoint to start a run.\n *\n * The data shape is the wire contract; the {@link Invocation} class is a\n * runtime view of that data with the same fields. {@link Invocation.fromJSON}\n * is the entry point used by agent handlers:\n *\n * ```ts\n * const data = (await req.json()) as InvocationData;\n * const invocation = Invocation.fromJSON(data);\n * const run = session.createRun(invocation, { signal: req.signal });\n * await run.start();\n * await run.loadConversation(); // walk channel history into the session tree\n * const messages = run.messages;\n * ```\n *\n * The body carries only what the agent needs out-of-band before the channel\n * is observable: the session/channel name and the `inputEventId` that triggered\n * the invocation. The agent mints the `invocationId` itself (one per HTTP\n * request) and returns it on the HTTP response, so it is not a body field. Run\n * identity also lives on the channel: the agent mints the `runId` for a fresh\n * run and reads the existing `runId` off the triggering input event for a\n * continuation — so the body carries no run-id either. Per-message metadata —\n * `clientId`, `parent`, `forkOf`, continuation status — likewise lives on the\n * channel and is resolved by the agent from the triggering input event, not\n * from the body. The `inputClientId` the agent re-stamps on its own publishes\n * comes from the publisher's Ably `clientId` on the matched input event, not\n * from a body field.\n */\n\n// ---------------------------------------------------------------------------\n// Wire shape\n// ---------------------------------------------------------------------------\n\n/**\n * Wire shape of a single invocation — the JSON body sent from the client\n * transport's HTTP POST to the agent endpoint.\n */\nexport interface InvocationData {\n /**\n * Identifier for the specific input event on the channel that triggered\n * this invocation. The agent locates the event via the `event-id`\n * header. Its wire headers carry the run-id for a continuation (absent for\n * a fresh run), so run identity is resolved from the channel, not the body.\n */\n inputEventId: string;\n /** Logical name of the session (chat) — used as the Ably channel name. */\n sessionName: string;\n}\n\n// ---------------------------------------------------------------------------\n// Runtime view\n// ---------------------------------------------------------------------------\n\n/**\n * Runtime view of an {@link InvocationData}. Constructed via\n * {@link Invocation.fromJSON}. Read-only; carries no behaviour beyond\n * exposing its fields.\n */\n// Spec: AIT-ST13\nexport class Invocation {\n /**\n * Identifier for the specific input event on the channel that triggered\n * this invocation. Run identity is resolved from that event's wire headers\n * (or minted), not from the body.\n */\n readonly inputEventId: string;\n /** Logical name of the session (chat). Used as the Ably channel name. */\n readonly sessionName: string;\n\n private constructor(data: InvocationData) {\n this.inputEventId = data.inputEventId;\n this.sessionName = data.sessionName;\n }\n\n /**\n * Build an Invocation from its JSON wire shape.\n * @param data - Parsed JSON body matching {@link InvocationData}.\n * @returns A new Invocation exposing the same fields.\n */\n static fromJSON(data: InvocationData): Invocation {\n return new Invocation(data);\n }\n\n /**\n * Serialise this invocation to its JSON wire shape — the body a client\n * POSTs to the agent's endpoint to wake a run. Round-trips through\n * {@link Invocation.fromJSON}.\n * @returns The {@link InvocationData} carrying this invocation's identity.\n */\n toJSON(): InvocationData {\n return {\n inputEventId: this.inputEventId,\n sessionName: this.sessionName,\n };\n }\n}\n","/**\n * loadHistory — load conversation history from an Ably channel and return\n * the raw Ably messages as a paginated HistoryPage result.\n *\n * This does NOT decode: it pages back through Ably history via the shared\n * {@link loadHistoryPages} primitive until `limit` complete messages are\n * present, then hands the raw Ably messages (oldest-first) to the caller.\n * The View re-decodes them into the Tree itself, so load-history only needs\n * a cheap, header-based completion counter to decide when to stop paging.\n *\n * The `limit` option controls the number of complete **messages** per page,\n * not the number of Ably messages fetched. A message is complete when\n * its terminal wire signal — `status: \"complete\"` / `\"cancelled\"`, or a\n * `discrete` create — has been seen. Runs that span a page boundary are\n * handled by the counter requiring both a start and a terminal signal\n * before counting a message complete.\n *\n * Because Ably history returns newest-first, each page's `rawMessages` are\n * reversed to chronological (oldest-first) so the caller can fold them in\n * order.\n *\n * Spec: AIT-CT11, AIT-CT11b.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_CODEC_MESSAGE_ID, HEADER_DISCRETE, HEADER_STATUS, HEADER_STREAM } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport { type HistoryPagesCursor, loadHistoryPages } from './load-history-pages.js';\nimport type { HistoryPage, LoadHistoryOptions } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Shared state across pages within one history traversal\n// ---------------------------------------------------------------------------\n\ninterface HistoryState {\n /** Cursor over the shared `loadHistoryPages` primitive; each `next()` returns one Ably page's messages (newest-first within the page). */\n cursor: HistoryPagesCursor;\n /** All raw Ably messages collected so far, in newest-first order (as received from Ably). */\n rawMessages: Ably.InboundMessage[];\n /**\n * How many complete messages have been served to the consumer so far.\n * Drives the buffered-page logic: when a single fetch gathers more than\n * `limit` completions, later pages are served from the buffer without\n * fetching, advancing this counter `limit` at a time.\n */\n returnedCount: number;\n /** How many raw Ably messages have been served to the consumer so far. */\n returnedRawCount: number;\n /**\n * `codec-message-id`s for which a start signal has been seen: any\n * `message.create` / `message.update` / `message.append` with\n * `stream: \"true\"` (the decoder establishes a tracker via create or\n * first-contact), or a `message.create` carrying `discrete` (a discrete\n * message, created and terminated in one Ably message).\n */\n startedCodecMessageIds: Set<string>;\n /**\n * `codec-message-id`s with a terminal wire signal: either `discrete`\n * on a `message.create` (discrete message) or `status: \"complete\"`\n * / `\"cancelled\"` on any action (closed stream).\n */\n terminatedCodecMessageIds: Set<string>;\n /**\n * `codec-message-id`s that are both started AND terminated — counted as\n * complete. The fetch loop reads this set's size to decide when to stop\n * paging. Maintained incrementally by {@link countNewCompletions}. Grows\n * monotonically.\n */\n completedCodecMessageIds: Set<string>;\n logger: Logger;\n}\n\n// ---------------------------------------------------------------------------\n// Incremental completion counting (header scan, no decode)\n// ---------------------------------------------------------------------------\n\n/**\n * Scan newly-added raw messages and track which `codec-message-id`s have\n * become complete. Used by {@link fetchUntilLimit} to decide when enough\n * completed messages have been collected, without running the decoder.\n *\n * A codec-message-id is considered complete only when BOTH of these have been seen:\n * - a \"start\" signal: either `discrete` on a `message.create`\n * (discrete messages are created and terminated by the same Ably message\n * message), OR any `message.create` / `message.update` / `message.append`\n * with `stream: \"true\"` (the decoder establishes a tracker via\n * create or first-contact).\n * - a \"terminal\" signal: `discrete` on the create, or\n * `status: \"complete\"` / `\"cancelled\"` on any later action.\n *\n * Why update and append count as starts: Ably history can compact a live\n * `create + append + ... + append{status:complete}` sequence into a single\n * `message.update` with `STREAM=true` and `STATUS=complete`. The decoder\n * handles that via first-contact. Counting only `message.create` as a start\n * would cause the fetch loop to page past a compacted run without ever\n * marking it complete.\n *\n * Requiring both halves matters when a streaming run spans a page\n * boundary: the terminal arrives in the newer page (fetched first) while\n * the start sits in an older page. Counting the terminal alone would stop\n * the fetch loop prematurely - the decoder would have no stream state to\n * resolve, and the message wouldn't make it into the result.\n *\n * Messages skipped for counting:\n * - Missing `codec-message-id`: lifecycle events not tied to a domain message.\n * - `message.delete`: clears the tracker, doesn't produce output.\n *\n * Amend-class Ably messages (events targeting an existing message via\n * `HEADER_CODEC_MESSAGE_ID`) flow through the same counter — the Sets naturally\n * dedup so a tool-output amend on an already-seen codec-message-id is idempotent.\n *\n * Known edge case: if Ably history is truncated and a terminal survives\n * while every start signal for its codec-message-id has rolled off, the counter will\n * never mark that `codec-message-id` complete. The loop keeps fetching until it runs\n * out of pages, then returns whatever raw messages it collected.\n * @param state - The shared history traversal state.\n * @param newMessages - The Ably messages just pushed onto `state.rawMessages`.\n */\nconst countNewCompletions = (state: HistoryState, newMessages: readonly Ably.InboundMessage[]): void => {\n for (const msg of newMessages) {\n const headers = getTransportHeaders(msg);\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n if (!codecMessageId) continue;\n\n const action = msg.action;\n const isDiscreteCreate = action === 'message.create' && HEADER_DISCRETE in headers;\n // Any content-producing action on a streamed serial counts as a start:\n // the decoder uses create or first-contact (update/append) to establish\n // its tracker. Delete clears tracker state and emits nothing, so it\n // never counts as a start.\n const hasStreamContent =\n headers[HEADER_STREAM] === 'true' &&\n (action === 'message.create' || action === 'message.update' || action === 'message.append');\n const status = headers[HEADER_STATUS];\n const isTerminal = status === 'complete' || status === 'cancelled';\n\n if (isDiscreteCreate || hasStreamContent) state.startedCodecMessageIds.add(codecMessageId);\n if (isDiscreteCreate || isTerminal) state.terminatedCodecMessageIds.add(codecMessageId);\n if (state.startedCodecMessageIds.has(codecMessageId) && state.terminatedCodecMessageIds.has(codecMessageId)) {\n state.completedCodecMessageIds.add(codecMessageId);\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Pull pages from the shared iterator until we have enough completions\n// ---------------------------------------------------------------------------\n\n/**\n * Pull chunks from the shared {@link loadHistoryPages} iterator until enough\n * completed messages have been collected (target = already-returned +\n * `limit`) or the iterator is exhausted.\n *\n * Uses {@link countNewCompletions} — a cheap O(new messages) header scan —\n * to decide when to stop, rather than running the decoder per page.\n * @param state - The shared history traversal state.\n * @param limit - Target number of completed messages beyond what has already been returned.\n */\nconst fetchUntilLimit = async (state: HistoryState, limit: number): Promise<void> => {\n const target = state.returnedCount + limit;\n while (state.completedCodecMessageIds.size < target && state.cursor.hasNext()) {\n state.logger.debug('loadHistory.fetchUntilLimit(); pulling next page', {\n collected: state.rawMessages.length,\n completed: state.completedCodecMessageIds.size,\n });\n const chunk = await state.cursor.next();\n if (!chunk) break;\n state.rawMessages.push(...chunk);\n countNewCompletions(state, chunk);\n }\n};\n\n// ---------------------------------------------------------------------------\n// Build HistoryPage result from current state\n// ---------------------------------------------------------------------------\n\n/**\n * Build a HistoryPage of raw Ably messages from the current fetch state.\n * @param state - The shared history traversal state.\n * @param limit - Max complete messages per page.\n * @returns A page of raw history messages with a `next()` cursor.\n */\nconst buildResult = (state: HistoryState, limit: number): HistoryPage => {\n // Advance the served-completion counter by up to `limit`, mirroring the\n // page granularity the consumer asked for. `rawMessages` for this page are\n // all messages fetched since the previous page (empty for buffered pages).\n const totalCompleted = state.completedCodecMessageIds.size;\n const served = Math.min(limit, Math.max(0, totalCompleted - state.returnedCount));\n state.returnedCount += served;\n\n const moreCompleted = totalCompleted > state.returnedCount;\n const moreFromCursor = state.cursor.hasNext();\n\n // Raw Ably messages for this page in chronological order (oldest first).\n const newRawCount = state.rawMessages.length - state.returnedRawCount;\n const rawMessages = newRawCount > 0 ? state.rawMessages.slice(state.returnedRawCount).toReversed() : [];\n state.returnedRawCount = state.rawMessages.length;\n\n return {\n rawMessages,\n hasNext: () => moreCompleted || moreFromCursor,\n next: async () => {\n if (moreCompleted) {\n return buildResult(state, limit);\n }\n if (!moreFromCursor) return;\n await fetchUntilLimit(state, limit);\n return buildResult(state, limit);\n },\n };\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Load conversation history from a channel and return the raw Ably messages.\n *\n * Drives the shared {@link loadHistoryPages} primitive with\n * `untilAttach: true` (gapless continuity with the live subscription) and a\n * Ably message page limit that overprovisions for the many-Ably-messages-per-domain-message\n * ratio. Each `HistoryPage` returned covers up to `limit` complete domain\n * messages; subsequent pages drain over-collected messages from the buffer\n * before pulling more from the iterator.\n *\n * The `limit` option controls the number of complete messages returned per\n * page, not the number of Ably messages fetched.\n * @param channel - The Ably channel to load history from.\n * @param options - Pagination options.\n * @param logger - Logger for diagnostic output.\n * @returns The first page of raw history messages.\n */\n// Spec: AIT-CT11, AIT-CT11b\nexport const loadHistory = async (\n channel: Ably.RealtimeChannel,\n options: LoadHistoryOptions | undefined,\n logger: Logger,\n): Promise<HistoryPage> => {\n const limit = options?.limit ?? 100;\n\n logger.trace('loadHistory();', { limit });\n\n // Request more Ably messages per page than the domain-message limit to\n // account for the many-to-one ratio (multiple Ably messages per domain message).\n // The wire pagination is internal to the iterator; the consumer-visible\n // pagination is by complete domain messages.\n const wireLimit = limit * 10;\n\n const cursor = await loadHistoryPages(channel, {\n pageLimit: wireLimit,\n untilAttach: true,\n logger,\n });\n\n const state: HistoryState = {\n cursor,\n rawMessages: [],\n returnedCount: 0,\n returnedRawCount: 0,\n startedCodecMessageIds: new Set<string>(),\n terminatedCodecMessageIds: new Set<string>(),\n completedCodecMessageIds: new Set<string>(),\n logger,\n };\n\n await fetchUntilLimit(state, limit);\n return buildResult(state, limit);\n};\n","/**\n * DefaultView — a paginated, branch-aware projection over the Tree.\n *\n * Wraps a Tree (RunNode-keyed) and manages a pagination window that controls\n * which Runs are visible to the UI. New live Runs appear immediately; older\n * Runs are revealed progressively via `loadOlder()`.\n *\n * `getMessages()` reads the Tree's visible node chain (input nodes + reply\n * runs, with sibling selection applied) and concatenates each node's\n * `codec.getMessages(node.projection)` to produce the flat\n * `CodecMessage<TMessage>[]` the UI renders.\n *\n * Each View owns its own branch selection state and pagination window,\n * allowing multiple independent Views over the same Tree.\n *\n * Events are scoped to the visible window — 'update' only fires when the\n * visible output changes, 'ably-message' only for messages corresponding to\n * visible Runs, and 'run' only for runs with visible content.\n */\n\nimport * as Ably from 'ably';\n\nimport { HEADER_CODEC_MESSAGE_ID, HEADER_RUN_ID } from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport type { Logger } from '../../logger.js';\nimport { getTransportHeaders } from '../../utils.js';\nimport type { Codec, CodecInputEvent, CodecMessage, CodecOutputEvent } from '../codec/types.js';\nimport type { WireApplier } from './decode-fold.js';\nimport { loadHistory } from './load-history.js';\nimport { nodeKey, type TreeInternal } from './tree.js';\nimport type {\n ActiveRun,\n BranchSelection,\n ConversationNode,\n HistoryPage,\n OutputEvent,\n RunInfo,\n RunLifecycleEvent,\n RunNode,\n SendOptions,\n View,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Events map\n// ---------------------------------------------------------------------------\n\ninterface ViewEventsMap {\n update: undefined;\n 'ably-message': Ably.InboundMessage;\n run: RunLifecycleEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Send delegate\n// ---------------------------------------------------------------------------\n\n/**\n * Internal delegate function provided by the session for executing sends.\n * The View pre-computes the visible branch's flat message list and the\n * codec-message-id of its tail (for auto-parent routing) before calling\n * the delegate, so the delegate has no back-reference to the View.\n *\n * Each TInput carries its own routing metadata (`parent` / `target` /\n * `codecMessageId`) via the {@link CodecInputEvent} base; the delegate\n * reads those fields directly without runtime classification.\n *\n * `parentCodecMessageId` is the codec-message-id of the last message in\n * the visible branch (extracted from the tail Run's projection per codec\n * convention), or `undefined` for an empty conversation. The session\n * uses it as the auto-parent for fresh user messages.\n */\nexport type SendDelegate<TInput extends CodecInputEvent> = (\n input: TInput[],\n options: SendOptions | undefined,\n parentCodecMessageId: string | undefined,\n) => Promise<ActiveRun>;\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for creating a View. */\ninterface ViewOptions<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage> {\n /** The tree to project. */\n tree: TreeInternal<TInput, TOutput, TProjection>;\n /** The Ably channel to load history from. */\n channel: Ably.RealtimeChannel;\n /** The codec used to project messages and mint regenerate inputs. */\n codec: Codec<TInput, TOutput, TProjection, TMessage>;\n /**\n * The Tree's single decode-and-apply engine, owned by the session and\n * shared with the live decode loop. History replay feeds pages through it\n * so the one decoder instance sees every route into the Tree.\n */\n applier: WireApplier;\n /** Delegate for executing sends through the session. */\n sendDelegate: SendDelegate<TInput>;\n /** Logger for diagnostic output. */\n logger: Logger;\n /** Called when the view is closed, allowing the owner to clean up references. */\n onClose?: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Branch selection\n// ---------------------------------------------------------------------------\n\n/**\n * Internal tagged union representing why a branch was selected for an\n * edit-fork group. Stored per group-root runId in the View's\n * `_branchSelections` map. Not the public-facing {@link BranchSelection}\n * — that's a UI-facing bundle returned by `view.branchSelection(id)`.\n */\ntype BranchSelectionState =\n /** Explicit navigation via `selectSibling()`. The selected input-node key. */\n | { kind: 'user'; selectedKey: string }\n /** This view initiated an edit fork — auto-selected the new input node. */\n | { kind: 'auto'; selectedKey: string }\n /** An external fork appeared — pinned to the currently-visible sibling to prevent drift. */\n | { kind: 'pinned'; selectedKey: string };\n\n/**\n * Selection state for a regenerate group. Keyed by the anchor codec-message-id (the\n * assistant codec-message-id being regenerated). Distinct from {@link BranchSelectionState}\n * because regenerate groups are message-level (group members share an\n * anchor codec-message-id), not edit forks of the user prompt.\n *\n * Unlike fork-of groups, regenerate groups do not \"pin to current visible\"\n * when a new member appears externally — the default for a regenerate\n * slot is always the latest member, so an external regenerator auto-rolls\n * forward unless the user has explicitly selected an earlier member.\n */\ntype RegenSelection =\n /** Explicit navigation via `selectSibling()`. The selected reply-run id. */\n | { kind: 'user'; selectedRunId: string }\n /** This view initiated a regenerate — auto-selected the new reply run when it arrived. */\n | { kind: 'auto'; selectedRunId: string }\n /**\n * This view's `regenerate()` is in flight. Keyed (in `_regenSelections`) by\n * the regenerate group's root; `carrierCodecMessageId` is the regenerate\n * carrier event's id, used to recognise the new reply run when it appears.\n */\n | { kind: 'pending'; carrierCodecMessageId: string };\n\n/**\n * One alternative inside a {@link MessageBranchPoint}. The representative is the\n * member's own head message for fork-of and whole-reply regen groups, but the\n * *regenerate target* (a non-head message) for a non-head regen group - so it is\n * tracked explicitly rather than re-derived from the node's head.\n */\ninterface BranchMember {\n /**\n * The member node's `nodeKey` (tree.ts): a runId for a reply/regenerator run,\n * a codecMessageId for an input node. Matched by `_resolveSelectedIndex`.\n */\n memberNodeKey: string;\n /** The codec-message-id rendered in this member's branch-arrow slot. */\n representativeCodecMessageId: string;\n}\n\n/**\n * A resolved branch point: the group `kind` plus the member alternatives.\n *\n * Terms: \"regenerate target\" = the message being replaced; \"regenerator run\" =\n * the run that replaces it; \"non-head message\" = any message after a run's\n * first (index > 0, includes the tail).\n *\n * The three kinds, by anchor:\n * - `fork-of` — edit-style branch anchored at the user input node; members are\n * the alternate prompts (input-node sibling group).\n * - `regen` — whole-reply regenerate branch anchored at the assistant slot;\n * members are the original reply + its regenerator runs (same-input-node\n * sibling reply runs).\n * - `non-head-regen` — a regenerate that replaced a non-head message inside a\n * multi-message reply run; members are the owner run (the regenerate target in\n * place) plus each regenerator run. Not expressible as a same-parent\n * sibling-run group, so the View resolves and renders it itself (see\n * `_extractMessages`).\n *\n * `groupRoot` is the selection-map key: the input group root for fork-of, the\n * original reply's group root for regen, and the regenerate target's\n * codec-message-id for non-head-regen.\n */\ntype MessageBranchPoint =\n | { kind: 'fork-of'; groupRoot: string; members: BranchMember[] }\n | { kind: 'regen'; groupRoot: string; members: BranchMember[] }\n | { kind: 'non-head-regen'; groupRoot: string; members: BranchMember[] };\n\n// ---------------------------------------------------------------------------\n// Send-input normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * Normalise the two input shapes `View.send` accepts (a single TInput\n * or an array) into the array shape the SendDelegate consumes.\n * @param input - The raw input from `View.send`.\n * @returns The normalised input array.\n */\nconst _normaliseSend = <TInput extends CodecInputEvent>(input: TInput | TInput[]): TInput[] =>\n Array.isArray(input) ? input : [input];\n\n/**\n * Project a Tree `RunNode` down to the View-facing `RunInfo` shape:\n * drop the codec projection and the structural fields that callers\n * reach via `session.tree` when they need them.\n * @param run - The tree's RunNode.\n * @returns A projection-free RunInfo.\n */\nconst _toRunInfo = <TProjection>(run: RunNode<TProjection>): RunInfo => ({\n runId: run.runId,\n clientId: run.clientId,\n invocationId: run.invocationId,\n ...run.state,\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport class DefaultView<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> implements View<TInput, TMessage> {\n private readonly _tree: TreeInternal<TInput, TOutput, TProjection>;\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: Codec<TInput, TOutput, TProjection, TMessage>;\n private readonly _applier: WireApplier;\n private readonly _sendDelegate: SendDelegate<TInput>;\n private readonly _logger: Logger;\n private readonly _emitter: EventEmitter<ViewEventsMap>;\n private readonly _onClose?: () => void;\n\n /**\n * View-local branch selections: group-root runId → selection intent.\n * Fork points not present here default to the latest sibling.\n */\n private readonly _branchSelections = new Map<string, BranchSelectionState>();\n\n /**\n * View-local regenerate-group selections: anchor codec-message-id (the assistant\n * codec-message-id being regenerated) → selection intent. Distinct from\n * {@link _branchSelections} because a regenerate group is a set of\n * same-parent reply runs — message-level alternatives at a single\n * conversation slot, not edit forks of the prompt. Groups not present here default to the latest\n * member (the most recent regenerator, or the original if no regen has\n * landed).\n */\n private readonly _regenSelections = new Map<string, RegenSelection>();\n\n /**\n * Non-head regenerate selections, keyed by the regenerate target's\n * codec-message-id. Separate from {@link _regenSelections} because a non-head\n * regenerator parents inside the owner run rather than as a same-parent\n * sibling, so it lives outside the Tree's `visibleNodes` selection space and\n * is resolved at extraction (see `_extractMessages`). Value is the selected\n * member's nodeKey (the owner run id, or a regenerator run id); absent groups\n * default to the newest regenerator.\n */\n private readonly _nonHeadRegenSelections = new Map<string, RegenSelection>();\n\n /** Spec: AIT-CT11c — runIds loaded from history but not yet revealed to the UI. */\n private readonly _withheldRunIds = new Set<string>();\n\n /** Snapshot of visible node keys — used to detect structural changes and for selection pinning. */\n private _lastVisibleNodeKeys: string[] = [];\n\n /**\n * Snapshot of visible projection references — used to detect in-place\n * projection updates (streaming). One entry per visible Run.\n */\n private _lastVisibleProjections: TProjection[] = [];\n\n /**\n * Snapshot of the visible flat message chain with codec-message-ids —\n * exposed verbatim via `getMessages()` and the internal correlation\n * source for parent/branch routing.\n */\n private _lastVisibleMessagePairs: CodecMessage<TMessage>[] = [];\n\n /** Cached visible node-key Set — for O(1) lookup in event scoping. */\n private _lastVisibleNodeKeySet = new Set<string>();\n\n /** Whether there are more history pages to fetch from the channel. */\n private _hasMoreHistory = false;\n\n /** Internal state for continuing history pagination. */\n private _lastHistoryPage: HistoryPage | undefined;\n\n /** Buffer of withheld nodes (input + reply), drained newest-first by successive loadOlder() calls. */\n private readonly _withheldBuffer: ConversationNode<TProjection>[] = [];\n\n /**\n * Message-level trim on top of the run-level pagination window. Runs are\n * revealed whole (via `_withheldRunIds`/`_withheldBuffer`), so a `loadOlder`\n * may surface more messages than asked; this is the count of OLDEST messages\n * of the visible node chain to hide from `getMessages()` so a page lands on\n * exactly `limit` messages. The boundary run still appears in `runs()` (it's\n * a revealed node); only its oldest messages are trimmed from the flat list.\n * Live messages append at the newest end and are never trimmed.\n */\n private _hiddenMessageCount = 0;\n\n /** Unsubscribe functions for tree event subscriptions. */\n private readonly _unsubs: (() => void)[] = [];\n\n /**\n * Cached result of the last flat-nodes computation. Drives the visible\n * message snapshot exposed via `getMessages()`; refreshed by\n * `_computeFlatNodes()` on structural changes, selection changes,\n * and history reveal.\n */\n private _cachedNodes: ConversationNode<TProjection>[] = [];\n\n private _loadingOlder = false;\n private _processingHistory = false;\n private _closed = false;\n\n constructor(options: ViewOptions<TInput, TOutput, TProjection, TMessage>) {\n this._tree = options.tree;\n this._channel = options.channel;\n this._codec = options.codec;\n this._applier = options.applier;\n this._sendDelegate = options.sendDelegate;\n this._onClose = options.onClose;\n this._logger = options.logger.withContext({ component: 'View' });\n this._logger.trace('DefaultView();');\n this._emitter = new EventEmitter<ViewEventsMap>(this._logger);\n\n // Compute initial cache and snapshot visible state\n this._cachedNodes = this._computeFlatNodes();\n this._updateVisibleSnapshot(this._cachedNodes);\n\n // Subscribe to tree events and re-emit scoped versions\n this._unsubs.push(\n this._tree.on('update', () => {\n this._onTreeUpdate();\n }),\n this._tree.on('ably-message', (msg) => {\n this._onTreeAblyMessage(msg);\n }),\n this._tree.on('run', (event) => {\n this._onTreeRun(event);\n }),\n this._tree.on('output', (event) => {\n this._onTreeOutput(event);\n }),\n );\n }\n\n /**\n * Handle decoded outputs folded into a Run (streaming delta). If the run\n * is on the visible chain, recompute the flat message list and emit\n * `update`.\n * @param event - The output event from the Tree.\n */\n private _onTreeOutput(event: OutputEvent<TOutput>): void {\n if (this._processingHistory) return;\n // The fold target may be a reply run (event.runId) or a user input node\n // (event.runId undefined — the agent mints run-ids, so an input fold has\n // none). Gate on whichever key the visible set holds.\n const folded =\n (event.runId !== undefined && this._lastVisibleNodeKeySet.has(event.runId)) ||\n (event.inputCodecMessageId !== undefined && this._lastVisibleNodeKeySet.has(event.inputCodecMessageId));\n if (!folded) return;\n\n // The Tree emits `output` once per inbound message fold (with empty\n // `events` for inputs-only folds), so it fires whenever a visible Run's\n // projection changed and we always re-emit. The Reducer contract permits\n // in-place mutation, which means we cannot use projection-ref or\n // TMessage-ref equality to detect change: a streaming chunk legitimately\n // mutates the same UIMessage object, and a ref-equality short-circuit\n // would suppress every update. React state setters at the subscriber\n // boundary already dedup by array reference, so a redundant emit is a\n // no-op for unchanged hook consumers.\n this._lastVisibleProjections = this._cachedNodes.map((n) => n.projection);\n this._lastVisibleMessagePairs = this._extractMessages(this._cachedNodes).slice(this._hiddenMessageCount);\n this._emitter.emit('update');\n }\n\n // -------------------------------------------------------------------------\n // Public query methods\n // -------------------------------------------------------------------------\n\n getMessages(): CodecMessage<TMessage>[] {\n return this._lastVisibleMessagePairs;\n }\n\n runs(): RunInfo[] {\n // `_cachedNodes` is the visible node chain (inputs + reply runs) with\n // pagination and sibling selection already applied. RunInfo is reply-run\n // shaped, so filter to runs before projecting.\n return this._cachedNodes\n .filter((node): node is RunNode<TProjection> => node.kind === 'run')\n .map((node) => _toRunInfo(node));\n }\n\n /**\n * Compute the fresh visible node chain. The Tree's `visibleNodes` already\n * applies kind-blind reachability and sibling selection (edit versions /\n * regenerate runs collapse to the selected member), so the View only layers\n * its pagination window on top: drop nodes whose key is currently withheld.\n * @returns A fresh array of visible nodes (inputs + reply runs).\n */\n private _computeFlatNodes(): ConversationNode<TProjection>[] {\n const treeNodes = this._treeVisibleNodes();\n if (this._withheldRunIds.size === 0) return treeNodes;\n return treeNodes.filter((node) => !this._withheldRunIds.has(nodeKey(node)));\n }\n\n /**\n * Recompute the visible node chain, refresh the cache + snapshot, and emit\n * `update` unconditionally. Use after a mutation that always changes the\n * visible output (e.g. an explicit selection or a withheld-batch reveal).\n */\n private _recomputeAndEmit(): void {\n this._cachedNodes = this._computeFlatNodes();\n this._updateVisibleSnapshot(this._cachedNodes);\n this._emitter.emit('update');\n }\n\n /**\n * Recompute the visible node chain and, only if it differs from the current\n * snapshot, refresh the cache + snapshot and emit `update`. Use after a\n * mutation that may or may not move the visible window (e.g. a structural\n * tree update, or a deferred regenerate promotion that may already match).\n */\n private _recomputeAndEmitIfChanged(): void {\n const nodes = this._computeFlatNodes();\n if (this._visibleChanged(nodes)) {\n this._cachedNodes = nodes;\n this._updateVisibleSnapshot(nodes);\n this._emitter.emit('update');\n }\n }\n\n /**\n * Resolve the reply Run that owns a codec-message-id, narrowing the Tree's\n * node union to a {@link RunNode}. A user-input codec-message-id resolves to\n * an input node and yields `undefined` here — callers that must handle input\n * nodes use {@link _tree.getNodeByCodecMessageId} directly.\n * @param codecMessageId - The codec-message-id to resolve.\n * @returns The owning RunNode, or undefined if absent or not a reply Run.\n */\n private _runByCodecMessageId(codecMessageId: string): RunNode<TProjection> | undefined {\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n return node?.kind === 'run' ? node : undefined;\n }\n\n /**\n * The regenerator runs that replaced a non-head message of a reply run. They\n * file under the target's predecessor (not the owner run's input node), so the\n * Tree's `visibleNodes` cannot collapse them into the owner's slot; this\n * surfaces them for the View to resolve and render. Head-message (index 0)\n * regenerates are excluded - those are whole-reply sibling runs the Tree\n * already groups.\n * @param targetCodecMessageId - The regenerate target's (non-head) message id.\n * @param predecessorCodecMessageId - The codec-message-id immediately before it in the owner run.\n * @returns The regenerator runs in startSerial order (oldest first).\n */\n private _nonHeadRegenerators(\n targetCodecMessageId: string,\n predecessorCodecMessageId: string,\n ): RunNode<TProjection>[] {\n return this._tree\n .getReplyRuns(predecessorCodecMessageId)\n .filter((r) => r.regeneratesCodecMessageId === targetCodecMessageId)\n .toSorted((a, b) => (a.startSerial ?? '').localeCompare(b.startSerial ?? ''));\n }\n\n /**\n * Resolve the selected member of a non-head regenerate group anchored at\n * `targetCodecMessageId`. Members are the owner run `O` (memberNodeKey =\n * `ownerRunId`, the regenerate target in place) followed by each regenerator\n * run. Honours an explicit {@link _nonHeadRegenSelections} entry, else\n * defaults to the latest member (newest regenerator), mirroring the\n * whole-reply regenerate default.\n * @param targetCodecMessageId - The regenerate target's message id (the group anchor).\n * @param ownerRunId - The runId of the run that owns the regenerate target.\n * @param regenerators - The regenerator runs (oldest first) from `_nonHeadRegenerators`.\n * @returns The selected member's node key (`ownerRunId` or a regenerator runId).\n */\n private _selectedNonHeadMember(\n targetCodecMessageId: string,\n ownerRunId: string,\n regenerators: RunNode<TProjection>[],\n ): string {\n const sel = this._nonHeadRegenSelections.get(targetCodecMessageId);\n if (sel && sel.kind !== 'pending') {\n const keys = [ownerRunId, ...regenerators.map((r) => r.runId)];\n if (keys.includes(sel.selectedRunId)) return sel.selectedRunId;\n }\n // Default: latest member = newest regenerator (regenerators is oldest-first).\n return regenerators.at(-1)?.runId ?? ownerRunId;\n }\n\n /**\n * Flatten visible nodes to messages, collapsing a non-head regenerate into the\n * slot it replaces: while emitting a reply run, at each non-head message that\n * has a selected regenerator, drop that message and the run's tail and emit the\n * selected regenerator instead (recursive for regen-of-regen). Whole-reply\n * regenerates need nothing here - visibleNodes already picks the sibling.\n * @param nodes - Visible nodes (inputs + reply runs), chronological.\n * @returns The flat message list, each paired with its codec-message-id.\n */\n private _extractMessages(nodes: ConversationNode<TProjection>[]): CodecMessage<TMessage>[] {\n const messages: CodecMessage<TMessage>[] = [];\n // Regenerator runs already emitted via substitution at their anchor — skip\n // them when the node walk reaches them directly.\n const consumedRunIds = new Set<string>();\n\n for (const node of nodes) {\n if (node.kind === 'run' && consumedRunIds.has(node.runId)) continue;\n this._emitNodeMessages(node, messages, consumedRunIds);\n }\n return messages;\n }\n\n /**\n * Emit one visible node's messages into `out`, applying non-head regenerate\n * substitution for a reply run (see `_extractMessages`). Input nodes and runs\n * with no non-head regenerators emit their projection verbatim.\n * @param node - The node to emit.\n * @param out - The accumulating flat message list (mutated in place).\n * @param consumedRunIds - Set of regenerator runIds already emitted via substitution (mutated in place).\n */\n private _emitNodeMessages(\n node: ConversationNode<TProjection>,\n out: CodecMessage<TMessage>[],\n consumedRunIds: Set<string>,\n ): void {\n const own = this._codec.getMessages(node.projection);\n if (node.kind !== 'run') {\n out.push(...own);\n return;\n }\n for (let i = 0; i < own.length; i++) {\n const m = own[i];\n if (!m) continue;\n // Head message (i === 0) regenerates are whole-reply sibling runs, already\n // resolved by visibleNodes — only non-head messages anchor a non-head group.\n const predecessor = i > 0 ? own[i - 1]?.codecMessageId : undefined;\n if (predecessor !== undefined) {\n const regenerators = this._nonHeadRegenerators(m.codecMessageId, predecessor);\n if (regenerators.length > 0) {\n // Every regenerator (and any same-anchor sibling the Tree already\n // collapsed in `visibleNodes`) is an alternative at THIS one slot, so\n // mark them all consumed up front — the node walk must not re-emit the\n // Tree's default-latest sibling once we render a different member here.\n for (const r of regenerators) consumedRunIds.add(r.runId);\n const selectedKey = this._selectedNonHeadMember(m.codecMessageId, node.runId, regenerators);\n if (selectedKey !== node.runId) {\n // A regenerator is selected: drop M and the rest of O, emit the\n // selected regenerator in M's place (recursively for nested regen).\n const chosen = regenerators.find((r) => r.runId === selectedKey);\n if (chosen) {\n this._emitNodeMessages(chosen, out, consumedRunIds);\n return;\n }\n }\n // Original (owner run) selected: fall through and emit M from O.\n }\n }\n out.push(m);\n }\n }\n\n hasOlder(): boolean {\n return this._hiddenMessageCount > 0 || this._withheldBuffer.length > 0 || this._hasMoreHistory;\n }\n\n /**\n * Reveal `limit` more older codecMessages in this view — fewer only when\n * channel history is exhausted.\n *\n * Internally runs are revealed WHOLE (run-granular withholding), counting\n * codecMessages to decide how many runs to bring in, then the flat list\n * returned by {@link getMessages} is trimmed to exactly `limit` more\n * messages. So a run straddling the boundary still appears in {@link runs}\n * (it's a revealed node) while only its newest messages show in\n * `getMessages`. Live messages append at the newest end and are never\n * trimmed.\n * @param limit - Number of older codecMessages to reveal. Defaults to 10.\n */\n async loadOlder(limit = 10): Promise<void> {\n if (this._closed || this._loadingOlder) return;\n this._loadingOlder = true;\n this._logger.trace('DefaultView.loadOlder();', { limit });\n\n try {\n // Phase A: the boundary run is already revealed (a previous loadOlder\n // pulled in a whole run that overshot the message limit); reveal more of\n // its trimmed-off oldest messages without fetching or revealing new runs.\n if (this._hiddenMessageCount >= limit) {\n this._hiddenMessageCount -= limit;\n this._recomputeAndEmit();\n return;\n }\n\n // Phase B: reveal whole older runs covering the remaining message budget,\n // then re-trim so exactly `limit` new messages surface. Runs are revealed\n // whole (node granularity); the trim makes the message count exact.\n const need = limit - this._hiddenMessageCount;\n const before = this._extractMessages(this._computeFlatNodes()).length;\n const revealedSoFar = (): number => this._extractMessages(this._computeFlatNodes()).length - before;\n\n // Drain the withheld buffer toward `need` (whole older runs, newest-first).\n if (this._withheldBuffer.length > 0) {\n const splitIdx = this._messageTailSplitIndex(this._withheldBuffer, need);\n const batch = this._withheldBuffer.splice(splitIdx);\n this._releaseWithheld(batch);\n }\n\n // If the buffer was empty or fell short of `need` (e.g. it held a\n // zero-message run), fetch channel history for the remainder. The fetch\n // path loops over pages internally until it covers its target or history\n // is exhausted, so a single call here suffices.\n if (revealedSoFar() < need) {\n await this._fetchOlder(need - revealedSoFar());\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- close() may be set during the await above\n if (this._closed) return;\n }\n\n const after = this._extractMessages(this._computeFlatNodes()).length;\n // `after - before` whole-run messages were added at the oldest end; show\n // `limit` of them (newest), hiding the overshoot plus what was already\n // trimmed. `<= 0` when history is exhausted before `limit` is reached.\n this._hiddenMessageCount = Math.max(0, this._hiddenMessageCount + (after - before) - limit);\n this._recomputeAndEmit();\n } catch (error) {\n this._logger.error('DefaultView.loadOlder(); failed', { error });\n throw error;\n } finally {\n this._loadingOlder = false;\n }\n }\n\n /**\n * Fetch older channel history covering at least `target` more codecMessages,\n * buffering the older nodes and revealing whole runs. The withheld buffer is\n * assumed already drained by the caller. Loads the first page when no history\n * has been fetched yet, otherwise advances to the next older page; the\n * page-walk inside {@link _revealFromPage} loops until `target` messages are\n * covered or history runs out. No-op (leaving `_hasMoreHistory` false) once\n * channel history is exhausted.\n * @param target - Minimum additional codecMessages this fetch aims to cover.\n */\n private async _fetchOlder(target: number): Promise<void> {\n if (!this._hasMoreHistory && !this._lastHistoryPage) {\n await this._loadFirstPage(target);\n return;\n }\n\n if (!this._hasMoreHistory) return;\n\n if (!this._lastHistoryPage?.hasNext()) {\n this._hasMoreHistory = false;\n return;\n }\n\n const nextPage = await this._lastHistoryPage.next();\n if (this._closed || !nextPage) {\n if (!nextPage) this._hasMoreHistory = false;\n return;\n }\n\n await this._revealFromPage(nextPage, target);\n }\n\n /**\n * Find the index in `nodes` (chronological, oldest-first) at which the newest\n * whole runs covering at least `target` codecMessages begin. Walks newest-first\n * summing each node's `codec.getMessages(projection)` count; once the running\n * total reaches `target`, the current node (and everything newer) is the\n * revealed batch — so whole runs are revealed and the batch may overshoot\n * `target` (the caller trims). Returns `0` when the nodes hold fewer than\n * `target` messages — reveal everything.\n *\n * Shared by the buffer-drain and history-fetch reveal paths so they agree on\n * \"covering `target` messages\".\n * @param nodes - Candidate nodes, oldest-first.\n * @param target - Minimum codecMessages the revealed batch must cover.\n * @returns The split index; `nodes[splitIdx..]` is the revealed batch.\n */\n private _messageTailSplitIndex(nodes: ConversationNode<TProjection>[], target: number): number {\n let messages = 0;\n for (let i = nodes.length - 1; i >= 0; i--) {\n const node = nodes[i];\n if (!node) continue;\n messages += this._codec.getMessages(node.projection).length;\n if (messages >= target) return i; // reveal nodes[i..]\n }\n return 0; // fewer than `target` messages — reveal everything\n }\n\n // -------------------------------------------------------------------------\n // Run lookup\n // -------------------------------------------------------------------------\n\n runOf(codecMessageId: string): RunInfo | undefined {\n this._logger.trace('DefaultView.runOf();', { codecMessageId });\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (!node) return undefined;\n if (node.kind === 'run') return _toRunInfo(node);\n // Input node: resolve to its selected reply run (undefined if none started).\n const reply = this._selectedReplyRun(node.codecMessageId);\n return reply ? _toRunInfo(reply) : undefined;\n }\n\n /**\n * Resolve the reply run currently selected for an input node, honouring the\n * View's regenerate selection. Falls back to the latest reply run when no\n * selection has been recorded; undefined when no reply run has started.\n * @param inputCodecMessageId - The input node's codec-message-id.\n * @returns The selected reply RunNode, or undefined.\n */\n private _selectedReplyRun(inputCodecMessageId: string): RunNode<TProjection> | undefined {\n const replies = this._tree.getReplyRuns(inputCodecMessageId);\n if (replies.length === 0) return undefined;\n if (replies.length === 1) return replies[0];\n // Multiple reply runs = a regenerate group. Honour the View's selection\n // (keyed by group root) else default to the latest.\n const groupRoot = this._tree.getGroupRoot(replies[0]?.runId ?? '');\n const sel = this._regenSelections.get(groupRoot);\n const selectedKey = sel && sel.kind !== 'pending' ? sel.selectedRunId : undefined;\n if (selectedKey !== undefined) {\n const chosen = replies.find((r) => r.runId === selectedKey);\n if (chosen) return chosen;\n }\n // Latest by startSerial; getReplyRuns is set-ordered, so sort defensively.\n return replies.toSorted((a, b) => (a.startSerial ?? '').localeCompare(b.startSerial ?? '')).at(-1);\n }\n\n run(runId: string): RunInfo | undefined {\n this._logger.trace('DefaultView.run();', { runId });\n const run = this._tree.getRunNode(runId);\n return run ? _toRunInfo(run) : undefined;\n }\n\n // -------------------------------------------------------------------------\n // Branch navigation (msg-anchored)\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT13c, AIT-CT13d — branch points are codec-message-id\n // anchored. The View resolves the anchor (the user prompt for edits,\n // the assistant slot for regens) and routes the selection to the\n // appropriate internal selection map. Tree-level introspection\n // (RunNode access, runId-keyed queries) remains on the {@link Tree}.\n\n branchSelection(codecMessageId: string): BranchSelection<TMessage> {\n const branch = this._resolveMessageBranchPoint(codecMessageId);\n if (branch) {\n // Each member contributes its representative message as the branch-arrow\n // slot: for an edit fork that is the alternate user prompt; for a\n // whole-reply regenerate group the variant's first message; for a non-head\n // regenerate group the regenerate target (original) or the regenerator's\n // first message.\n const siblings = branch.members.flatMap((member) => {\n const owner = this._tree.getNodeByCodecMessageId(member.representativeCodecMessageId);\n if (!owner) return [];\n const found = this._codec\n .getMessages(owner.projection)\n .find((m) => m.codecMessageId === member.representativeCodecMessageId);\n return found ? [found.message] : [];\n });\n\n if (siblings.length > 0) {\n const index = this._resolveSelectedIndex(branch);\n const clamped = Math.max(0, Math.min(index, siblings.length - 1));\n const selected = siblings[clamped];\n return {\n hasSiblings: siblings.length > 1,\n siblings,\n index: clamped,\n selected,\n };\n }\n }\n\n // Known non-anchor message: the bundle's invariant is that\n // `siblings` contains the rendered message itself for any known\n // codec-message-id, so plain bubbles get `siblings.length === 1`\n // (not `0`) and the indexing space matches between read and write.\n // Resolve the owning node kind-blind — a plain user prompt is an input\n // node, an assistant message lives in a reply run; both carry a projection.\n const owner = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (owner) {\n const found = this._codec.getMessages(owner.projection).find((m) => m.codecMessageId === codecMessageId);\n if (found !== undefined) {\n return { hasSiblings: false, siblings: [found.message], index: 0, selected: found.message };\n }\n }\n\n // Unknown id, or the owner Run is known but the codec doesn't surface\n // a message with this id from the projection (e.g. an event-only fold\n // such as a tool result that mutates an assistant in-place without\n // exposing its own TMessage). Treat both as \"no rendered message\",\n // returning the safe empty bundle.\n return { hasSiblings: false, siblings: [], index: 0, selected: undefined };\n }\n\n // Spec: AIT-CT13c, AIT-CT13d\n selectSibling(codecMessageId: string, index: number): void {\n this._logger.trace('DefaultView.selectSibling();', { codecMessageId, index });\n const branch = this._resolveMessageBranchPoint(codecMessageId);\n if (!branch) return;\n const clamped = Math.max(0, Math.min(index, branch.members.length - 1));\n const selected = branch.members[clamped];\n if (!selected) return; // unreachable: clamped is always in bounds\n if (branch.kind === 'fork-of') {\n this._branchSelections.set(branch.groupRoot, { kind: 'user', selectedKey: selected.memberNodeKey });\n this._logger.debug('DefaultView.selectSibling(); fork-of', {\n codecMessageId,\n index: clamped,\n selectedKey: selected.memberNodeKey,\n });\n } else if (branch.kind === 'non-head-regen') {\n // Non-head groups live outside the visibleNodes sibling space — store in\n // the dedicated map the message-extraction substitution reads.\n this._nonHeadRegenSelections.set(branch.groupRoot, { kind: 'user', selectedRunId: selected.memberNodeKey });\n this._logger.debug('DefaultView.selectSibling(); non-head-regen', {\n codecMessageId,\n index: clamped,\n selectedRunId: selected.memberNodeKey,\n anchor: branch.groupRoot,\n });\n } else {\n this._regenSelections.set(branch.groupRoot, { kind: 'user', selectedRunId: selected.memberNodeKey });\n this._logger.debug('DefaultView.selectSibling(); regenerate', {\n codecMessageId,\n index: clamped,\n selectedRunId: selected.memberNodeKey,\n groupRoot: branch.groupRoot,\n });\n }\n this._recomputeAndEmit();\n }\n\n /**\n * Resolve the currently selected sibling's index inside a branch group.\n * Pending selections fall back to the latest sibling. The caller clamps\n * the returned index against any post-extraction filtering.\n * @param branch - Resolved branch-point descriptor from `_resolveMessageBranchPoint`.\n * @returns The selected sibling's index within `branch.siblings`.\n */\n private _resolveSelectedIndex(branch: MessageBranchPoint): number {\n if (branch.kind === 'fork-of') {\n const sel = this._branchSelections.get(branch.groupRoot);\n if (!sel) return branch.members.length - 1;\n const idx = branch.members.findIndex((m) => m.memberNodeKey === sel.selectedKey);\n return idx === -1 ? branch.members.length - 1 : idx;\n }\n const sel =\n branch.kind === 'non-head-regen'\n ? this._nonHeadRegenSelections.get(branch.groupRoot)\n : this._regenSelections.get(branch.groupRoot);\n if (!sel || sel.kind === 'pending') return branch.members.length - 1;\n const idx = branch.members.findIndex((m) => m.memberNodeKey === sel.selectedRunId);\n return idx === -1 ? branch.members.length - 1 : idx;\n }\n\n /**\n * Resolve the branch point anchored at `codecMessageId`, if any, returning the\n * group `kind` + members + groupRoot so the caller routes to the correct\n * selection map directly (not via a runId dispatch that would mis-route when\n * the owning Run is in both a fork-of and a regen group).\n * @param codecMessageId - The codec-message-id to look up.\n * @returns The resolved branch point, or undefined when `codecMessageId`\n * anchors no group.\n */\n private _resolveMessageBranchPoint(codecMessageId: string): MessageBranchPoint | undefined {\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (!node) return undefined;\n\n // Edit-fork branch point: `codecMessageId` is a user INPUT node that has\n // sibling input nodes (alternate prompts via fork-of). The anchor is the\n // input node's own codec-message-id.\n if (node.kind === 'input') {\n const siblings = this._tree.getSiblingNodes(node.codecMessageId);\n if (siblings.length > 1) {\n return {\n kind: 'fork-of',\n groupRoot: this._tree.getGroupRoot(node.codecMessageId),\n members: this._nodeHeadMembers(siblings),\n };\n }\n return undefined;\n }\n\n // Non-head regenerate branch point: `codecMessageId` is the rendered slot for\n // a regenerate that replaced a non-head message inside a multi-message reply\n // run. Resolved BEFORE the same-parent `regen` group below: several non-head\n // regenerators of one anchor share a parent (the anchor's predecessor), so\n // the Tree files them as their own sibling group excluding the owner run; the\n // non-head resolver instead gathers the owner plus every regenerator into one\n // anchor-keyed group.\n const ownMessages = this._codec.getMessages(node.projection);\n const nonHead = this._resolveNonHeadBranchPoint(node, ownMessages, codecMessageId);\n if (nonHead) return nonHead;\n\n // Regenerate branch point: `codecMessageId` is owned by a reply run that has\n // sibling reply runs (the original reply + its regenerators, all parented at\n // the same input node). Anchor on the head message of the run so arrows\n // appear once per variant, not on every follow-up message.\n const siblings = this._tree.getSiblingNodes(node.runId);\n if (siblings.length > 1 && ownMessages.at(0)?.codecMessageId === codecMessageId) {\n return {\n kind: 'regen',\n groupRoot: this._tree.getGroupRoot(node.runId),\n members: this._nodeHeadMembers(siblings),\n };\n }\n\n return undefined;\n }\n\n /**\n * Resolve a non-head regenerate branch point from a reply-run message, if any.\n * `codecMessageId` is either (a) a non-head message `M` of its owner run with\n * regenerators, or (b) a regenerator run's head; both resolve to the same group\n * anchored at `M` (key matching {@link _nonHeadRegenSelections}).\n * @param node - The reply run owning `codecMessageId`.\n * @param ownMessages - That run's projected messages (already extracted).\n * @param codecMessageId - The slot's codec-message-id (an `M`, or a regenerator head).\n * @returns The non-head branch point, or undefined when `codecMessageId` anchors none.\n */\n private _resolveNonHeadBranchPoint(\n node: RunNode<TProjection>,\n ownMessages: CodecMessage<TMessage>[],\n codecMessageId: string,\n ): MessageBranchPoint | undefined {\n // Case (b): `codecMessageId` is a regenerator run's head. Re-anchor on the\n // message it regenerates and resolve from the owner run's perspective.\n const isHead = ownMessages.at(0)?.codecMessageId === codecMessageId;\n if (isHead && node.regeneratesCodecMessageId !== undefined) {\n const anchorId = node.regeneratesCodecMessageId;\n const owner = this._runByCodecMessageId(anchorId);\n if (owner) {\n const ownerMsgs = this._codec.getMessages(owner.projection);\n const idx = ownerMsgs.findIndex((mm) => mm.codecMessageId === anchorId);\n const predecessor = idx > 0 ? ownerMsgs[idx - 1]?.codecMessageId : undefined;\n if (predecessor !== undefined) {\n return this._buildNonHeadGroup(anchorId, owner.runId, predecessor);\n }\n }\n return undefined;\n }\n\n // Case (a): `codecMessageId` is a non-head message of its owner run.\n const idx = ownMessages.findIndex((mm) => mm.codecMessageId === codecMessageId);\n const predecessor = idx > 0 ? ownMessages[idx - 1]?.codecMessageId : undefined;\n if (predecessor === undefined) return undefined;\n return this._buildNonHeadGroup(codecMessageId, node.runId, predecessor);\n }\n\n /**\n * Build the {@link MessageBranchPoint} for a non-head regenerate group, or\n * undefined when the anchor has no regenerators. The owner member's\n * representative is the anchor message (the regenerate target); each\n * regenerator's is its head message.\n * @param anchorCodecMessageId - The regenerate target's (non-head) message id.\n * @param ownerRunId - The runId owning the regenerate target.\n * @param predecessorCodecMessageId - The codec-message-id immediately before the anchor in the owner run.\n * @returns The non-head branch point, or undefined when there are no regenerators.\n */\n private _buildNonHeadGroup(\n anchorCodecMessageId: string,\n ownerRunId: string,\n predecessorCodecMessageId: string,\n ): MessageBranchPoint | undefined {\n const regenerators = this._nonHeadRegenerators(anchorCodecMessageId, predecessorCodecMessageId);\n if (regenerators.length === 0) return undefined;\n const members: BranchMember[] = [{ memberNodeKey: ownerRunId, representativeCodecMessageId: anchorCodecMessageId }];\n for (const r of regenerators) {\n const head = this._codec.getMessages(r.projection).at(0);\n if (head) members.push({ memberNodeKey: r.runId, representativeCodecMessageId: head.codecMessageId });\n }\n return { kind: 'non-head-regen', groupRoot: anchorCodecMessageId, members };\n }\n\n /**\n * Project nodes to {@link BranchMember}s for fork-of / whole-reply regen\n * groups, where each member's branch-arrow representative is its own head\n * message and its memberNodeKey is its node key.\n * @param nodes - The sibling nodes.\n * @returns One member per node that has a head message.\n */\n private _nodeHeadMembers(nodes: ConversationNode<TProjection>[]): BranchMember[] {\n const members: BranchMember[] = [];\n for (const n of nodes) {\n const head = this._codec.getMessages(n.projection).at(0);\n if (head) members.push({ memberNodeKey: nodeKey(n), representativeCodecMessageId: head.codecMessageId });\n }\n return members;\n }\n\n // -------------------------------------------------------------------------\n // Write operations\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT3, AIT-CT4\n async send(input: TInput | TInput[], options?: SendOptions): Promise<ActiveRun> {\n this._logger.trace('DefaultView.send();');\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to send; view is closed', ErrorCode.InvalidArgument, 400);\n }\n\n const normalised = _normaliseSend<TInput>(input);\n\n // The codec-message-id of the visible branch tail — the delegate uses it\n // for auto-parent routing on fresh user messages.\n const parentCodecMessageId = this._lastVisibleMessagePairs.at(-1)?.codecMessageId;\n\n const result = await this._sendDelegate(normalised, options, parentCodecMessageId);\n this._applyForkAutoSelect(result, options);\n return result;\n }\n\n /**\n * Auto-select / pin branch selections after a forking send.\n * @param result - The ActiveRun returned by the delegate.\n * @param options - The SendOptions passed by the caller.\n */\n private _applyForkAutoSelect(result: ActiveRun, options: SendOptions | undefined): void {\n // Spec: AIT-CT13e\n if (!options?.forkOf) return;\n\n // An edit inserts a NEW user input node optimistically; its codec-message-id\n // is the (only) optimistic id and IS its node key. Edit forks are input-node\n // sibling groups, so the selection is keyed by the input group root and the\n // selected member is the new input node's key.\n const editedInputKey = result.optimisticCodecMessageIds.at(0);\n if (editedInputKey === undefined) return;\n const groupRoot = this._tree.getGroupRoot(editedInputKey);\n\n this._branchSelections.set(groupRoot, { kind: 'auto', selectedKey: editedInputKey });\n this._recomputeAndEmit();\n }\n\n /**\n * Auto-select / pin the regenerate group anchored at `anchorCodecMessageId` so\n * the new Run's content appears as soon as the agent's run-start lands.\n *\n * `View.regenerate()` calls this with the assistant codec-message-id being\n * regenerated. The Run doesn't exist yet on the channel (the regenerate\n * wire is wire-only); the selection is recorded as `pending` and\n * promoted to `auto` by `_pinRegenSelections` once the corresponding\n * Run is created in the tree.\n * @param result - The ActiveRun returned by the delegate (run-id is the new regenerator's).\n * @param anchorCodecMessageId - The codec-message-id of the assistant being regenerated.\n */\n private _applyRegenerateAutoSelect(result: ActiveRun, anchorCodecMessageId: string): void {\n // A regenerate produces a new reply run parented at the SAME input node as\n // the original reply (the regenerate group). The agent mints the run-id, so\n // we cannot pin by it synchronously. Resolve the group root from the\n // original reply run owning the anchor, and pin a pending selection keyed by\n // that group root, carrying the regenerate carrier's codec-message-id\n // (`result.inputCodecMessageId`) so we can promote when the new reply run lands.\n const anchorRun = this._runByCodecMessageId(anchorCodecMessageId);\n if (!anchorRun) return;\n\n // Non-head regenerate: the anchor is a non-head message of its owner run, so\n // the new run won't be a same-parent sibling — it parents at the anchor's\n // predecessor. Defer in the dedicated non-head map (keyed by the anchor\n // message), not the sibling-group regen map.\n const anchorMsgs = this._codec.getMessages(anchorRun.projection);\n if (anchorMsgs.at(0)?.codecMessageId !== anchorCodecMessageId) {\n this._nonHeadRegenSelections.set(anchorCodecMessageId, {\n kind: 'pending',\n carrierCodecMessageId: result.inputCodecMessageId,\n });\n this._logger.debug('DefaultView._applyRegenerateAutoSelect(); deferring non-head regenerate selection', {\n anchorCodecMessageId,\n carrier: result.inputCodecMessageId,\n });\n this._resolvePendingNonHeadRegenSelections();\n this._recomputeAndEmitIfChanged();\n return;\n }\n\n const groupRoot = this._tree.getGroupRoot(anchorRun.runId);\n\n this._regenSelections.set(groupRoot, {\n kind: 'pending',\n carrierCodecMessageId: result.inputCodecMessageId,\n });\n this._logger.debug('DefaultView._applyRegenerateAutoSelect(); deferring regenerate selection', {\n anchorCodecMessageId,\n groupRoot,\n carrier: result.inputCodecMessageId,\n });\n\n // The new reply run may already be in the tree (run-start raced ahead of the\n // sendDelegate resolution). Promote now and recompute so the visible set\n // catches up without waiting for the next structural change.\n this._resolvePendingRegenSelections();\n this._recomputeAndEmitIfChanged();\n }\n\n // Spec: AIT-CT5, AIT-CT13d\n async regenerate(messageId: string, options?: SendOptions): Promise<ActiveRun> {\n this._logger.trace('DefaultView.regenerate();', { messageId });\n\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to regenerate; view is closed', ErrorCode.InvalidArgument, 400);\n }\n\n // `messageId` is the assistant being regenerated. The new Run is a\n // continuation of the regenerated message's Run, not a fork: the\n // message-level replacement (new assistant supersedes the original)\n // happens at projection extraction time. We still resolve the parent\n // user prompt so the new assistant's wire `parent` is correct,\n // and we send the truncated history (through the parent inclusive)\n // so the LLM re-answers the right message.\n const targetRun = this._runByCodecMessageId(messageId);\n if (!targetRun) {\n throw new Ably.ErrorInfo(\n `unable to regenerate; message not found in tree: ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const parentCodecMessageId = this._findParentMsgId(targetRun, messageId);\n if (!parentCodecMessageId) {\n throw new Ably.ErrorInfo(\n `unable to regenerate; parent user message not found for ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n // Canonical regen anchor: when the user clicks Regenerate on an\n // already-regenerated assistant, the new alternative SHOULD belong\n // to the SAME branch point as the previous regen — but ONLY when\n // the target is the position-equivalent of the group anchor (the\n // head message of the regenerator Run). For a trailing follow-up\n // message inside a regenerator Run (e.g. the LLM text after the\n // regenerated tool call), the user expects the regen to anchor at\n // the specific message they clicked, not roll up to the group root.\n // Rebasing trailing regens to the group root produces a confusing\n // \"N+1 / N+1\" counter on the tool-call bubble and runs the whole\n // turn from scratch instead of just regenerating the text.\n let regenAnchorMsgId = messageId;\n if (targetRun.regeneratesCodecMessageId !== undefined) {\n const firstMsg = this._codec.getMessages(targetRun.projection).at(0);\n if (firstMsg?.codecMessageId === messageId) {\n regenAnchorMsgId = targetRun.regeneratesCodecMessageId;\n }\n }\n\n const sendOptions: SendOptions = {\n ...options,\n parent: parentCodecMessageId,\n };\n\n // Mint a regenerate input via the codec. The codec's well-known\n // `Regenerate` carries `target: regenAnchorMsgId` and `parent:\n // parentCodecMessageId`; the session reads those fields off the input\n // directly when building transport headers (`fork-of` and\n // `parent`). The agent's input-event lookup catches the wire signal;\n // no tree-upsert / projection fold runs locally.\n const regenerate = this._codec.createRegenerate(regenAnchorMsgId, parentCodecMessageId);\n const result = await this._sendDelegate([regenerate], sendOptions, parentCodecMessageId);\n this._applyRegenerateAutoSelect(result, regenAnchorMsgId);\n return result;\n }\n\n // Spec: AIT-CT6\n async edit(messageId: string, inputs: TInput | TInput[], options?: SendOptions): Promise<ActiveRun> {\n this._logger.trace('DefaultView.edit();', { messageId });\n\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to edit; view is closed', ErrorCode.InvalidArgument, 400);\n }\n\n // The edit target is a user prompt — a run-less INPUT node — so resolve\n // it kind-blind, not via the reply-run-only lookup.\n const targetNode = this._tree.getNodeByCodecMessageId(messageId);\n if (!targetNode) {\n throw new Ably.ErrorInfo(\n `unable to edit; message not found in tree: ${messageId}`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const parentCodecMessageId = this._findParentMsgId(targetNode, messageId);\n\n return this.send(inputs, {\n ...options,\n forkOf: messageId,\n parent: parentCodecMessageId,\n });\n }\n\n /**\n * Find the codec-message-id of the message immediately preceding `targetMsgId` in\n * the visible conversation.\n *\n * Consults the View's visible message chain first so message-level\n * replacements (regenerate) are respected: regenerating an\n * already-regenerated assistant lands the predecessor on the user\n * prompt the regen is responding to, NOT on the hidden original\n * assistant that occupies the same conversation slot. Falls back to a\n * projection-walk for the rare case where `targetMsgId` isn't on the\n * visible chain (e.g. caller is operating on a Run that's selection-\n * hidden by the current branch).\n * @param targetNode - The node (input node or reply run) that owns `targetMsgId`.\n * @param targetMsgId - The codec-message-id to find the parent of.\n * @returns The parent codec-message-id, or undefined if no predecessor exists.\n */\n private _findParentMsgId(targetNode: ConversationNode<TProjection>, targetMsgId: string): string | undefined {\n const visible = this._lastVisibleMessagePairs;\n const visIdx = visible.findIndex((m) => m.codecMessageId === targetMsgId);\n if (visIdx > 0) {\n return visible[visIdx - 1]?.codecMessageId;\n }\n if (visIdx === 0) return undefined;\n\n const messages = this._codec.getMessages(targetNode.projection);\n const idx = messages.findIndex((m) => m.codecMessageId === targetMsgId);\n if (idx > 0) {\n return messages[idx - 1]?.codecMessageId;\n }\n if (idx === 0 && targetNode.parentCodecMessageId !== undefined) {\n // The structural predecessor is the node owning parentCodecMessageId\n // (an input node, or a prior reply run). Its tail message is the parent.\n const parentNode = this._tree.getNodeByCodecMessageId(targetNode.parentCodecMessageId);\n if (parentNode) {\n return this._codec.getMessages(parentNode.projection).at(-1)?.codecMessageId;\n }\n }\n return undefined;\n }\n\n // -------------------------------------------------------------------------\n // Event subscription\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CT8a, AIT-CT8b, AIT-CT8e\n on(event: 'update', handler: () => void): () => void;\n on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;\n on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;\n on(\n event: 'update' | 'ably-message' | 'run',\n handler: (() => void) | ((msg: Ably.InboundMessage) => void) | ((event: RunLifecycleEvent) => void),\n ): () => void {\n // CAST: overload signatures enforce correct handler types per event name.\n const cb = handler as (arg: ViewEventsMap[keyof ViewEventsMap]) => void;\n this._emitter.on(event, cb);\n return () => {\n this._emitter.off(event, cb);\n };\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n close(): void {\n if (this._closed) return;\n this._logger.info('DefaultView.close();');\n this._closed = true;\n this._loadingOlder = false;\n for (const unsub of this._unsubs) unsub();\n this._unsubs.length = 0;\n this._emitter.off();\n this._branchSelections.clear();\n this._regenSelections.clear();\n this._nonHeadRegenSelections.clear();\n this._withheldRunIds.clear();\n this._withheldBuffer.length = 0;\n this._hiddenMessageCount = 0;\n this._onClose?.();\n }\n\n // -------------------------------------------------------------------------\n // Private: history loading\n // -------------------------------------------------------------------------\n\n private async _loadFirstPage(target: number): Promise<void> {\n // `loadHistory`'s limit and this view's reveal target both count complete\n // domain messages (codecMessages), so the target passes straight through.\n const firstPage = await loadHistory(this._channel, { limit: target }, this._logger);\n if (this._closed) return;\n await this._revealFromPage(firstPage, target);\n }\n\n /**\n * Walk channel history from `page` until the newly-observed nodes hold at\n * least `target` codecMessages (or the channel is exhausted), then reveal the\n * newest whole runs covering `target` and withhold the rest. Snapshots the\n * already-visible nodes up front so only newly-observed nodes count toward\n * `target`. No-op if the view closed during the page walk.\n * @param page - The decoded history page to start from.\n * @param target - Minimum codecMessages to reveal in this batch.\n */\n private async _revealFromPage(page: HistoryPage, target: number): Promise<void> {\n // Snapshot before loading: every node already in the tree stays visible.\n const beforeRunIds = new Set(this._treeVisibleNodes().map((n) => nodeKey(n)));\n\n const { newVisible, lastPage } = await this._loadUntilVisible(page, target, beforeRunIds);\n if (this._closed) return;\n this._lastHistoryPage = lastPage;\n this._hasMoreHistory = lastPage.hasNext();\n this._splitReveal(newVisible, target);\n }\n\n /**\n * Reveal the newest whole runs covering `target` codecMessages from\n * `newVisible` and withhold the rest so subsequent `loadOlder` calls can\n * drain them. Reveal granularity is the whole run; the caller trims the flat\n * message list (via `_hiddenMessageCount`) to make the visible message count\n * exact. Called by {@link _revealFromPage}.\n * @param newVisible - Newly observed nodes (inputs + reply runs) from the history fetch, chronological.\n * @param target - Minimum codecMessages the revealed batch must cover.\n */\n private _splitReveal(newVisible: ConversationNode<TProjection>[], target: number): void {\n const splitIdx = this._messageTailSplitIndex(newVisible, target);\n const batch = newVisible.slice(splitIdx);\n const withheld = newVisible.slice(0, splitIdx);\n for (const n of withheld) {\n this._withheldRunIds.add(nodeKey(n));\n }\n this._withheldBuffer.push(...withheld);\n this._releaseWithheld(batch);\n }\n\n /**\n * Replay a history page's raw messages into the Tree through the Tree's\n * single decode-and-apply engine — the same applier (and decoder instance)\n * the client's live loop uses, so history replay can't drift from it. The\n * shared decoder's version-guarded trackers make the overlap between the\n * two routes safe: an in-flight stream that spans the attach boundary is\n * continued rather than re-started, and content the live route already\n * incorporated decodes to nothing.\n * @param page - The history page returned by `loadHistory`.\n */\n private _processHistoryPage(page: HistoryPage): void {\n this._processingHistory = true;\n try {\n for (const rawMsg of page.rawMessages) {\n this._applier.apply(rawMsg);\n }\n\n // Emit ably-message in a batch AFTER the whole page is applied, so a\n // subscriber resolving the owning Run sees the fully-rebuilt tree.\n for (const msg of page.rawMessages) {\n this._tree.emitAblyMessage(msg);\n }\n } finally {\n this._processingHistory = false;\n }\n }\n\n private async _loadUntilVisible(\n firstPage: HistoryPage,\n target: number,\n beforeRunIds: Set<string>,\n ): Promise<{ newVisible: ConversationNode<TProjection>[]; lastPage: HistoryPage }> {\n this._processHistoryPage(firstPage);\n let page = firstPage;\n\n const newVisibleCount = (): number => {\n let count = 0;\n for (const n of this._treeVisibleNodes()) {\n // Count newly-visible codecMessages toward the target (whole runs are\n // revealed; the caller trims to the exact message count).\n if (!beforeRunIds.has(nodeKey(n))) count += this._codec.getMessages(n.projection).length;\n }\n return count;\n };\n\n while (newVisibleCount() < target && page.hasNext()) {\n const nextPage = await page.next();\n if (!nextPage || this._closed) break;\n this._processHistoryPage(nextPage);\n page = nextPage;\n }\n\n const newVisible = this._treeVisibleNodes().filter((n) => !beforeRunIds.has(nodeKey(n)));\n return { newVisible, lastPage: page };\n }\n\n // Spec: AIT-CT11a\n private _releaseWithheld(nodes: ConversationNode<TProjection>[]): void {\n for (const n of nodes) {\n this._withheldRunIds.delete(nodeKey(n));\n }\n if (nodes.length > 0) {\n this._recomputeAndEmit();\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: scoped event forwarding\n // -------------------------------------------------------------------------\n\n private _updateVisibleSnapshot(nodes?: ConversationNode<TProjection>[]): void {\n const resolved = nodes ?? this._cachedNodes;\n // Identity key = nodeKey (runId for reply runs, codecMessageId for inputs),\n // so the visible set scopes events for both kinds and input-node parents.\n this._lastVisibleNodeKeys = resolved.map((n) => nodeKey(n));\n this._lastVisibleNodeKeySet = new Set(this._lastVisibleNodeKeys);\n this._lastVisibleProjections = resolved.map((n) => n.projection);\n // Run-level reveal, message-level trim: drop the oldest `_hiddenMessageCount`\n // messages so a `loadOlder` page lands on exactly `limit` messages even\n // though whole runs were revealed.\n this._lastVisibleMessagePairs = this._extractMessages(resolved).slice(this._hiddenMessageCount);\n }\n\n private _onTreeUpdate(): void {\n // Suppress update forwarding while processing history pages. During\n // _processHistoryPage, each tree.applyMessage() fires this handler\n // synchronously — but _withheldRunIds hasn't been populated yet, so\n // _computeFlatNodes() would return unfiltered history. Without this guard,\n // subscribers briefly see all history Runs before the pagination window\n // is applied. The final update is emitted by _releaseWithheld after\n // withholding is set up.\n if (this._processingHistory) return;\n\n // The Tree emits `update` only on structural change (new/removed Run,\n // sort-reorder, startSerial promotion, run-start backfill), so every\n // update reaching here warrants a full re-walk. Content-only folds flow\n // through `output` (_onTreeOutput) instead.\n\n // Pin selections for previously-visible Runs that now have siblings.\n // This prevents new forks (from other views' edits/regenerates) from\n // shifting this view to a branch the user didn't navigate to.\n this._pinBranchSelections();\n this._resolvePendingRegenSelections();\n this._resolvePendingNonHeadRegenSelections();\n\n this._recomputeAndEmitIfChanged();\n }\n\n /**\n * Build the unified selection map the Tree's `visibleNodes` consumes:\n * `groupRootKey -> selectedKey`, covering both edit forks (input-node groups,\n * keyed by the input group root) and regenerate groups (reply-run groups,\n * keyed by the original reply's group root). Pending entries (no chosen\n * member yet) are omitted so the Tree falls back to the latest sibling.\n * @returns The merged group-root → selected-key map.\n */\n private _resolveSelections(): Map<string, string> {\n const resolved = new Map<string, string>();\n for (const [groupRoot, sel] of this._branchSelections) {\n resolved.set(groupRoot, sel.selectedKey);\n }\n for (const [groupRoot, sel] of this._regenSelections) {\n if (sel.kind === 'pending') continue;\n resolved.set(groupRoot, sel.selectedRunId);\n }\n return resolved;\n }\n\n /**\n * The Tree's visible node chain under this view's current selections — the\n * reachable, sibling-resolved nodes before the View's pagination window is\n * applied.\n * @returns The selection-resolved visible node chain.\n */\n private _treeVisibleNodes(): ConversationNode<TProjection>[] {\n return this._tree.visibleNodes(this._resolveSelections());\n }\n\n /**\n * For each previously-visible Run that now has siblings but no explicit\n * selection, pin the selection to that Run's runId. This preserves the\n * current branch when new forks appear from other views or external\n * sources.\n *\n * Exception: if the fork was initiated by this view (tracked as a\n * `pending` BranchSelection), select the newest sibling (the awaited Run)\n * instead of pinning the old one.\n */\n private _pinBranchSelections(): void {\n for (const key of this._lastVisibleNodeKeys) {\n const node = this._tree.getNode(key);\n // Edit forks are INPUT-node sibling groups; only input nodes pin here.\n // Regenerate (reply-run) groups roll forward via _resolvePendingRegenSelections.\n if (node?.kind !== 'input') continue;\n const siblings = this._tree.getSiblingNodes(key);\n if (siblings.length <= 1) continue;\n const groupRoot = this._tree.getGroupRoot(key);\n const existing = this._branchSelections.get(groupRoot);\n\n // Spec: AIT-CT13f — external edit fork: pin to the currently-visible\n // sibling so a fork from another view doesn't drift this view's branch.\n if (existing) continue;\n this._branchSelections.set(groupRoot, { kind: 'pinned', selectedKey: key });\n }\n }\n\n /**\n * Roll `pending` and `auto` regenerate selections forward to the newest\n * group member. A regenerate slot defaults to the latest member, so each\n * new regenerator (this view's awaited run, or an external one) auto-rolls\n * the slot forward — UNLESS the user explicitly selected an earlier member\n * (`user`), which pins and is left untouched. The agent mints the run-id, so\n * we can't match the awaited run by id — once the group grows we adopt the\n * newest as the selected member.\n */\n private _resolvePendingRegenSelections(): void {\n for (const [groupRoot, sel] of this._regenSelections) {\n if (sel.kind === 'user') continue;\n const group = this._tree.getSiblingNodes(groupRoot).filter((n): n is RunNode<TProjection> => n.kind === 'run');\n if (group.length <= 1) continue;\n const newest = group.at(-1);\n if (!newest) continue;\n this._regenSelections.set(groupRoot, { kind: 'auto', selectedRunId: newest.runId });\n }\n }\n\n /**\n * Roll `pending` and `auto` non-head regenerate selections forward to the\n * newest regenerator of their anchor message. Mirrors\n * {@link _resolvePendingRegenSelections} for the non-head group, which lives in\n * a separate selection map (anchored by the regenerate target rather than a\n * sibling-group root): a `user` selection pins and is left untouched; a\n * `pending`/`auto` slot adopts the newest regenerator once one lands. The\n * anchor's predecessor — the key the regenerators file under — is recovered\n * from the owning run's projection.\n */\n private _resolvePendingNonHeadRegenSelections(): void {\n for (const [anchorId, sel] of this._nonHeadRegenSelections) {\n if (sel.kind === 'user') continue;\n const owner = this._runByCodecMessageId(anchorId);\n if (!owner) continue;\n const ownerMsgs = this._codec.getMessages(owner.projection);\n const idx = ownerMsgs.findIndex((m) => m.codecMessageId === anchorId);\n const predecessor = idx > 0 ? ownerMsgs[idx - 1]?.codecMessageId : undefined;\n if (predecessor === undefined) continue;\n const newest = this._nonHeadRegenerators(anchorId, predecessor).at(-1);\n if (!newest) continue;\n this._nonHeadRegenSelections.set(anchorId, { kind: 'auto', selectedRunId: newest.runId });\n }\n }\n\n private _onTreeAblyMessage(msg: Ably.InboundMessage): void {\n // Re-emit only if the message corresponds to a visible Run\n const headers = getTransportHeaders(msg);\n const codecMessageId = headers[HEADER_CODEC_MESSAGE_ID];\n const runId = headers[HEADER_RUN_ID];\n\n if (!codecMessageId && !runId) {\n // Lifecycle / control events with no run/message identity (cancel, error)\n // are always forwarded.\n this._emitter.emit('ably-message', msg);\n return;\n }\n\n if (runId && this._lastVisibleNodeKeySet.has(runId)) {\n this._emitter.emit('ably-message', msg);\n }\n }\n\n private _onTreeRun(event: RunLifecycleEvent): void {\n // Check if the run is already on the visible branch.\n if (this._lastVisibleNodeKeySet.has(event.runId)) {\n this._emitter.emit('run', event);\n return;\n }\n\n // For run-start, use branch metadata to predict visibility before\n // messages arrive. Own runs have optimistic inserts (caught above).\n // Remote runs carry parent/forkOf from the agent.\n if (event.type === 'start' && this._isRunStartVisible(event)) {\n this._lastVisibleNodeKeySet.add(event.runId);\n this._emitter.emit('run', event);\n }\n }\n\n /**\n * Predict whether a run-start's messages will be visible on this view's\n * branch using the parent/forkOf metadata from the event.\n * @param event - The run-start lifecycle event.\n * @returns True if the run is expected to be visible on this view's branch.\n */\n private _isRunStartVisible(event: RunLifecycleEvent & { type: 'start' }): boolean {\n const { parent } = event;\n\n // No parent metadata — can't determine branch, forward as default.\n if (parent === undefined) return true;\n\n // The wire `parent` is a codec-message-id (the prior message). Resolve it\n // kind-blind to its owning NODE — an input node (the user prompt this run\n // replies to) or a prior reply run — and check that node's key against the\n // visible set. Input-node keys are populated into the set by\n // _updateVisibleSnapshot.\n const parentNode = this._tree.getNodeByCodecMessageId(parent);\n if (!parentNode) return true; // unknown parent: forward conservatively\n return this._lastVisibleNodeKeySet.has(nodeKey(parentNode));\n }\n\n private _visibleChanged(newNodes: ConversationNode<TProjection>[]): boolean {\n if (newNodes.length !== this._lastVisibleNodeKeys.length) return true;\n for (const [i, node] of newNodes.entries()) {\n if (nodeKey(node) !== this._lastVisibleNodeKeys[i]) return true;\n if (node.projection !== this._lastVisibleProjections[i]) return true;\n }\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a View that projects a paginated window over a Tree.\n * @param options - The tree, channel, codec, and logger to use.\n * @returns A new {@link DefaultView} instance.\n */\nexport const createView = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage>(\n options: ViewOptions<TInput, TOutput, TProjection, TMessage>,\n): DefaultView<TInput, TOutput, TProjection, TMessage> => new DefaultView(options);\n","/**\n * Core client-side session, parameterized by codec.\n *\n * Composes the conversation Tree to handle the full client-side lifecycle.\n * `connect()` subscribes to the Ably channel (which implicitly attaches it).\n * The same subscription, decoder, and channel are reused across runs.\n *\n * The client publishes user messages directly to the channel via the shared\n * codec encoder. It does not send HTTP: waking an agent is the application's\n * concern — it POSTs `run.toInvocation().toJSON()` to its own endpoint if and\n * when it wants one woken (the Vercel ChatTransport does this for useChat\n * parity). The agent locates the triggering input event by its `event-id`\n * header and publishes run lifecycle events (run-start, run-end) plus assistant\n * chunks, minting and stamping the invocation-id itself. The channel is the\n * durable session record; agents that weren't running at publish time can\n * resume by reading channel rewind.\n */\n\nimport * as Ably from 'ably';\n// Also augments RealtimeChannel with `.object` (ably/liveobjects side-effect).\nimport type * as AblyObjects from 'ably/liveobjects';\n\nimport {\n EVENT_CANCEL,\n EVENT_RUN_END,\n HEADER_CODEC_MESSAGE_ID,\n HEADER_EVENT_ID,\n HEADER_INPUT_CODEC_MESSAGE_ID,\n HEADER_INVOCATION_ID,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_RUN_ID,\n HEADER_RUN_REASON,\n} from '../../constants.js';\nimport { ErrorCode } from '../../errors.js';\nimport { EventEmitter } from '../../event-emitter.js';\nimport type { Logger } from '../../logger.js';\nimport { LogLevel, makeLogger } from '../../logger.js';\nimport { errorCause, errorMessage, getTransportHeaders } from '../../utils.js';\nimport { registerAgent } from '../agent.js';\nimport { resolveChannelModes } from '../channel-options.js';\nimport type { Codec, CodecInputEvent, CodecOutputEvent, Encoder } from '../codec/types.js';\nimport { createWireApplier, type WireApplier } from './decode-fold.js';\nimport { buildRunEndError, buildTransportHeaders } from './headers.js';\nimport { Invocation } from './invocation.js';\nimport { bestEffortDetach, continuityLostError, isContinuityLost, requireConnected } from './session-support.js';\nimport type { DefaultTree } from './tree.js';\nimport { createTree } from './tree.js';\nimport type { ActiveRun, ClientSession, ClientSessionOptions, RunEndReason, SendOptions, Tree, View } from './types.js';\nimport { createView, type DefaultView } from './view.js';\n\n/**\n * Returned from `on()` when the session is already closed — the subscription\n * is silently ignored since no further events will fire.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-function -- intentional no-op\nconst noopUnsubscribe = (): void => {};\n\n// ---------------------------------------------------------------------------\n// Internal state machine\n// ---------------------------------------------------------------------------\n\nenum ClientSessionState {\n READY = 'ready',\n CLOSED = 'closed',\n}\n\n// ---------------------------------------------------------------------------\n// Event map for the session's typed EventEmitter\n// ---------------------------------------------------------------------------\n\ninterface ClientSessionEventsMap {\n error: Ably.ErrorInfo;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CT1\nclass DefaultClientSession<\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n> implements ClientSession<TInput, TOutput, TProjection, TMessage> {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _client: Ably.Realtime;\n private readonly _codec: Codec<TInput, TOutput, TProjection, TMessage>;\n private readonly _logger: Logger;\n\n // Typed event emitter — the session emits only 'error'; all data events live on Tree/View\n private readonly _emitter: EventEmitter<ClientSessionEventsMap>;\n\n // Sub-components\n private readonly _tree: DefaultTree<TInput, TOutput, TProjection>;\n private readonly _view: DefaultView<TInput, TOutput, TProjection, TMessage>;\n private readonly _views = new Set<DefaultView<TInput, TOutput, TProjection, TMessage>>();\n /**\n * The Tree's single decode-and-apply engine, binding the session's one\n * decoder instance. Shared by the live decode loop and every View's history\n * replay so an attach-boundary in-flight stream is continued (not\n * re-started) by hydration, and re-delivered content decodes to nothing.\n */\n private readonly _applier: WireApplier;\n /**\n * Shared encoder for the lifetime of the session. The client only ever\n * uses `publishInput` (input wire), so the encoder's stream tracker map\n * stays empty across the session. Closed once on session close.\n */\n private readonly _encoder: Encoder<TInput, TOutput>;\n\n // Spec: AIT-CT10, AIT-CT10a\n readonly tree: Tree<TOutput, TProjection>;\n readonly view: View<TInput, TMessage>;\n\n // Channel subscription is established lazily on connect()\n private _connectPromise: Promise<void> | undefined;\n private readonly _onMessage: (msg: Ably.InboundMessage) => void;\n\n private _state = ClientSessionState.READY;\n private _hasAttachedOnce: boolean;\n private readonly _onChannelStateChange: Ably.channelEventCallback;\n\n /**\n * Backing settlers for each in-flight run's `ActiveRun.runId` promise.\n * Resolved with the agent-minted run-id when the matching `ai-run-start`\n * (fresh send) or `ai-run-resume` (continuation) is observed; rejected if\n * the session closes first. There is no deadline —\n * `send()` resolves on publish and does not block on run-start.\n *\n * Keyed by the triggering input's codec-message-id — the handle the client\n * owns at send time, which the agent echoes back on run-start as\n * `input-codec-message-id`. This is uniform across fresh sends and\n * continuations (a continuation is itself an input event — tool-approval or\n * tool-result — with its own codec-message-id), so reconciliation never\n * depends on a client-minted run/invocation id.\n */\n private readonly _pendingRunStarts = new Map<\n string,\n { resolve: (runId: string) => void; reject: (e: Ably.ErrorInfo) => void }\n >();\n\n constructor(options: ClientSessionOptions<TInput, TOutput, TProjection, TMessage>) {\n // Spec: AIT-CT1a, AIT-CT1a2 — register this SDK on both the connection\n // (options.agents) and channel-attach (params.agent) paths. Idempotent\n // across sessions sharing one client.\n const channelOptions: Ably.ChannelOptions = registerAgent(options.client, options.codec);\n // Spec: AIT-CT23 — request object modes etc. when channelModes opts in.\n const modes = resolveChannelModes(options.channelModes);\n if (modes) channelOptions.modes = modes;\n this._channel = options.client.channels.get(options.channelName, channelOptions);\n this._client = options.client;\n this._codec = options.codec;\n this._logger = (options.logger ?? makeLogger({ logLevel: LogLevel.Silent })).withContext({\n component: 'ClientSession',\n });\n\n this._emitter = new EventEmitter<ClientSessionEventsMap>(this._logger);\n this._hasAttachedOnce = this._channel.state === 'attached';\n\n // Compose sub-components\n this._tree = createTree<TInput, TOutput, TProjection>(this._codec, this._logger);\n this._applier = createWireApplier(this._tree, this._codec.createDecoder());\n this._view = createView<TInput, TOutput, TProjection, TMessage>({\n tree: this._tree,\n channel: this._channel,\n codec: this._codec,\n applier: this._applier,\n sendDelegate: this._internalSend.bind(this),\n logger: this._logger,\n onClose: () => this._views.delete(this._view),\n });\n this._encoder = this._codec.createEncoder(this._channel);\n\n this._views.add(this._view);\n\n // Public accessors (typed as narrow interfaces)\n this.tree = this._tree;\n this.view = this._view;\n\n // Seed tree with initial messages — the session assigns a codecMessageId\n // per seed message. Each seed becomes a run-less input node (no run-id —\n // the client never mints one); the parent chain mirrors the original seed\n // sequence (a user→user input chain the Tree threads kind-blind).\n if (options.messages) {\n let prevMsgId: string | undefined;\n for (const msg of options.messages) {\n const codecMessageId = crypto.randomUUID();\n const seedHeaders: Record<string, string> = {\n [HEADER_CODEC_MESSAGE_ID]: codecMessageId,\n [HEADER_ROLE]: 'user',\n };\n if (prevMsgId) seedHeaders[HEADER_PARENT] = prevMsgId;\n this._tree.applyMessage({ inputs: [this._codec.createUserMessage(msg)], outputs: [] }, seedHeaders);\n prevMsgId = codecMessageId;\n }\n }\n\n // Spec: AIT-CT2\n // Listener function reference — bound now so it can be unsubscribed on close.\n this._onMessage = (ablyMessage: Ably.InboundMessage) => {\n this._handleMessage(ablyMessage);\n };\n\n // Listen for channel state changes that break message continuity.\n // _hasAttachedOnce is seeded from the channel's current state so that\n // pre-attached channels are handled correctly. It distinguishes the\n // initial attach (expected) from a genuine discontinuity.\n this._onChannelStateChange = (stateChange: Ably.ChannelStateChange) => {\n this._handleChannelStateChange(stateChange);\n };\n this._channel.on(this._onChannelStateChange);\n }\n\n // ---------------------------------------------------------------------------\n // Public accessors\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT21\n get presence(): Ably.RealtimePresence {\n return this._channel.presence;\n }\n\n // Spec: AIT-CT22\n get object(): AblyObjects.RealtimeObject {\n return this._channel.object;\n }\n\n // ---------------------------------------------------------------------------\n // Public connection API\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT2\n // eslint-disable-next-line @typescript-eslint/promise-function-async -- preserve reference equality across calls\n connect(): Promise<void> {\n if (this._state === ClientSessionState.CLOSED) {\n return Promise.reject(new Ably.ErrorInfo('unable to connect; session is closed', ErrorCode.SessionClosed, 400));\n }\n if (this._connectPromise) return this._connectPromise;\n\n this._logger.trace('DefaultClientSession.connect();');\n // Subscribe before attach (RTL7g) — subscribe implicitly attaches the channel.\n this._connectPromise = this._channel.subscribe(this._onMessage).then(\n () => {\n this._logger.debug('DefaultClientSession.connect(); subscribed and attached');\n },\n (error: unknown) => {\n const errInfo = new Ably.ErrorInfo(\n `unable to subscribe to channel; ${errorMessage(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n errorCause(error),\n );\n this._logger.error('DefaultClientSession.connect(); subscribe failed');\n this._emitter.emit('error', errInfo);\n throw errInfo;\n },\n );\n return this._connectPromise;\n }\n\n /**\n * The session's identity, read from the Ably client's `auth.clientId`. Read\n * lazily (never cached at construction): under token auth the client only\n * learns its clientId once the connection reaches CONNECTED, which is\n * guaranteed by the time any write runs — every write awaits `connect()`,\n * and the channel cannot attach before the connection is CONNECTED. A\n * connection with no concrete identity (anonymous, or a wildcard `*` token)\n * resolves to `undefined`, so no run/input client id is stamped.\n * @returns The client's concrete identity, or `undefined` if it has none.\n */\n // Spec: AIT-CT1b\n private _resolveClientId(): string | undefined {\n const clientId = this._client.auth.clientId;\n return clientId && clientId !== '*' ? clientId : undefined;\n }\n\n private async _requireConnected(method: string): Promise<void> {\n return requireConnected(this._connectPromise, method);\n }\n\n // ---------------------------------------------------------------------------\n // Message subscription handler\n // ---------------------------------------------------------------------------\n\n private _handleMessage(ablyMessage: Ably.InboundMessage): void {\n if (this._state === ClientSessionState.CLOSED) return;\n\n try {\n // Spec: AIT-CT16a\n // Live-only: surface an agent error carried on a run-end BEFORE applying\n // it, preserving the original 'error'-before-tree-'run' emit ordering.\n // Consumers that expose a per-run stream (e.g. the Vercel ChatTransport)\n // error their stream off this event. The agent only publishes run-end\n // after run-start, so no pending-run-start tracker is outstanding.\n if (ablyMessage.name === EVENT_RUN_END) {\n const headers = getTransportHeaders(ablyMessage);\n // CAST: agent always writes a valid RunEndReason; default to 'complete' for robustness\n const reason = (headers[HEADER_RUN_REASON] ?? 'complete') as RunEndReason;\n if (reason === 'error') {\n const errInfo = buildRunEndError(headers);\n this._logger.error('ClientSession._handleMessage(); agent error received', {\n runId: headers[HEADER_RUN_ID],\n invocationId: headers[HEADER_INVOCATION_ID],\n code: errInfo.code,\n });\n this._emitter.emit('error', errInfo);\n }\n }\n\n // Reconstruct the tree via the Tree's single decode-and-apply engine —\n // the same applier (and decoder instance) the Views' history replay\n // uses, so the live loop can't drift from it and an attach-boundary\n // stream isn't double-decoded.\n const event = this._applier.apply(ablyMessage);\n\n // Live-only: resolve the pending `runId` promise on a fresh run-start or\n // a continuation run-resume. Key by the echoed `input-codec-message-id`\n // — the mirror of the arming key on `_pendingRunStarts` (see that\n // field's JSDoc). Every send carries at least one input, so the agent\n // always echoes it.\n if (event && (event.type === 'start' || event.type === 'resume')) {\n const startedKey = getTransportHeaders(ablyMessage)[HEADER_INPUT_CODEC_MESSAGE_ID];\n if (startedKey !== undefined) {\n const pending = this._pendingRunStarts.get(startedKey);\n if (pending) {\n this._pendingRunStarts.delete(startedKey);\n // Resolve the run handle's `runId` promise with the agent-minted id.\n pending.resolve(event.runId);\n }\n }\n }\n\n // Emit ably-message AFTER the apply so View subscribers can find the\n // owning node in `_lastVisibleNodeKeySet` (keyed by run-id for reply runs\n // and codec-message-id for inputs), which is refreshed by the tree\n // 'update' events the apply triggers.\n this._tree.emitAblyMessage(ablyMessage);\n } catch (error) {\n this._emitter.emit(\n 'error',\n new Ably.ErrorInfo(\n `unable to process channel message; ${errorMessage(error)}`,\n ErrorCode.SessionSubscriptionError,\n 500,\n errorCause(error),\n ),\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // Channel state change handler\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT19, AIT-CT19a\n private _handleChannelStateChange(stateChange: Ably.ChannelStateChange): void {\n if (this._state === ClientSessionState.CLOSED) return;\n\n const { current, resumed } = stateChange;\n\n // Track the initial attach so we don't treat it as a discontinuity\n if (current === 'attached' && !this._hasAttachedOnce) {\n this._hasAttachedOnce = true;\n return;\n }\n\n if (!isContinuityLost(stateChange)) return;\n\n this._logger.error('ClientSession._handleChannelStateChange(); channel continuity lost', {\n current,\n resumed,\n previous: stateChange.previous,\n });\n\n const err = continuityLostError(stateChange, 'deliver events');\n\n // Surface the loss via the session `error` event. Consumers that expose a\n // per-run stream (e.g. the Vercel ChatTransport) error their stream off\n // this event; observer-run state lives entirely in the Tree's projection\n // and stays consistent regardless of continuity loss.\n this._emitter.emit('error', err);\n }\n\n // ---------------------------------------------------------------------------\n // Cancel helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Tear down local state for a send whose channel publish failed.\n * Idempotent.\n * @param codecMessageIds - The codec-message-ids of the failed send's\n * optimistic input nodes (the client mints no run-id, so the optimistic\n * inserts are keyed by their codec-message-ids).\n */\n private _cleanupFailedSend(codecMessageIds: string[]): void {\n for (const codecMessageId of codecMessageIds) {\n // Drop the optimistic input node only if the publish never produced a\n // server-assigned serial (i.e. nothing live observed it). A server-acked\n // node is part of the canonical channel state and must stay; the View /\n // observers already see it. A fresh send's optimistic inserts are input\n // nodes (keyed by codec-message-id).\n const node = this._tree.getNodeByCodecMessageId(codecMessageId);\n if (node?.kind === 'input' && node.serial === undefined) {\n // An input node's key is its codec-message-id, so delete by it directly.\n this._tree.delete(node.codecMessageId);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT10b\n createView(): View<TInput, TMessage> {\n if (this._state === ClientSessionState.CLOSED) {\n throw new Ably.ErrorInfo('unable to create view; session is closed', ErrorCode.SessionClosed, 400);\n }\n this._logger.trace('DefaultClientSession.createView();');\n const view = createView<TInput, TOutput, TProjection, TMessage>({\n tree: this._tree,\n channel: this._channel,\n codec: this._codec,\n applier: this._applier,\n sendDelegate: this._internalSend.bind(this),\n logger: this._logger,\n onClose: () => this._views.delete(view),\n });\n this._views.add(view);\n return view;\n }\n\n // Spec: AIT-CT3, AIT-CT4\n private async _internalSend(\n input: TInput[],\n sendOptions: SendOptions | undefined,\n parentCodecMessageId: string | undefined,\n ): Promise<ActiveRun> {\n if (this._state === ClientSessionState.CLOSED) {\n throw new Ably.ErrorInfo('unable to send; session is closed', ErrorCode.SessionClosed, 400);\n }\n await this._requireConnected('send');\n // CAST: re-check after await — close() may have been called while waiting for connect.\n // TypeScript's control flow narrows _state after the first check, but the\n // await yields and close() can mutate _state concurrently.\n if ((this._state as ClientSessionState) === ClientSessionState.CLOSED) {\n throw new Ably.ErrorInfo('unable to send; session is closed', ErrorCode.SessionClosed, 400);\n }\n\n // Spec: AIT-CT20\n const state = this._channel.state;\n if (state !== 'attached' && state !== 'attaching') {\n throw new Ably.ErrorInfo(`unable to send; channel is ${state}`, ErrorCode.ChannelNotReady, 400);\n }\n\n this._logger.trace('ClientSession._internalSend();');\n\n const isContinuation = sendOptions?.runId !== undefined;\n\n // The agent mints run-ids, not the client. A fresh send carries no run-id\n // (the agent mints it and echoes it on run-start); only a continuation\n // reuses the existing run-id the caller passed.\n const runId = sendOptions?.runId;\n\n // Spec: AIT-CT3d\n // Auto-compute parent from the visible branch tail when not explicitly\n // provided. The View pre-resolves the codec-message-id of the last visible message\n // since the session is codec-agnostic and can't extract it from TMessage.\n let autoParent: string | undefined;\n if (sendOptions?.parent === undefined && !sendOptions?.forkOf) {\n autoParent = parentCodecMessageId;\n }\n\n const codecMessageIds = new Set<string>();\n interface ItemState {\n input: TInput;\n codecMessageId: string;\n inputEventId: string;\n headers: Record<string, string>;\n /** Inputs that reference an existing codec-message without contributing fresh local content (regenerate, tool resolutions) are wire-only — no optimistic projection fold. Fresh user-messages always fold, even when they pin their own codecMessageId. */\n isWireOnly: boolean;\n }\n const items: ItemState[] = [];\n\n // Per-input wire prep: read routing fields off the input directly, then\n // mint per-event ids and build transport headers. Regenerate inputs are\n // wire-only (no optimistic fold); other inputs fold into the projection\n // optimistically.\n for (const entry of input) {\n const inputEventId = crypto.randomUUID();\n // Use the input's `codecMessageId` when set (e.g. tool resolution\n // targeting the prior assistant); otherwise mint a fresh id.\n const codecMessageId = entry.codecMessageId ?? crypto.randomUUID();\n codecMessageIds.add(codecMessageId);\n\n // Inputs that reference an existing message (regenerate, tool\n // resolutions targeting an assistant) are wire-only — no optimistic\n // fold needed because either the receiving content doesn't\n // materialise on this side (regenerate) or the target already exists\n // and will be amended when the wire echoes back.\n //\n // A fresh `user-message` is never wire-only, even on the rare path\n // where it carries an explicit `codecMessageId`: it is new content that\n // must fold into the local projection immediately. Excluding it here\n // keeps the optimistic user bubble from depending on the channel\n // round-trip. (The session mints the codec-message-id for fresh user\n // messages; the caller's `message.id` is preserved but never used as\n // the correlation key.)\n const isWireOnly =\n entry.kind !== 'user-message' && (entry.kind === 'regenerate' || entry.codecMessageId !== undefined);\n\n // The input's own routing fields override the auto-parent /\n // sendOptions defaults. For regenerate inputs, `target` becomes the\n // `msg-regenerate` wire header. The fork anchor comes from\n // `sendOptions.forkOf` (set by `View.edit`). The transport reads\n // these directly without runtime classification.\n const parent = entry.parent ?? (sendOptions?.parent === undefined ? autoParent : sendOptions.parent);\n const forkOf = sendOptions?.forkOf;\n const regenerates = entry.kind === 'regenerate' ? entry.target : undefined;\n\n const headers = buildTransportHeaders({\n role: 'user',\n runId,\n codecMessageId,\n runClientId: this._resolveClientId(),\n ...(parent !== undefined && { parent }),\n ...(forkOf !== undefined && { forkOf }),\n ...(regenerates !== undefined && { regenerates }),\n inputEventId,\n });\n\n // Spec: AIT-CT3c — optimistic fold for non-wire-only inputs.\n if (!isWireOnly) {\n this._tree.applyMessage({ inputs: [entry], outputs: [] }, headers);\n }\n\n items.push({ input: entry, codecMessageId, inputEventId, headers, isWireOnly });\n\n // Spec: AIT-CT3e — chain subsequent inputs off the previous one when\n // auto-parenting is in effect.\n if (!isWireOnly && sendOptions?.parent === undefined && !sendOptions?.forkOf && entry.parent === undefined) {\n autoParent = codecMessageId;\n }\n }\n\n // The trigger event is the last input — the one the agent looks up on the\n // channel via `event-id`, surfaced on `ActiveRun` (and via `toInvocation()`)\n // so the application can point an invocation at it. Its codec-message-id is\n // the handle the client owns at send time; the agent echoes it back on\n // run-start as `input-codec-message-id`, and it keys the run-start tracker.\n const triggerItem = items.at(-1);\n if (triggerItem === undefined) {\n // Every send must carry at least one input — only new input starts or\n // continues a run. The loop above produced no items, so nothing was\n // published or folded optimistically.\n throw new Ably.ErrorInfo(\n 'unable to send; inputs array is empty (include at least one input)',\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const triggerInputEventId = triggerItem.inputEventId;\n const startedKey = triggerItem.codecMessageId;\n\n // Arm the run-start tracker backing the returned `ActiveRun.runId` promise.\n // The run-start handler resolves it with the agent-minted run-id when this\n // send's `ai-run-start` is observed; close() rejects it on teardown. No\n // deadline — `send()` resolves on publish; callers bound the wait by racing\n // `run.runId` against their own timeout.\n //\n // Key on the arming side mirrors the resolve side — see `_pendingRunStarts`\n // for the full keying invariant. The executor runs synchronously, so the\n // tracker entry is registered before `new Promise` returns.\n const runIdPromise = new Promise<string>((resolve, reject) => {\n this._pendingRunStarts.set(startedKey, { resolve, reject });\n });\n // Suppress unhandled-rejection warnings for callers that never await\n // `run.runId`; the caller still observes the rejection if it does await.\n runIdPromise.catch(() => {\n /* observed via run.runId, if at all */\n });\n\n // Publish each input in original order via the shared encoder. The\n // codec routes user-message inputs into a per-part discrete batch and\n // tool-resolution / regenerate inputs into a single discrete write —\n // all on the `ai-input` wire.\n const publishPromise = (async () => {\n try {\n for (const item of items) {\n await this._encoder.publishInput(item.input, {\n extras: { headers: item.headers },\n messageId: item.codecMessageId,\n });\n }\n } catch (error) {\n const cause = errorCause(error);\n const isPermission = cause?.statusCode === 401 || cause?.statusCode === 403;\n const err = new Ably.ErrorInfo(\n isPermission\n ? `unable to publish events; missing publish capability on the channel`\n : `unable to publish events; ${errorMessage(error)}`,\n isPermission ? ErrorCode.InsufficientCapability : ErrorCode.SessionSendFailed,\n isPermission ? 401 : 500,\n cause,\n );\n this._emitter.emit('error', err);\n // The input never reached the channel — there is no run to wait on.\n // Drop the run-start tracker so close() doesn't later reject an orphan.\n this._pendingRunStarts.delete(startedKey);\n // Continuations didn't insert optimistic nodes, so there is nothing to\n // clear for them — only a fresh send's optimistic input nodes need\n // removing, keyed by their codec-message-ids (the client mints no runId).\n if (!isContinuation) this._cleanupFailedSend([...codecMessageIds]);\n throw err;\n }\n })();\n\n // `send()` resolves once the input is published. The core never sends\n // HTTP — waking an agent is the application's concern. Callers POST\n // `run.toInvocation().toJSON()` to their endpoint if they want one woken,\n // and await `run.runId` if they need to know it was picked up.\n await publishPromise;\n\n return {\n inputCodecMessageId: startedKey,\n runId: runIdPromise,\n inputEventId: triggerInputEventId,\n // The agent mints the run-id, so a fresh run has none until run-start.\n // Cancel synchronously by the triggering input's codec-message-id (the\n // handle the client owns at send time, = `inputCodecMessageId`): the\n // agent resolves it to the run once its input-event lookup completes, and\n // buffers a cancel that arrives before then so an early cancel is honoured\n // rather than dropped. A continuation additionally carries its known\n // run-id so the agent can match the run directly.\n cancel: async () => {\n await this._publishCancel({\n inputCodecMessageId: startedKey,\n ...(runId !== undefined && { runId }),\n });\n },\n optimisticCodecMessageIds: [...codecMessageIds],\n toInvocation: () =>\n // The invocation body carries no run-id: run identity lives on the\n // channel (the agent mints a fresh run-id, or reads a continuation's\n // from the triggering input event, which carries the reused run-id).\n Invocation.fromJSON({\n inputEventId: triggerInputEventId,\n sessionName: this._channel.name,\n }),\n };\n }\n\n // Spec: AIT-CT7, AIT-CT7a\n async cancel(runId: string): Promise<void> {\n return this._publishCancel({ runId });\n }\n\n /**\n * Publish an `ai-cancel` signal. The agent resolves the target run by\n * whichever identifier is present:\n *\n * - `runId` — a continuation, whose run-id the caller already knows.\n * - `inputCodecMessageId` — a fresh send, whose run-id the agent mints at\n * run-start. The client can only key the cancel by the triggering input's\n * codec-message-id (the `ActiveRun.inputCodecMessageId`) it owns at send\n * time; the agent resolves it to the run once its input-event lookup\n * completes, buffering a cancel that arrives before then.\n *\n * Both may be present (a continuation knows its run-id AND published an\n * input). An `event-id` is always stamped so channel rewind redelivers the\n * cancel to a per-request / serverless agent that attaches after it was\n * published.\n *\n * Publishing the cancel signal is all the core does. The consumer-facing\n * stream (if any) lives in the layer that built it — e.g. the Vercel\n * ChatTransport closes its stream on cancel — and the Tree's RunNode is left\n * intact so late agent events (a cancel append, a trailing\n * `status: cancelled`) still fold into the Run's projection.\n * @param target - The run identifier(s) to cancel. At least one of `runId` /\n * `inputCodecMessageId` must be set.\n * @param target.runId - The run-id to cancel (continuations).\n * @param target.inputCodecMessageId - The triggering input's\n * codec-message-id to cancel (fresh sends, before run-start).\n */\n private async _publishCancel(target: { runId?: string; inputCodecMessageId?: string }): Promise<void> {\n if (this._state === ClientSessionState.CLOSED) return;\n await this._requireConnected('cancel');\n // CAST: re-check after await — close() may have been called while waiting for connect.\n if ((this._state as ClientSessionState) === ClientSessionState.CLOSED) return;\n this._logger.debug('ClientSession._publishCancel();', {\n runId: target.runId,\n inputCodecMessageId: target.inputCodecMessageId,\n });\n\n const headers: Record<string, string> = {\n // Stamp a per-cancel event-id so channel rewind redelivers this cancel\n // to an agent that attaches after it was published.\n [HEADER_EVENT_ID]: crypto.randomUUID(),\n };\n if (target.runId !== undefined) headers[HEADER_RUN_ID] = target.runId;\n if (target.inputCodecMessageId !== undefined) headers[HEADER_INPUT_CODEC_MESSAGE_ID] = target.inputCodecMessageId;\n\n await this._channel.publish({\n name: EVENT_CANCEL,\n extras: { ai: { transport: headers } },\n });\n }\n\n // Spec: AIT-CT8, AIT-CT8c, AIT-CT8d\n on(event: 'error', handler: (error: Ably.ErrorInfo) => void): () => void {\n if (this._state === ClientSessionState.CLOSED) return noopUnsubscribe;\n // CAST: the overload signature enforces the correct handler type.\n const cb = handler;\n this._emitter.on(event, cb);\n return () => {\n this._emitter.off(event, cb);\n };\n }\n\n // Spec: AIT-CT12, AIT-CT12b, AIT-CT10c\n async close(): Promise<void> {\n if (this._state === ClientSessionState.CLOSED) return;\n this._state = ClientSessionState.CLOSED;\n this._logger.info('ClientSession.close();');\n\n if (this._connectPromise) {\n this._channel.unsubscribe(this._onMessage);\n }\n this._channel.off(this._onChannelStateChange);\n\n this._emitter.off();\n for (const v of this._views) v.close();\n this._views.clear();\n // Reject any in-flight `run.runId` promises so callers awaiting run-start\n // settle rather than hang.\n if (this._pendingRunStarts.size > 0) {\n const closedErr = new Ably.ErrorInfo('unable to await run-start; session closed', ErrorCode.SessionClosed, 400);\n for (const pending of this._pendingRunStarts.values()) {\n pending.reject(closedErr);\n }\n this._pendingRunStarts.clear();\n }\n\n // Best-effort encoder close — flushes any pending stream operations.\n // The client only uses the discrete input path (publishInput), so this is\n // typically a no-op, but it releases any internal resources cleanly.\n try {\n await this._encoder.close();\n } catch {\n // Swallow: encoder close is best-effort during teardown\n }\n\n await bestEffortDetach(this._channel, this._connectPromise, this._logger, 'ClientSession');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a client-side session that manages conversation state over an Ably channel.\n *\n * The caller owns the client's lifecycle; the session owns its channel.\n * The session is created in a not-yet-connected state — callers must\n * `await session.connect()` before `send`, `regenerate`, `edit`, `update`,\n * or `cancel`.\n * @param options - Configuration for the client session.\n * @returns A new {@link ClientSession} instance.\n */\nexport const createClientSession = <\n TInput extends CodecInputEvent,\n TOutput extends CodecOutputEvent,\n TProjection,\n TMessage,\n>(\n options: ClientSessionOptions<TInput, TOutput, TProjection, TMessage>,\n): ClientSession<TInput, TOutput, TProjection, TMessage> => new DefaultClientSession(options);\n","/**\n * Vercel AI SDK transport wrappers that pre-bind the UIMessageCodec.\n *\n * These are convenience factories so consumers don't need to pass the codec\n * explicitly when using the Vercel AI SDK integration.\n *\n * ```ts\n * import { createClientSession } from '@ably/ai-transport/vercel';\n *\n * const session = createClientSession({ client, channelName: 'ai:demo' });\n * await session.connect();\n * ```\n */\n\n// Chat transport adapter\nexport type { ChatTransport, ChatTransportOptions, SendMessagesRequestContext } from './chat-transport.js';\nexport { createChatTransport } from './chat-transport.js';\n\nimport type * as AI from 'ai';\n\nimport { createAgentSession as createCoreAgentSession } from '../../core/transport/agent-session.js';\nimport { createClientSession as createCoreClientSession } from '../../core/transport/client-session.js';\nimport type {\n AgentSession,\n AgentSessionOptions,\n ClientSession,\n ClientSessionOptions,\n} from '../../core/transport/types.js';\nimport { UIMessageCodec, type VercelInput, type VercelOutput, type VercelProjection } from '../codec/index.js';\n\n/** Core client session options with Vercel AI SDK types pre-applied. */\ntype CoreClientOpts = ClientSessionOptions<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;\n\n/** Options for creating a Vercel client session. Same as core options but without the codec field. */\nexport type VercelClientSessionOptions = Omit<CoreClientOpts, 'codec'>;\n\n/** Options for creating a Vercel agent session. Same as core options but without the codec field. */\nexport type VercelAgentSessionOptions = Omit<\n AgentSessionOptions<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>,\n 'codec'\n>;\n\n/**\n * Create a client-side session pre-configured with the Vercel AI SDK codec.\n *\n * Equivalent to calling the core `createClientSession` with `codec: UIMessageCodec`.\n * The core session is a pure Ably-channel transport — it never sends HTTP.\n * To wake a serverless agent over HTTP, POST `run.toInvocation().toJSON()`\n * yourself, or use `createChatTransport` (which does it for useChat parity).\n * @param options - Configuration for the client session (codec is provided automatically).\n * @returns A new {@link ClientSession} for Vercel AI SDK UIMessage/UIMessageChunk types.\n */\nexport const createClientSession = (\n options: VercelClientSessionOptions,\n): ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage> =>\n createCoreClientSession({ ...options, codec: UIMessageCodec });\n\n/**\n * Create an agent (server-side) session pre-configured with the Vercel AI SDK codec.\n *\n * Equivalent to calling the core `createAgentSession` with `codec: UIMessageCodec`.\n * @param options - Configuration for the agent session (codec is provided automatically).\n * @returns A new {@link AgentSession} for Vercel AI SDK UIMessage/UIMessageChunk types.\n */\nexport const createAgentSession = (\n options: VercelAgentSessionOptions,\n): AgentSession<VercelOutput, VercelProjection, AI.UIMessage> =>\n createCoreAgentSession({ ...options, codec: UIMessageCodec });\n","import * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport type { RunEndReason, StreamResult } from '../core/transport/types.js';\nimport { ErrorCode } from '../errors.js';\n\n/**\n * The outcome of a Vercel `streamText` response piped through `Run.pipe`.\n * Discriminated on `reason`: `'suspend'` means the run should pause; the\n * non-`'suspend'` arms describe how it terminated, and an `'error'` outcome\n * always carries `error`.\n *\n * This is a *description of what the Vercel run resulted in*, not a command to\n * the SDK. The common case maps cleanly onto one transport action — `'suspend'`\n * → `Run.suspend()`, everything else → `Run.end()` — and to make that case a\n * one-liner the non-`'suspend'` arms are deliberately assignable to\n * {@link RunEndParams}, so after a `suspend` guard the whole object passes\n * straight to `Run.end(outcome)`. That assignability is a convenience for this\n * adapter, not a constraint on what an outcome can mean: responding to an\n * outcome may also involve work outside this SDK (persisting a result,\n * notifying a human, triggering a downstream workflow), and the developer is\n * free to do that around the terminal call.\n *\n * The type is Vercel-specific by design. Outcomes are the layer where agent\n * SDKs diverge most — both in what they report (the `'suspend'` arm exists only\n * because Vercel surfaces unexecuted tool calls as a non-terminal finish) and\n * in what a developer must do in response. A different SDK's outcome type would\n * have different arms; hence each adapter names its own rather than sharing a\n * single core `RunOutcome`. The vocabulary it bottoms out in\n * ({@link RunEndParams}, `Run.suspend`/`Run.end`) is the shared, codec-agnostic\n * part that does live in core.\n */\nexport type VercelRunOutcome =\n | {\n /**\n * The LLM requested tools the SDK did not auto-execute, so the run\n * pauses rather than ending — call `Run.suspend()`.\n */\n reason: 'suspend';\n /** Never present for a suspend outcome. */\n error?: never;\n }\n | {\n /** A non-error terminal reason; pass the outcome to `Run.end()`. */\n reason: Exclude<RunEndReason, 'error'>;\n /** Never present for a non-error outcome. */\n error?: never;\n }\n | {\n /** The run ended in error; pass the outcome to `Run.end()`. */\n reason: Extract<RunEndReason, 'error'>;\n /**\n * The terminal error: the underlying stream / `finishReason` failure\n * wrapped as an `Ably.ErrorInfo` (code `StreamError`).\n */\n error: Ably.ErrorInfo;\n };\n\n/**\n * Derive the {@link VercelRunOutcome} for a Vercel `streamText` response that\n * was piped through `Run.pipe`. Preserves transport-level outcomes\n * (`'cancelled'`, `'error'`) from the pipe result; when the pipe completed\n * naturally, awaits Vercel's `finishReason` and returns `'suspend'` for\n * `'tool-calls'` (the LLM requested tools the SDK did not auto-execute, so the\n * run should suspend rather than end), or `'complete'` otherwise.\n *\n * Surfaces the failure for both error shapes so the caller can forward it to\n * `Run.end(reason, error)`: a stream that threw (`pipeResult.error`) and a\n * `finishReason` that rejected with a non-abort error (e.g.\n * `NoOutputGeneratedError`, network blow-ups). The error is wrapped as an\n * `Ably.ErrorInfo` (code `StreamError`). A stream that already produced a\n * codec-level error chunk is unaffected — stamping run-end is the\n * codec-agnostic baseline that any consumer can read.\n *\n * Tolerates `finishReason` rejection. Vercel AI SDK v6 rejects\n * `streamText().finishReason` with the abort signal's reason when the stream\n * is aborted before any step completes, and rejects with\n * `NoOutputGeneratedError` when the model produced nothing at all. Without\n * this guard the rejection would bubble out of the route handler's `after()`\n * block, skip the developer's `Run.end(...)` call, and leave the run with no\n * `ai-run-end` event on the channel — so observers' UIs stay stuck on\n * `streaming` indefinitely.\n *\n * Saves callers from interpreting Vercel domain semantics inline at the end\n * of every route handler.\n * @param pipeResult - The result returned by `Run.pipe`.\n * @param finishReason - The `finishReason` promise from a `streamText` result.\n * @returns The {@link VercelRunOutcome}: the terminal `reason` (or `'suspend'`)\n * and, when `reason` is `'error'`, the wrapped `error` to pass to `Run.end`.\n */\nexport const vercelRunOutcome = async (\n pipeResult: StreamResult,\n finishReason: PromiseLike<AI.FinishReason>,\n): Promise<VercelRunOutcome> => {\n if (pipeResult.reason !== 'complete') {\n // Vercel's `result.finishReason` getter creates the underlying Promise\n // eagerly, before the caller hands it to us. When `streamText` is\n // aborted before any step completes, Vercel rejects that Promise with\n // the abort signal's reason — typically a DOMException whose\n // `.message` is a read-only getter. Returning early without ever\n // attaching a handler lets Node report it as an unhandled rejection;\n // Next.js' dev bundler then tries to mutate `.message` for logging\n // and crashes with a confusing TypeError. Attach a silent handler so\n // the rejection is observed and discarded — the transport-level\n // `pipeResult.reason` is already what we return.\n Promise.resolve(finishReason).catch(() => {\n /* intentionally discarded; reason already known from pipeResult */\n });\n if (pipeResult.reason === 'error') {\n return { reason: 'error', error: _toErrorInfo(pipeResult.error) };\n }\n return { reason: pipeResult.reason };\n }\n try {\n const finish = await finishReason;\n if (finish === 'tool-calls') return { reason: 'suspend' };\n return { reason: 'complete' };\n } catch (error) {\n // Abort-shaped rejections are surfaced from streamText when the run was\n // cancelled before any step finished — treat the run as cancelled so the\n // observable lifecycle matches the cancel that triggered it. Everything\n // else is a real error (e.g. NoOutputGeneratedError, network blow-ups);\n // surface it as such — wrapped so the caller can stamp it on run-end — so\n // the developer sees the failure rather than a silent cancel.\n if (_isAbortLikeError(error)) return { reason: 'cancelled' };\n return { reason: 'error', error: _toErrorInfo(error) };\n }\n};\n\n/**\n * Wrap a caught stream / `finishReason` failure as an `Ably.ErrorInfo` so it\n * can be passed to `Run.end(reason, error)`. An error that is already an\n * `Ably.ErrorInfo` is returned unchanged; anything else is wrapped with code\n * `StreamError`, mirroring how `Run.pipe` wraps stream errors for `onError`.\n * @param error - The caught error (or `undefined` when the stream reported none).\n * @returns The error as an `Ably.ErrorInfo`.\n */\nconst _toErrorInfo = (error: unknown): Ably.ErrorInfo => {\n if (error instanceof Ably.ErrorInfo) return error;\n const message = error instanceof Error ? error.message : String(error);\n return new Ably.ErrorInfo(`unable to complete run; ${message}`, ErrorCode.StreamError, 500);\n};\n\n/**\n * Heuristic for \"this error came from an AbortSignal aborting\".\n * Covers `DOMException` aborts (browser / Node 20+ `streamText`),\n * plain `Error` objects whose `name` is `'AbortError'`, and anything\n * else carrying that conventional name. Avoids importing\n * `@ai-sdk/provider-utils` just for `isAbortError`.\n * @param error - The error to test.\n * @returns `true` if the error looks like an abort.\n */\nconst _isAbortLikeError = (error: unknown): boolean => {\n if (typeof error !== 'object' || error === null) return false;\n const name = (error as { name?: unknown }).name;\n return name === 'AbortError';\n};\n"],"mappings":";;AAgBA,IAAa,IAAgB,UAGhB,IAAgB,UAGhB,IAAmB,aAGnB,IAAkB,YAOlB,IAAgB,UAGhB,IAAuB,iBAYvB,IAAkB,YAGlB,IAA0B,oBAG1B,IAAuB,iBAavB,IAAyB,mBAGzB,IAAc,QAOd,IAAgB,UAGhB,IAAiB,WAYjB,IAAwB,kBAOxB,IAAoB,cAYpB,IAAgC,0BAOhC,IAAoB,cAGpB,IAAuB,iBAkBvB,IAAe,aAGf,IAAkB,gBAQlB,IAAoB,kBAQpB,IAAmB,iBAGnB,IAAgB,cAQhB,IAAkB,aAQlB,KAAiB,YC3KlB,IAAL,yBAAA,GAAA;QAIL,EAAA,EAAA,aAAA,OAAA,cAKA,EAAA,EAAA,kBAAA,SAAA,mBAMA,EAAA,EAAA,yBAAA,SAAA,0BAQA,EAAA,EAAA,wBAAA,SAAA,yBAKA,EAAA,EAAA,2BAAA,UAAA,4BAKA,EAAA,EAAA,sBAAA,UAAA,uBAKA,EAAA,EAAA,oBAAA,UAAA,qBAKA,EAAA,EAAA,gBAAA,UAAA,iBAKA,EAAA,EAAA,oBAAA,UAAA,qBAOA,EAAA,EAAA,wBAAA,UAAA,yBAMA,EAAA,EAAA,kBAAA,UAAA,mBAOA,EAAA,EAAA,cAAA,UAAA,eAOA,EAAA,EAAA,qBAAA,UAAA,sBAOA,EAAA,EAAA,qBAAA,UAAA;AACF,EAAA,CAAA,CAAA,GASa,KAAe,GAA2B,MAA8B,EAAU,SAAS,GClF3F,KAAgB,MAA4B,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,GAUjG,KAAc,MACzB,aAAiB,EAAK,YAAY,IAAQ,KAAA,GAYtC,KAAa,GAA8B,MAAwD;CAEvG,IAAM,IAAS,EAAQ;CACvB,IAAI,CAAC,KAAU,OAAO,KAAW,UAAU,OAAO,CAAC;CACnD,IAAM,IAAM,EAA4B;CACxC,IAAI,CAAC,KAAM,OAAO,KAAO,UAAU,OAAO,CAAC;CAC3C,IAAM,IAAO,EAA+B;CAI5C,OAHI,CAAC,KAAO,OAAO,KAAQ,WAAiB,CAAC,IAGtC;AACT,GASa,KAAuB,MAClC,EAAU,GAAS,WAAW,GASnB,KAAmB,MAAyD,EAAU,GAAS,OAAO,GAOtG,KAAa,MAAuC;CAC3D,UAAU,KAAA,GACd,IAAI;EACF,OAAO,KAAK,MAAM,CAAK;CACzB,QAAQ;EACN;CACF;AACF,GASa,MAAqB,MAA2B;CACtD,OACL,IAAI;EAEF,OAAO,KAAK,MAAM,CAAK;CACzB,QAAQ;EACN,OAAO;CACT;AACF,GASa,KACX,GACA,OAC4B;CAC5B,GAAG;CACH,GAAG;AACL,IAOa,MAAa,MAAmD;CACvE,UAAU,KAAA,GACd,OAAO,MAAU;AACnB,GAgBa,KAAmB,GAAc,MACxC,EAAE,WAAW,KAAA,KAAa,EAAE,WAAW,KAAA,IAAkB,IACzD,EAAE,WAAW,KAAA,IAAkB,IAC/B,EAAE,WAAW,KAAA,KACb,EAAE,SAAS,EAAE,SAAe,KAChC,EAAI,EAAE,SAAS,EAAE,SA0BN,KAAqD,MAAwB;CACxF,IAAM,IAAS,CAAC;CAChB,KAAK,IAAM,KAAO,GAChB,AAAI,OAAO,UAAU,eAAe,KAAK,GAAK,CAAG,KAAK,EAAI,OAAS,KAAA,MACjE,EAAO,KAAO,EAAI;CAKtB,OAAO;AACT,GCrEM,IAAN,MAAgD;CAU9C,YAAY,GAAuB,IAA8B,CAAC,GAAG;EAQnE,iCAb2B,IAAI,IAAyB,mBACtB,CAAC,kBAEnB,IAGhB,KAAK,UAAU,GACf,KAAK,iBAAiB,EAAQ,QAC9B,KAAK,iBACH,EAAQ,oBACD,CAEP,IACF,KAAK,UAAU,EAAQ,QAAQ,YAAY,EAAE,WAAW,cAAc,CAAC;CACzE;CAGA,MAAM,gBAAgB,GAAyB,GAAkD;EAE/F,AADA,KAAK,iBAAiB,GACtB,KAAK,SAAS,MAAM,yCAAyC,EAAE,MAAM,EAAQ,KAAK,CAAC;EACnF,IAAM,IAAM,KAAK,sBAAsB,GAAS,CAAI;EACpD,OAAO,KAAK,QAAQ,QAAQ,CAAG;CACjC;CAGA,MAAM,qBAAqB,GAA4B,GAAkD;EAEvG,AADA,KAAK,iBAAiB,GACtB,KAAK,SAAS,MAAM,8CAA8C,EAAE,OAAO,EAAS,OAAO,CAAC;EAC5F,IAAM,IAAO,EAAS,KAAK,MAAM,KAAK,sBAAsB,GAAG,GAAM,EAAI,CAAC;EAC1E,OAAO,KAAK,QAAQ,QAAQ,CAAI;CAClC;CAGA,MAAM,YAAY,GAAkB,GAAwB,GAAoC;EAE9F,AADA,KAAK,iBAAiB,GACtB,KAAK,SAAS,MAAM,qCAAqC;GAAE,MAAM,EAAQ;GAAM;EAAS,CAAC;EAEzF,IAAM,IAAY,KAAK,gBAAgB,EAAQ,kBAAkB,CAAI;EAGrE,AAFA,EAAU,KAAiB,QAC3B,EAAU,KAAiB,aAC3B,EAAU,KAAoB;EAC9B,IAAM,IAAQ,EAAQ,gBAAgB,CAAC,GAEjC,IAAoB;GACxB,MAAM,EAAQ;GACd,MAAM,EAAQ;GACd,QAAQ,EAAE,IAAI,KAAK,UAAU,GAAW,CAAK,EAAE;EACjD;EAEA,KAAK,iBAAiB,CAAG;EAEzB,IAAM,KAAS,MADM,KAAK,QAAQ,QAAQ,CAAG,GACvB,QAAQ;EAG9B,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,0DAA0D,EAAQ,KAAK,eAAe,EAAS,IAC/F,EAAU,YACV,GACF;EAcF,AAXA,KAAK,UAAU,IAAI,GAAU;GAC3B;GACA,MAAM,EAAQ;GACd;GACA,aAAa,EAAQ;GACrB,qBAAqB;GACrB,iBAAiB;GACjB,WAAW;GACX,WAAW;EACb,CAAC,GAED,KAAK,SAAS,MAAM,oDAAoD;GACtE,MAAM,EAAQ;GACd;GACA;EACF,CAAC;CACH;CAGA,aAAa,GAAkB,GAAoB;EACjD,KAAK,iBAAiB;EAEtB,IAAM,IAAU,KAAK,UAAU,IAAI,CAAQ;EAC3C,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,8DAA8D,EAAS,IACvE,EAAU,iBACV,GACF;EAGF,EAAQ,eAAe;EAEvB,IAAM,IAA0B;GAC9B,QAAQ,EAAQ;GAChB;GACA,QAAQ,EAAE,IAAI,KAAK,UAAU,EAAE,GAAG,EAAQ,oBAAoB,GAAG,EAAE,GAAG,EAAQ,gBAAgB,CAAC,EAAE;EACnG;EAEA,KAAK,iBAAiB,CAAS;EAC/B,IAAM,IAAI,KAAK,QAAQ,cAAc,CAAS;EAC9C,KAAK,SAAS,KAAK;GAAE,SAAS;GAAG;EAAS,CAAC;CAC7C;CAGA,MAAM,YAAY,GAAkB,GAAuC;EAEzE,AADA,KAAK,iBAAiB,GACtB,KAAK,SAAS,MAAM,qCAAqC,EAAE,YAAS,CAAC;EAErE,IAAM,IAAU,KAAK,UAAU,IAAI,CAAQ;EAC3C,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,0DAA0D,EAAS,IACnE,EAAU,iBACV,GACF;EAOF,AAHA,EAAQ,eAAe,EAAQ,MAG/B,EAAQ,YAAY;EAEpB,IAAM,EAAE,cAAW,aAAU,KAAK,cAAc,GAAS,CAAO;EAChE,EAAU,KAAiB;EAE3B,IAAM,IAAoB;GACxB,QAAQ,EAAQ;GAChB,MAAM,EAAQ;GACd,QAAQ,EAAE,IAAI,KAAK,UAAU,GAAW,CAAK,EAAE;EACjD;EAEA,KAAK,iBAAiB,CAAG;EACzB,IAAM,IAAI,KAAK,QAAQ,cAAc,CAAG;EAKxC,AAJA,KAAK,SAAS,KAAK;GAAE,SAAS;GAAG;EAAS,CAAC,GAE3C,MAAM,KAAK,cAAc,GAEzB,KAAK,SAAS,MAAM,mDAAmD,EAAE,YAAS,CAAC;CACrF;CAGA,MAAM,iBAAiB,GAAoC;EAEzD,AADA,KAAK,iBAAiB,GACtB,KAAK,SAAS,MAAM,0CAA0C,EAAE,aAAa,KAAK,UAAU,KAAK,CAAC;EAElG,KAAK,IAAM,KAAW,KAAK,UAAU,OAAO,GAAG;GAI7C,IAAI,EAAQ,aAAa,EAAQ,WAAW;GAC5C,EAAQ,YAAY;GAEpB,IAAM,EAAE,cAAW,aAAU,KAAK,cAAc,GAAS,KAAA,GAAW,CAAI;GACxE,EAAU,KAAiB;GAE3B,IAAM,IAAoB;IACxB,QAAQ,EAAQ;IAChB,MAAM;IACN,QAAQ,EAAE,IAAI,KAAK,UAAU,GAAW,CAAK,EAAE;GACjD;GAEA,KAAK,iBAAiB,CAAG;GACzB,IAAM,IAAI,KAAK,QAAQ,cAAc,CAAG;GACxC,KAAK,SAAS,KAAK;IAAE,SAAS;IAAG,UAAU,EAAQ;GAAS,CAAC;EAC/D;EAEA,MAAM,KAAK,cAAc;CAC3B;CAGA,MAAc,gBAA+B;EAE3C,IAAI,KAAK,eACP,OAAO,KAAK;EAGd,IAAM,IAAW,KAAK;EACtB,SAAK,WAAW,CAAC,GAEb,EAAS,WAAW,GAIxB;GAFA,KAAK,SAAS,MAAM,uCAAuC,EAAE,OAAO,EAAS,OAAO,CAAC,GAErF,KAAK,gBAAgB,KAAK,SAAS,CAAQ;GAC3C,IAAI;IACF,MAAM,KAAK;GACb,UAAU;IACR,KAAK,gBAAgB,KAAA;GACvB;EAL2C;CAM7C;CAEA,MAAc,SAAS,GAA0C;EAC/D,IAAM,IAAU,MAAM,QAAQ,WAAW,EAAS,IAAI,OAAO,MAAM,EAAE,OAAO,CAAC,GACvE,oBAAW,IAAI,IAAY;EAEjC,KAAK,IAAM,CAAC,GAAG,MAAW,EAAQ,QAAQ,GAAG;GAC3C,IAAM,IAAQ,EAAS;GACvB,AAAI,KAAS,EAAO,WAAW,cAC7B,EAAS,IAAI,EAAM,QAAQ;EAE/B;EAEA,IAAI,EAAS,SAAS,GAAG;GACvB,KAAK,SAAS,MAAM,2DAA2D;GAC/E;EACF;EAEA,KAAK,SAAS,KAAK,iEAAiE,EAClF,eAAe,CAAC,GAAG,CAAQ,EAC7B,CAAC;EAED,IAAM,IAAyD,CAAC;EAEhE,KAAK,IAAM,KAAY,GAAU;GAC/B,IAAM,IAAU,KAAK,UAAU,IAAI,CAAQ;GAC3C,IAAI,CAAC,GAAS;GAEd,IAAM,IAAiB,EAAQ,YAAY,cAAc,YACnD,IAAoB;IACxB,QAAQ,EAAQ;IAChB,MAAM,EAAQ;IACd,QAAQ,EACN,IAAI,KAAK,UACP;KAAE,GAAG,EAAQ;MAAsB,IAAgB;IAAe,GAClE,EAAE,GAAG,EAAQ,gBAAgB,CAC/B,EACF;GACF;GAEA,IAAI;IACF,MAAM,KAAK,QAAQ,cAAc,CAAG;GACtC,SAAS,GAAO;IACd,EAAe,KAAK;KAAE;KAAU;IAAM,CAAC;GACzC;EACF;EAEA,IAAI,EAAe,SAAS,GAAG;GAC7B,IAAM,IAAM,EAAe,KAAK,MAAM,EAAE,QAAQ,EAAE,KAAK,IAAI;GAE3D,MADA,KAAK,SAAS,MAAM,uDAAuD,EAAE,eAAe,EAAI,CAAC,GAC3F,IAAI,EAAK,UACb,mEAAmE,KACnE,EAAU,uBACV,GACF;EACF;CACF;CAGA,MAAM,QAAuB;EACvB,UAAK,SAET;GADA,KAAK,SAAS,MAAM,6BAA6B,GACjD,KAAK,UAAU;GACf,IAAI;IACF,MAAM,KAAK,cAAc;GAC3B,UAAU;IACR,KAAK,UAAU,MAAM;GACvB;GACA,KAAK,SAAS,MAAM,4CAA4C;EANjD;CAOjB;CAOA,iBAAyB,GAAyB;EAChD,IAAI;GACF,KAAK,eAAe,CAAG;EACzB,SAAS,GAAO;GACd,KAAK,SAAS,MAAM,qDAAqD,EAAE,SAAM,CAAC;EACpF;CACF;CAEA,mBAAiC;EAC/B,IAAI,KAAK,SACP,MAAM,IAAI,EAAK,UAAU,uDAAuD,EAAU,iBAAiB,GAAG;CAElH;CAUA,gBACE,GACA,GACwB;EAExB,IAAM,IAAY;GAAE,GADE,EAAa,KAAK,gBAAgB,SAAS,GAAM,QAAQ,OACxD;GAAe,GAAG;EAAiB;EAI1D,OAHI,GAAM,cAAc,KAAA,MACtB,EAAU,KAA2B,EAAK,YAErC;CACT;CASA,UAAkB,GAAmC,GAAyC;EAC5F,OAAO,OAAO,KAAK,CAAK,EAAE,SAAS,IAAI;GAAE;GAAW;EAAM,IAAI,EAAE,aAAU;CAC5E;CAEA,sBAA8B,GAAyB,GAAqB,IAAW,IAAqB;EAC1G,IAAM,IAAY,KAAK,gBAAgB,EAAQ,kBAAkB,CAAI;EAErE,AADA,EAAU,KAAiB,SACvB,MAIF,EAAU,KAAmB;EAE/B,IAAM,IAAoB;GACxB,MAAM,EAAQ;GACd,MAAM,EAAQ;GACd,QAAQ;IACN,IAAI,KAAK,UAAU,GAAW,EAAQ,gBAAgB,CAAC,CAAC;IACxD,GAAI,EAAQ,YAAY,EAAE,WAAW,GAAK,IAAI,CAAC;GACjD;EACF;EAGA,OADA,KAAK,iBAAiB,CAAG,GAClB;CACT;CAWA,cACE,GACA,GACA,GACsE;EACtE,IAAM,IAAgB,EAAa,KAAK,gBAAgB,SAAS,GAAM,QAAQ,OAAO;EAGtF,OAAO;GAAE,WAAA;IAFW,GAAG,EAAQ;IAAqB,GAAG;IAAe,GAAG,GAAS;GAEzE;GAAW,OAAA;IADJ,GAAG,EAAQ;IAAiB,GAAG,GAAS;GACpC;EAAM;CAC5B;AACF,GAYa,KAAqB,GAAuB,IAA8B,CAAC,MACtF,IAAI,EAAmB,GAAQ,CAAO,GClZlC,IAAN,MAAgE;CAO9D,YAAY,GAAiC,IAA8B,CAAC,GAAG;EAI7E,oCAN8B,IAAI,IAAgC,GAGlE,KAAK,SAAS,GACd,KAAK,kBAAkB,EAAQ,gBAC/B,KAAK,kBAAkB,EAAQ,gBAC/B,KAAK,UAAU,EAAQ,QAAQ,YAAY,EAAE,WAAW,cAAc,CAAC;CACzE;CAEA,OAAO,GAAwC;EAC7C,IAAM,IAAS,EAAQ;EAIvB,QAFA,KAAK,SAAS,MAAM,gCAAgC;GAAE;GAAQ,QAAQ,EAAQ;GAAQ,MAAM,EAAQ;EAAK,CAAC,GAElG,GAAR;GAEE,KAAK,kBAAkB;IACrB,IAAM,IAAU,KAAK,WAAW,CAAO;IACvC,OAAO,EAAQ,kBAAA,WAAsC,SACjD,KAAK,sBAAsB,GAAS,EAAQ,QAAQ,EAAQ,QAAQ,MAAM,IAC1E,KAAK,OAAO,eAAe,CAAO;GACxC;GAEA,KAAK,kBACH,OAAO,KAAK,cAAc,CAAO;GAGnC,KAAK,kBACH,OAAO,KAAK,cAAc,CAAO;GAGnC,KAAK,kBACH,OAAO,KAAK,cAAc,CAAO;GAGnC,SACE,OAAO,CAAC;EAEZ;CACF;CAMA,WAAmB,GAA8C;EAC/D,OAAO;GACL,MAAM,EAAQ,QAAQ;GAEtB,MAAM,EAAQ;GACd,kBAAkB,EAAoB,CAAO;GAC7C,cAAc,EAAgB,CAAO;EACvC;CACF;CAOA,YAAoB,GAAsC;EACxD,OAAO,OAAO,EAAQ,QAAS,WAAW,EAAQ,OAAO;CAC3D;CAMA,sBAA8B,GAAmC;EAC1D,SAAK,iBACV,IAAI;GACF,KAAK,gBAAgB,CAAO;EAC9B,SAAS,GAAO;GACd,KAAK,SAAS,MAAM,8DAA8D,EAAE,SAAM,CAAC;EAC7F;CACF;CAEA,sBAA8B,GAAgB,GAA+C;EACtF,SAAK,iBACV,IAAI;GACF,KAAK,gBAAgB,GAAQ,CAAO;EACtC,SAAS,GAAO;GACd,KAAK,SAAS,MAAM,8DAA8D,EAAE,SAAM,CAAC;EAC7F;CACF;CAyBA,qBACE,GACA,GACA,GACA,GACS;EAcT,OAbI,MAAY,KAAA,KAAa,KAAW,EAAQ,WAC9C,KAAK,SAAS,MAAM,sBAAsB,EAAO,oCAAoC;GACnF;GACA;GACA,gBAAgB,EAAQ;EAC1B,CAAC,GACM,MAEL,EAAQ,UACV,KAAK,SAAS,MAAM,sBAAsB,EAAO,uCAAuC;GAAE;GAAQ;EAAQ,CAAC,GACpG,OAEL,MAAY,KAAA,MAAW,EAAQ,UAAU,IACtC;CACT;CASA,cAAsB,GAAmC;EAEvD,AADA,EAAQ,SAAS,IACjB,EAAQ,cAAc;CACxB;CAoBA,qBACE,GACA,GACA,GACA,GACS;EAUT,OATI,MAAW,cACb,EAAQ,KAAK,GAAG,KAAK,OAAO,eAAe,GAAS,CAAmB,CAAC,GACxE,KAAK,cAAc,CAAO,GACnB,MAEL,MAAW,eACb,KAAK,cAAc,CAAO,GACnB,MAEF;CACT;CAMA,sBACE,GACA,GACA,GACU;EACV,IAAI,CAAC,GAAQ,OAAO,CAAC;EAGrB,IADiB,KAAK,aAAa,IAAI,CACnC,GAMF,OAHA,KAAK,SAAS,MAAM,mFAAmF,EACrG,UACF,CAAC,GACM,CAAC;EAGV,IAAM,IAAW,EAAQ,mBAAA,gBAAwC,IAE3D,IAA8B;GAClC,MAAM,EAAQ;GACd;GACA,aAAa;GACb,cAAc,EAAE,GAAG,EAAQ,aAAa;GACxC,kBAAkB,EAAE,GAAG,EAAQ,iBAAiB;GAChD,SAAS,KAAW;GACpB,QAAQ;EACV;EASA,OARA,KAAK,aAAa,IAAI,GAAQ,CAAO,GAErC,KAAK,SAAS,MAAM,0DAA0D;GAC5E,MAAM,EAAQ;GACd;GACA;EACF,CAAC,GAEM,KAAK,OAAO,iBAAiB,CAAO;CAC7C;CAOA,cAAsB,GAAwC;EAC5D,IAAM,IAAS,EAAQ;EACvB,IAAI,CAAC,GAAQ,OAAO,CAAC;EAErB,IAAM,IAAU,KAAK,aAAa,IAAI,CAAM;EAC5C,IAAI,CAAC,GAQH,OAHA,KAAK,SAAS,KAAK,yFAAyF,EAC1G,UACF,CAAC,GACM,KAAK,cAAc,CAAO;EAGnC,IAAI,KAAK,qBAAqB,iBAAiB,GAAQ,GAAS,EAAQ,QAAQ,MAAM,GAAG,OAAO,CAAC;EAEjG,IAAM,IAAY,EAAoB,CAAO,GACvC,IAAe,EAAgB,CAAO,GACtC,IAAQ,OAAO,EAAQ,QAAS,WAAW,EAAQ,OAAO,IAC1D,IAAS,EAAU,IACnB,IAAoB,CAAC;EAgB3B,OAdI,EAAM,SAAS,MACjB,EAAQ,eAAe,GACvB,EAAQ,KAAK,GAAG,KAAK,OAAO,iBAAiB,GAAS,CAAK,CAAC,IAG1D,KAAK,qBAAqB,GAAS,GAAQ,GAAc,CAAO,KAClE,KAAK,SAAS,MACZ,8CAA8C,MAAW,aAAa,aAAa,eACnF,EACE,UAAU,EAAQ,SACpB,CACF,GAGK;CACT;CAOA,cAAsB,GAAwC;EAC5D,IAAM,IAAS,EAAQ;EACvB,IAAI,CAAC,GAAQ,OAAO,CAAC;EAErB,IAAM,IAAU,KAAK,WAAW,CAAO,GACjC,IAAY,EAAQ,oBAAoB,CAAC,GACzC,IAAQ,EAAQ,gBAAgB,CAAC,GACjC,IAAa,EAAU,OAAmB,QAC1C,IAAS,EAAU,IAEnB,IAAU,KAAK,aAAa,IAAI,CAAM;EAE5C,IAAI,CAAC,GACH,OAAO,KAAK,oBAAoB,GAAS,GAAY,GAAQ,GAAQ,EAAQ,QAAQ,MAAM;EAG7F,IAAI,KAAK,qBAAqB,iBAAiB,GAAQ,GAAS,EAAQ,QAAQ,MAAM,GAAG,OAAO,CAAC;EAGjG,IAAM,IAAO,KAAK,YAAY,CAAO;EAGrC,IAAI,EAAK,WAAW,EAAQ,WAAW,GAAG;GACxC,IAAM,IAAQ,EAAK,MAAM,EAAQ,YAAY,MAAM,GAC7C,IAAoB,CAAC;GAS3B,OAPI,EAAM,SAAS,MACjB,EAAQ,cAAc,GACtB,EAAQ,KAAK,GAAG,KAAK,OAAO,iBAAiB,GAAS,CAAK,CAAC,IAG9D,KAAK,qBAAqB,GAAS,GAAQ,GAAO,CAAO,GAElD;EACT;EASA,OANA,EAAQ,cAAc,GACtB,EAAQ,eAAe,EAAE,GAAG,EAAM,GAClC,EAAQ,mBAAmB,EAAE,GAAG,EAAU,GAE1C,KAAK,sBAAsB,CAAO,GAE3B,CAAC;CACV;CAEA,oBACE,GACA,GACA,GACA,GACA,GACU;EAEV,IAAI,CAAC,GACH,OAAO,KAAK,OAAO,eAAe,CAAO;EAG3C,IAAM,IAAW,EAAQ,mBAAA,gBAAwC,IAC3D,IAAQ,EAAQ,gBAAgB,CAAC,GACjC,IAAO,OAAO,EAAQ,QAAS,WAAW,EAAQ,OAAO;EAE/D,KAAK,SAAS,MAAM,kEAAkE;GACpF,MAAM,EAAQ;GACd;GACA;EACF,CAAC;EAGD,IAAM,IAAiC;GACrC,MAAM,EAAQ;GACd;GACA,aAAa;GACb,cAAc,EAAE,GAAG,EAAM;GACzB,kBAAkB,EAAE,GAAG,EAAQ,iBAAiB;GAChD,SAAS,KAAW;GACpB,QAAQ;EACV;EACA,KAAK,aAAa,IAAI,GAAQ,CAAU;EAGxC,IAAM,IAAU,KAAK,OAAO,iBAAiB,CAAU;EAcvD,OAZI,EAAK,SAAS,KAChB,EAAQ,KAAK,GAAG,KAAK,OAAO,iBAAiB,GAAY,CAAI,CAAC,GAG5D,MAAW,cACb,EAAQ,KAAK,GAAG,KAAK,OAAO,eAAe,GAAY,CAAK,CAAC,IAG3D,MAAW,cAAc,MAAW,gBACtC,KAAK,cAAc,CAAU,GAGxB;CACT;CAOA,cAAsB,GAAwC;EAC5D,IAAM,IAAS,EAAQ;EACvB,IAAI,CAAC,GAAQ,OAAO,CAAC;EAErB,IAAM,IAAU,KAAK,aAAa,IAAI,CAAM;EAa5C,OAXA,KAAK,sBAAsB,GAAQ,CAAO,GAEtC,KAIF,KAAK,cAAc,CAAO,GAG5B,KAAK,SAAS,MAAM,uCAAuC,EAAE,UAAO,CAAC,GAE9D,CAAC;CACV;AACF,GAYa,MACX,GACA,IAA8B,CAAC,MACP,IAAI,EAAmB,GAAO,CAAO,GC3ZzD,KAAN,MAA0E;CAIxE,YAAY,GAA+B;EACzC,gCAH0B,IAAI,IAAyB,GAGvD,KAAK,UAAU;CACjB;CAEA,aAAa,GAAiB,GAAuD;EACnF,IAAM,IAAU,KAAK,aAAa,CAAO,GACnC,IAAmB,CAAC;EAC1B,KAAK,IAAM,KAAS,KAAK,SACvB,AAAK,EAAQ,IAAI,EAAM,GAAG,MACxB,EAAQ,IAAI,EAAM,GAAG,GACrB,EAAO,KAAK,GAAG,EAAM,MAAM,CAAO,CAAC;EAGvC,OAAO;CACT;CAEA,YAAY,GAAiB,GAAwB;EACnD,KAAK,aAAa,CAAO,EAAE,IAAI,CAAQ;CACzC;CAEA,WAAW,GAAiB,GAAwB;EAClD,KAAK,SAAS,IAAI,CAAO,GAAG,OAAO,CAAQ;CAC7C;CAEA,WAAW,GAAuB;EAChC,KAAK,SAAS,OAAO,CAAO;CAC9B;CAEA,aAAqB,GAA8B;EACjD,IAAI,IAAM,KAAK,SAAS,IAAI,CAAO;EAKnC,OAJK,MACH,oBAAM,IAAI,IAAI,GACd,KAAK,SAAS,IAAI,GAAS,CAAG,IAEzB;CACT;AACF,GAYa,MAAkC,MAC7C,IAAI,GAAwB,CAAM;;;AC7BpC,SAAgB,EAA2B,GAAQ,GAAuD;CACxG,OAAO;EACL;EACA,OAAO,MAAY,EAAQ,MAAQ;EACnC,QAAQ,GAAS,MAAU;GACzB,AAAI,OAAO,KAAU,aAAU,EAAQ,KAAO;EAChD;CACF;AACF;AAeA,SAAgB,EAA4B,GAAQ,GAAyD;CAC3G,OAAO;EACL;EACA,OAAO,MAAY,GAAU,EAAQ,EAAI,KAAK;EAC9C,QAAQ,GAAS,MAAU;GACzB,AAAI,OAAO,KAAU,cAAW,EAAQ,KAAO,OAAO,CAAK;EAC7D;CACF;AACF;AAYA,IAAa,KAA2C,OAA2C;CACjG;CAIA,OAAO,MAAY,EAAU,EAAQ,EAAI;CACzC,QAAQ,GAAS,MAAU;EAGzB,AAAI,KAAiC,SAAM,EAAQ,KAAO,KAAK,UAAU,CAAK;CAChF;AACF,IAaa,MACX,GACA,GACA,OACuB;CACvB;CACA,OAAO,MAAY;EACjB,IAAM,IAAM,EAAQ;EAEpB,OAAO,EAAQ,MAAM,MAAc,MAAc,CAAG,KAAK;CAC3D;CACA,QAAQ,GAAS,MAAU;EACzB,AAAI,OAAO,KAAU,aAAU,EAAQ,KAAO;CAChD;AACF,ICnLa,IAAc,QAGrB,KAAkB,MAWX,MAAmB,MAC9B,EAAQ,SAAS,EAAe,KAAK,MAA2B,EAAM,WAAW,EAAQ,MAAM,GAAG,EAAE,CAAC,IAAI,KAAA,GAQ9F,KAAmB,YAWnB,MAAQ,GAAgB,MAA0B,EAAmC,IAYrF,MACX,GACA,GACA,GACA,MAC2B;CAC3B,IAAM,IAA8B,GAAG,IAAc,EAAU;CAC/D,KAAK,IAAM,KAAS,GACd,KAAQ,CAAC,EAAK,SAAS,EAAM,GAAG,KACpC,EAAM,MAAM,GAAK,GAAK,GAAQ,EAAM,GAAG,CAAC;CAE1C,OAAO;AACT,GA2Ba,MAAmC,GAAqB,MACnE,EAAM,MAAM,MAAS,CAAC,EAAK,SAAS,EAAK,aAAa,CAAQ,KAAK,EAAM,MAAM,MAAS,EAAK,QAAQ,CAAQ,CAAC,GAmBnG,MACX,MAC2B;CAC3B,IAAM,oBAAiB,IAAI,IAAsC,GAC3D,IAAwC,CAAC;CAC/C,KAAK,IAAM,KAAc,GACnB,EAAW,cAAc,YACzB,EAAW,QAAO,EAAU,KAAK,CAAU,IAC1C,EAAe,IAAI,EAAW,MAAM,CAAU;CAErD,OAAO;EAAE;EAAgB;CAAU;AACrC,GAEa,KACX,GACA,MAC4B;CAC5B,IAAM,IAA+B,CAAC;CACtC,KAAK,IAAM,KAAS,GAAQ;EAC1B,IAAM,IAAQ,EAAM,KAAK,CAAO;EAChC,AAAI,MAAU,KAAA,MAAW,EAAI,EAAM,OAAO;CAC5C;CACA,OAAO;AACT,GCnGa,MACX,MAC8B;CAC9B,IAAM,oBAAS,IAAI,IAAgC;CACnD,KAAK,IAAM,KAAc,GAAa,EAAO,IAAI,EAAW,MAAM,CAAU;CAE5E,IAAM,KAAe,GAAkC,MAAiC;EACtF,IAAI,EAAW,UAAU,OAAO,CAAC;EAEjC,IAAM,IAAM,EAAW,EAAW,QAAQ,EAAI,YAAY;EAC1D,AAAI,EAAW,QAAM,OAAO,OAAO,GAAK,EAAW,KAAK,OAAO,EAAI,IAAI,CAAC;EAExE,IAAM,IAAiB,EAAI,iBAAA,uBAA6C;EAOxE,OAAO,CAAC;GAAE,MAAM,EAAW;GAAM;GAAgB,SAAS,EAAe,CAAG;EAAE,CAAiB;CACjG,GAEM,KAAe,GAAgC,MAAiC;EACpF,IAAM,IAAW,EAAI,aAAA,YAAkC,IACjD,IAAW,GAAQ,EAAW,OAAO,CAAQ;EACnD,IAAI,CAAC,GAAU,OAAO,CAAC;EAEvB,IAAM,IAAM,EAAW,EAAS,QAAQ,EAAI,YAAY;EAExD,AADI,EAAS,QAAM,OAAO,OAAO,GAAK,EAAS,KAAK,OAAO,EAAI,IAAI,CAAC,GACpE,EAAI,OAAO;EAMX,IAAM,IAAU,EAAW,SAAS,EAAe,CAAG,GAAG;GACvD,cAAc,EAAI;GAClB,kBAAkB,EAAI;EACxB,CAAC;EAKD,OAAO,CAAC;GAAE,MAAM,EAAW;GAAM,GAAG;EAAQ,CAAiB;CAC/D;CAEA,OAAO,EACL,SAAS,MAAQ;EACf,IAAM,IAAa,EAAO,IAAI,EAAI,SAAS;EAG3C,OAFK,IACD,EAAW,cAAc,UAAgB,EAAY,GAAY,CAAG,IACjE,EAAY,GAAY,CAAG,IAFV,CAAC;CAG3B,EACF;AACF,GCxDM,MAAwB,GAAyB,MACrD,GAAS,qBAAqB,KAAA,IAC1B,IACA;CAAE,GAAG;CAAS,kBAAkB;EAAE,GAAG,EAAQ;EAAkB,GAAG,EAAQ;CAAiB;AAAE,GAStF,MACX,GACA,MAC8B;CAC9B,IAAM,oBAAS,IAAI,IAAgC;CACnD,KAAK,IAAM,KAAc,GAAa,EAAO,IAAI,EAAW,MAAM,CAAU;CAE5E,IAAM,IAAc,OAClB,GACA,GACA,GACA,MACkB;EAClB,IAAI,EAAW,UAAU;GAEvB,MAAM,EAAK,gBACT;IAAE,MAAM;IAAU,MAAM;IAAI,cAAc,GAAG,IAAc,EAAW,KAAK;GAAE,GAC7E,EAAI,IACN;GACA;EACF;EAIA,IAAM,IAAS,GAAK,GAAO,SAAS;EACpC,IAAI,OAAO,KAAW,aAAY,GAChC,MAAM,IAAI,EAAK,UACb,kCAAkC,EAAW,KAAK,kFAClD,EAAU,iBACV,GACF;EAEF,IAAM,IAAe,GAAY,EAAW,QAAQ,EAAW,MAAM,CAAM,GACrE,IAAO,EAAW,OAAO,EAAW,KAAK,OAAO,CAAM,IAAI;EAChE,MAAM,EAAK,gBAAgB;GAAE,MAAM;GAAU;GAAM;EAAa,GAAG,EAAI,IAAI;CAC7E,GAEM,IAAc,OAClB,GACA,GACA,GACA,MACkB;EAGlB,IAAM,IAAU,EAAW,iBAAiB,CAAK,GAC3C,IAA6B,CAAC;EACpC,KAAK,IAAM,KAAQ,EAAW,QAAQ,CAAK,GAAG;GAC5C,IAAM,IAAW,EAAW,WAAW,CAAI,GACrC,IAAW,GAAQ,EAAW,OAAO,CAAQ;GACnD,IAAI,CAAC,GAAU;GAIf,IAAM,IAAS,GACT,IAAe;IACnB,GAAG,GAAY,EAAS,QAAQ,EAAW,MAAM,CAAM;IACvD,GAAG,GAAS;KACX,KAAmB;GACtB,GACM,IAAO,EAAS,OAAO,EAAS,KAAK,OAAO,CAAI,IAAI;GAC1D,EAAS,KAAK,GAAqB;IAAE,MAAM;IAAU;IAAM;GAAa,GAAG,CAAO,CAAC;EACrF;EAiBA,AAfI,EAAS,WAAW,KAOtB,EAAS,KACP,GACE;GAAE,MAAM;GAAU,MAAM;GAAI,cAAc;KAAG,IAAc,EAAW;IAAM,GAAG,GAAS;GAAa;EAAE,GACvG,CACF,CACF,GAGF,MAAM,EAAK,qBAAqB,GAAU,EAAI,IAAI;CACpD;CAEA,OAAO,EACL,QAAQ,OAAO,GAAO,GAAM,MAAQ;EAClC,IAAM,IAAa,EAAO,IAAI,EAAM,IAAI;EACxC,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,8CAA8C,EAAM,KAAK,IACzD,EAAU,iBACV,GACF;EAEF,OAAO,EAAW,cAAc,UAC5B,EAAY,GAAY,GAAO,GAAM,CAAG,IACxC,EAAY,GAAY,GAAO,GAAM,CAAG;CAC9C,EACF;AACF,GCkKa,WAAkE;CAuB7E,IAAM,KAhBQ,GAAkB,MAAyC;EAGvE,IAAM,IAAQ,GAAgB,CAAQ;EACtC,OAAO;GACL;GACA,GAAI,IAAQ,EAAE,SAAM,IAAI,CAAC;GACzB,QAAQ,EAAK,UAAU,CAAC;GACxB,MAAM,EAAK;EACb;CACF;CAQA,OAAO;EACL,QAAQ,GAAM,MAAS;GAIrB,IAAM,IAAM;GAKZ,OAAO;IACL,WAAW;IACX;IACA,QAAQ,GAAK,UAAU,CAAC;IACxB,MAAM,GAAK;IACX,UAAU,GAAK,YAAY;GAC7B;EACF;EACA,QAAQ,GAAM,MAAS;GAIrB,IAAM,IAAS,EAAK,MAA0E,CAAC;GAG/F,OAAO;IACL,WAAW;IACX;IACA,SAAS,EAAK;IACd,YAAY,EAAK;IACjB;IACA,gBAAgB,EAAK;IACrB,UAAU,EAAK;GACjB;EACF;CACF;AACF,GCvVM,KAAa,QAgCN,MACX,MAC+B;CAC/B,IAAM,EAAE,mBAAgB,iBAAc,GAAsB,CAAW,GACjE,oBAAe,IAAI,IAAuC;CAEhE,KAAK,IAAM,KAAc,GACvB,AAAI,EAAW,cAAc,YAC3B,EAAa,IAAI,EAAW,MAAM,CAAU;CAQhD,IAAM,KAAW,GAAmB,OAClC,EAAI,MAAc,GACX,EAAe,CAAG,IAGrB,KAAe,GAAsC,GAAmB,MAAkC;EAC9G,IAAM,IAAM,EAAW,EAAW,QAAQ,EAAI,YAAY;EAE1D,OADI,EAAW,QAAM,OAAO,OAAO,GAAK,EAAW,KAAK,OAAO,EAAI,IAAI,CAAC,GACjE,CAAC,EAAQ,GAAW,CAAG,CAAC;CACjC,GAMM,KAAY,MAChB,EAAa,IAAI,EAAQ,aAAA,QAA6B,EAAE;CAE1D,OAAO;EACL,aAAa,MAAY;GACvB,IAAM,IAAO,EAAS,CAAO;GAC7B,IAAI,CAAC,GAAM,OAAO,CAAC;GACnB,IAAM,IAAM,EAAW,EAAK,QAAQ,EAAQ,YAAY;GAExD,OADA,EAAI,EAAK,WAAW,EAAQ,UACrB,CAAC,EAAQ,EAAK,OAAO,CAAG,CAAC;EAClC;EAEA,aAAa,GAAS,MAAU;GAC9B,IAAM,IAAO,EAAS,CAAO;GAC7B,IAAI,CAAC,GAAM,OAAO,CAAC;GACnB,IAAM,IAA+B;KAAG,EAAK,UAAU,EAAQ;KAAW,EAAK,aAAa;GAAM;GAClG,OAAO,CAAC,EAAQ,EAAK,OAAO,CAAG,CAAC;EAClC;EAEA,WAAW,GAAS,MAAwB;GAC1C,IAAM,IAAO,EAAS,CAAO;GAC7B,IAAI,CAAC,GAAM,OAAO,CAAC;GACnB,IAAI,EAAK,WACP,OAAO,EAAK,UAAU;IACpB,UAAU,EAAQ;IAClB,aAAa,EAAQ;IACrB,cAAc,EAAQ;IACtB;GACF,CAAC;GAEH,IAAM,IAAM,EAAW,EAAK,QAAQ,CAAmB;GAEvD,OADA,EAAI,EAAK,WAAW,EAAQ,UACrB,CAAC,EAAQ,EAAK,KAAK,CAAG,CAAC;EAChC;EAEA,iBAAiB,GAAW,GAAc,GAAkB,MAAS;GACnE,IAAM,IAA2B;IAAE;IAAW;IAAc;IAAkB;GAAK,GAC7E,IAAM,EAAe,IAAI,CAAS;GACxC,IAAI,GAAK,OAAO,EAAY,GAAK,GAAW,CAAG;GAC/C,IAAM,IAAa,EAAa,IAAI,CAAS;GAC7C,IAAI,GAAY,gBAAgB,OAAO,EAAW,eAAe,CAAG;GACpE,IAAM,IAAW,EAAU,MAAM,MAAM,EAAE,QAAQ,CAAS,CAAC;GAE3D,OADI,IAAiB,EAAY,GAAU,GAAW,CAAG,IAClD,CAAC;EACV;CACF;AACF,GC7Fa,MACX,GACA,MAC+B;CAC/B,IAAM,EAAE,mBAAgB,iBAAc,GAAsB,CAAW,GACjE,oBAAgB,IAAI,IAAyF;CAEnH,KAAK,IAAM,KAAc,GACvB,AAAI,EAAW,cAAc,aAC3B,EAAc,IAAI,EAAW,OAAO;EAAE;EAAY,OAAO;CAAQ,CAAC,GAClE,EAAc,IAAI,EAAW,OAAO;EAAE;EAAY,OAAO;CAAQ,CAAC,GAClE,EAAc,IAAI,EAAW,KAAK;EAAE;EAAY,OAAO;CAAM,CAAC;CAIlE,OAAO,EACL,QAAQ,OAAO,GAAO,GAAM,MAAQ;EAClC,IAAM,EAAE,YAAS,GAEX,IAAc,EAAc,IAAI,CAAI;EAC1C,IAAI,GAAa;GACf,IAAM,EAAE,eAAY,aAAU,GACxB,KAAuB,GAAG,MAAS,GAAY,EAAW,QAAQ,EAAW,MAAM,GAAG,CAAI,GAE1F,IAAW,GAAK,GAAO,EAAW,OAAO;GAC/C,AAAI,MAAU,UACZ,MAAM,EAAK,YAAY,GAAU;IAAE,MAAM;IAAU,MAAM;IAAI,cAAc,EAAE,CAAK;GAAE,GAAG,EAAI,IAAI,IACtF,MAAU,UACnB,EAAK,aAAa,GAAU,GAAK,GAAO,EAAW,UAAU,CAAW,IAC/D,EAAW,QACpB,MAAM,EAAW,MAAM,GAAO,GAAM;IAAE;IAAG,MAAM;IAAU,WAAW,EAAI;IAAW,MAAM,EAAI;GAAK,CAAC,IAEnG,MAAM,EAAK,YAAY,GAAU;IAAE,MAAM;IAAU,MAAM;IAAI,cAAc,EAAE,CAAK;GAAE,CAAC;GAEvF;EACF;EAEA,IAAM,IAAa,EAAe,IAAI,CAAI,KAAK,EAAU,MAAM,MAAM,EAAE,QAAQ,CAAI,CAAC;EACpF,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UAAU,8CAA8C,EAAK,IAAI,EAAU,iBAAiB,GAAG;EAGhH,IAAM,KAAuB,GAAG,MAAS,GAAY,EAAW,QAAQ,EAAE,MAAM,GAAG,CAAI;EACvF,IAAI,EAAW,QAAQ;GACrB,MAAM,EAAW,OAAO,GAAO,GAAM;IAAE;IAAG,MAAM;IAAU,WAAW,EAAI;IAAW,MAAM,EAAI;GAAK,CAAC;GACpG;EACF;EAEA,IAAM,IAAO,EAAW,OAAO,EAAW,KAAK,OAAO,CAAK,IAAI;EAC/D,MAAM,EAAK,gBACT;GAAE,MAAM;GAAU;GAAM,cAAc,EAAE,CAAK;GAAG,WAAW,EAAW,YAAY,CAAK;EAAE,GACzF,EAAI,IACN;CACF,EACF;AACF,GCyLa,YAAqE;CAChF,QAAQ,GAAM,OAMX;EACC,WAAW;EACX;EACA,QAAQ,GAAM,UAAU,CAAC;EACzB,MAAM,GAAM;EACZ,WAAW,GAAM;EAGjB,OAAO,GAAgB,CAAI;EAC3B,QAAQ,GAAM;CAChB;CACF,SAAS,GAAM,OAEZ;EAAE,WAAW;EAAU;EAAM,GAAG;CAAK;AAC1C,IC7Na,YAA0F;CACrG,oBAAoB,OAAa;EAAE,MAAM;EAAgB;CAAQ;CACjE,mBAAmB,GAAQ,OAAY;EAAE,MAAM;EAAc;EAAQ;CAAO;CAC5E,mBAAmB,GAAgB,OAAa;EAAE,MAAM;EAAe;EAAgB;CAAQ;CAC/F,wBAAwB,GAAgB,OAAa;EAAE,MAAM;EAAqB;EAAgB;CAAQ;CAC1G,6BAA6B,GAAgB,OAAa;EACxD,MAAM;EACN;EACA;CACF;AACF,ICsEM,KAAN,MAGE;CAMA,YACE,GACA,GACA,GACA,GACA;EAIA,AAHA,KAAK,QAAQ,EAAkB,GAAQ,CAAO,GAC9C,KAAK,aAAa,EAAQ,WAC1B,KAAK,iBAAiB,GACtB,KAAK,gBAAgB;CACvB;CAEA,MAAM,aAAa,GAAe,GAAuC;EAIvE,MAAM,KAAK,cAAc,OAAO,GAAO,KAAK,OAAO,EAAE,MAAM,EAAQ,CAAC;CACtE;CAEA,MAAM,cAAc,GAAiB,GAAuC;EAC1E,MAAM,KAAK,eAAe,OAAO,GAAQ,KAAK,OAAO;GAAE,WAAW,KAAK;GAAY,MAAM;EAAQ,CAAC;CACpG;CAEA,MAAM,gBAA+B;EACnC,MAAM,KAAK,MAAM,iBAAiB;CACpC;CAEA,MAAM,QAAuB;EAC3B,MAAM,KAAK,MAAM,MAAM;CACzB;AACF,GAMM,MACJ,GACA,GACA,GACA,MACyB;CACzB,IAAM,IAAe,EAAQ,gBAAgB,CAAC,GACxC,IAAmB,EAAQ,oBAAoB,CAAC,GAChD,IAAY,EAAA,QAA6B;CAE/C,IAAI,EAAQ,SAAA,YACV,OAAO,EAAa,OAAO;EAAE;EAAW,MAAM,EAAQ;EAAM;EAAc;CAAiB,CAAC;CAG9F,IAAI,EAAQ,SAAA,aAA0B;EACpC,IAAM,IAAQ,EAAA,aAAmC,IAM3C,IAAa,GAAW;EAG9B,OAAO,CAAC,IAFO,MAAe,KAAA,KAAa,OAAO,OAAO,GAAY,CAAS,IAAI,EAAW,KAAa,KAAA,KACrF,GAAO,EAAE,gBAAa,CAAC,KAAK,CAAC,GAClC,GAAG,EAAc,eAAe,GAAW,GAAc,GAAkB,EAAQ,IAAI,CAAC;CAC1G;CAEA,OAAO,CAAC;AACV,GAMM,MAAkB,MAAyC,EAAQ,SAAS,GAE5E,MACJ,GACA,GACA,OACwC;CACxC,mBAAmB,MAAY;EAC7B,IAAI,CAAC,GAAe,CAAO,GAAG,OAAO,CAAC;EACtC,IAAM,IAAQ,EAAQ,iBAAA,aAAmC;EAEzD,OAAO,CAAC,GADI,GAAW,gBAAgB,GAAO,CAAO,KAAK,CAAC,GAC3C,GAAG,EAAc,WAAW,CAAO,CAAC;CACtD;CACA,mBAAmB,GAAS,MAAW,GAAe,CAAO,IAAI,EAAc,WAAW,GAAS,CAAK,IAAI,CAAC;CAC7G,iBAAiB,GAAS,MACxB,GAAe,CAAO,IAAI,EAAc,SAAS,GAAS,CAAmB,IAAI,CAAC;CACpF,iBAAiB,MAAY,GAAsB,GAAS,GAAe,GAAc,CAAS;AACpG,IAEM,KAAN,MAGE;CAGA,YAAY,GAAqC;EAC/C,KAAK,QAAQ;CACf;CAEA,OAAO,GAA+D;EACpE,IAAM,IAAS,KAAK,MAAM,OAAO,CAAO;EAUxC,OANI,EAAQ,SAAA,aAEH;GAAE,QAAQ;GAAoB,SAAS,CAAC;EAAE,IAI5C;GAAE,QAAQ,CAAC;GAAG,SAAS;EAAoB;CACpD;AACF,GAeM,KAAW,GAA2B,GAAiB,MAAwB;CACnF,IAAM,IAAS,EAAK,IAAI,CAAO;CAC/B,IAAI,MAAW,KAAA,GACb,MAAM,IAAI,EAAK,UACb,6CAA6C,EAAQ,wBAAwB,EAAO,OAAO,KAC3F,EAAU,iBACV,GACF;CAEF,EAAK,IAAI,GAAS,CAAK;AACzB,GAOM,MAA2B,GAAyC,MAAwB;CAChG,KAAK,IAAM,KAAS,GAClB,IAAI,EAAM,QAAA,UAAuB,EAAM,QAAA,YACrC,MAAM,IAAI,EAAK,UACb,2BAA2B,EAAM,yCAAyC,EAAM,IAAI,IACpF,EAAU,iBACV,GACF;AAGN,GAgBM,MACJ,GACA,MACS;CACT,IAAM,oBAAa,IAAI,IAAoB,GACrC,oBAAY,IAAI,IAAoB;CAC1C,KAAK,IAAM,KAAc,GACvB,IAAI,EAAW,cAAc,SAAS;EACpC,IAAM,IAAQ,iBAAiB,EAAW,KAAK;EAG/C,AAFA,EAAQ,GAAY,EAAW,MAAM,CAAK,GAC1C,EAAQ,GAAW,EAAW,MAAM,CAAK,GACzC,GAAwB,EAAW,QAAQ,CAAK;CAClD,OAAO;EACL,IAAM,IAAQ,kBAAkB,EAAW,KAAK;EAChD,EAAQ,GAAW,EAAW,MAAM,CAAK;EACzC,KAAK,IAAM,KAAS;GAAC,EAAW;GAAO,EAAW;GAAO,EAAW;EAAG,GACrE,EAAQ,GAAY,GAAO,CAAK;EAElC,GAAwB,EAAW,QAAQ,CAAK;CAClD;CAGF,IAAM,oBAAa,IAAI,IAAoB;CAC3C,KAAK,IAAM,KAAc,GAAQ;EAC/B,IAAM,IAAQ,SAAS,EAAW,UAAU,IAAI,EAAW,KAAK;EAEhE,IADA,EAAQ,GAAY,EAAW,MAAM,CAAK,GACtC,EAAW,cAAc,SAC3B,GAAwB,EAAW,QAAQ,CAAK;OAC3C;GACL,IAAM,oBAAY,IAAI,IAAoB;GAC1C,KAAK,IAAM,KAAQ,EAAW,OAAO;IACnC,IAAM,IAAY,GAAG,EAAM,SAAS,EAAK,SAAS;IAElD,AADA,EAAQ,GAAW,EAAK,UAAU,CAAS,GAC3C,GAAwB,EAAK,QAAQ,CAAS;GAChD;EACF;CACF;AACF,GAea,YAGT,MACyD;CACzD,IAAM,EAAE,YAAS,uBAAoB,GAG/B,IAAU,EAAO,OAAO,GAAuB,CAAC,GAChD,IAAS,EAAO,MAAM,GAAqB,CAAC;CAClD,GAAe,GAAS,CAAM;CAG9B,IAAM,IAAgB,GAA8B,GAAS,CAAe,GACtE,IAAe,GAA6B,GAAQ,EAAc,GAClE,IAAgB,GAA8B,CAAO,GACrD,IAAe,GAA6B,CAAM;CACxD,OAAO;EAGL,GAAI,EAAO,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,EAAO,WAAW;EAC3E,MAAM,EAAQ;EACd,MAAM,EAAQ;EACd,aAAa,EAAQ;EACrB,gBAAgB,GAAQ,IAAU,CAAC,MAAM,IAAI,GAAoB,GAAQ,GAAS,GAAe,CAAY;EAC7G,qBACE,IAAI,GAKF,GAAkB,GAAW,GAAe,GAAc,IAAkB,CAAC,GAAG,CAAC,CAAC,CACpF;EACF,GAAG,GAAwB;CAC7B;AACF,GChaW,KAAM,EAAS,IAAI,GAMnB,IAA0E,EAGrF,kBAAkB,GAEP,IAAc,EAAS,cAAc,EAAE,GAEvC,IAAY,EAAS,YAAY,EAAE,GAEnC,IAAW,EAAU,SAAS,GAE9B,KAAS,EAAS,OAAO,GAEzB,KAAoB,EAAU,kBAAkB,GAEhD,KAAa,EAAS,aAAa,EAAE,GAErC,KAAY,EAAS,YAAY,EAAE,GAKnC,KAAa,EAAS,WAAW,GAEjC,KAAY,EAAU,YAAY,EAAK,GAEvC,KAAU,EAAS,QAAQ,GAM3B,KAAgB,GAC3B,gBACA;CAAC;CAAQ;CAAU;CAAkB;CAAc;CAAS;AAAO,GACnE,MACF,GCvCM,WACJ,GAA0C,CACxC;CACE,KAAK;CACL,QAAQ,MAAQ,CAAC,EAAe;EAAE,MAAM;EAAkB,WAAW,EAAI;CAAU,CAAC,CAAC;AACvF,GACA;CACE,KAAK;CACL,aAAa,CAAC,EAAE,MAAM,aAAsB,CAAC;AAC/C,CACF,CAAC,GAQU,WAAmE;CAC9E,IAAM,IAAU,GAA6B;CAC7C,OAAO;EACL,YAAY;GACV,QAAQ,OACN,EAAQ,YAAY,GAAO,OAAO,GAC3B,CAAC;GAEV,eAAe,OACb,EAAQ,YAAY,GAAO,YAAY,GAChC,CAAC;GAEV,gBAAgB,OACd,EAAQ,WAAW,GAAO,YAAY,GAC/B,CAAC;GAEV,SAAS,OACP,EAAQ,WAAW,CAAK,GACjB,CAAC;GAEV,QAAQ,OACN,EAAQ,WAAW,CAAK,GACjB,CAAC;GAEV,QAAQ,OACN,EAAQ,WAAW,CAAK,GACjB,CAAC;GAEV,eAAe,GAAO,MAAQ,EAAQ,aAAa,GAAO,EAAE,WAAW,GAAW,KAAK,EAAI,YAAY,EAAE,CAAC;EAC5G;EACA,gBAAgB,GAAO,MACrB,EAAQ,aAAa,GAAO,EAAE,WAAW,GAAW,KAAK,EAAa,YAAY,EAAE,CAAC;CACzF;AACF,GCtCM,MAAY,MAAmD,OAAO,KAAS,cAAY,GAM3F,MAA8B,GAAe,MACjD,GAAS,CAAI,MAAM,EAAK,OAAS,KAAA,KAAa,OAAO,EAAK,MAAS,WAUxD,MAAY,MAA2B,OAAO,KAAS,WAAW,IAAO,IAEzE,MAA4B,MACvC,GAA2B,GAAM,WAAW,GAIjC,MAAiC,MAAuD,GAAS,CAAI,GAGrG,MAAkC,MAC7C,GAA2B,GAAM,WAAW,GAGjC,MAAmC,MAC9C,GAA2B,GAAM,SAAS,GCtCtC,KAA6C,CAAC;CAAE,MAAM;CAAQ,MAAM;AAAG,CAAC,GAUxE,MAAmB,MACvB,EAAK,SAAS,UAAU,EAAK,SAAS,UAAU,EAAK,KAAK,WAAW,OAAO,GAUjE,MAAU,EAAE,UAAO,eAAgF;CAG9G,EAAM,eAAe;EACnB,QAAQ,CAAC,CAAW;EACpB,MAAM;GACJ,SAAS,OAAO,EAAE,QAAQ,EAAE,OAAO;GAGnC,SAAS,OAAO,EAAE,QAAQ,GAA8B,CAAC,IAAI,EAAE,SAAS,KAAA,EAAU;EACpF;CACF,CAAC;CACD,EAAM,qBAAqB;EACzB,QAAQ,CAAC,CAAW;EACpB,MAAM;GACJ,SAAS,OAAO,EAAE,SAAS,EAAE,QAAQ;GACrC,SAAS,OAAO,EAAE,SAAS,GAAgC,CAAC,IAAK,EAAE,WAAW,KAAM,GAAG;EACzF;CACF,CAAC;CACD,EAAM,0BAA0B,EAAE,QAAQ;EAAC;EAAa;EAAW;CAAO,EAAE,CAAC;CAO7E,EAAM,cAAc,EAAE,UAAU,GAAK,CAAC;CAStC,EAAM,gBAAgB;EAMpB,UAAU,MAAU;GAClB,IAAM,IAAY,EAAM,QAAQ,MAAM,QAAQ,MAAS,GAAgB,CAAI,CAAC;GAC5E,OAAO,EAAU,SAAS,IAAI,IAAY;EAC5C;EACA,aAAa,MAAS,EAAK;EAC3B,QAAQ,MAAM;GACZ,EAAE,QAAQ,EAAE,MAAM;IAAE,SAAS,MAAM,EAAE;IAAM,SAAS,OAAO,EAAE,MAAM,GAAS,CAAC,EAAE;GAAG,EAAE,CAAC;GACrF,EAAE,QAAQ;IACR,QAAQ,CAAC,EAAU;IACnB,MAAM;KAAE,SAAS,MAAM,EAAE;KAAK,SAAS,OAAO,EAAE,KAAK,GAAS,CAAC,EAAE;IAAG;GACtE,CAAC;GACD,EAAE,UAAU;IACV,QAAQ,CAAC,EAAG;IACZ,MAAM;KAAE,SAAS,MAAM,EAAE;KAAM,SAAS,OAAO,EAAE,MAAM,EAAE;IAAG;GAC9D,CAAC;EACH;EACA,iBAAiB,MAAU;GACzB,IAAM,IAAuC,CAAC;GAE9C,OADA,GAAW,MAAM,GAAc,EAAM,QAAQ,EAAE,GACxC;IAAE;IAAc,kBAAkB,GAAG,IAAc,EAAM,QAAQ,KAAK;GAAE;EACjF;EACA,WAAW,GAAM,EAAE,iBAAc,0BAAuB;GAEtD,IAAM,IAAQ,EAAA,QAAiC;GAE/C,OAAO,EAAE,SAAS;IAAE,IADT,GAAW,KAAK,CAAY,KAAK;IACpB;IAAM,OAAO,CAAC,CAAI;GAAE,EAAE;EAChD;CACF,CAAC;AACH,GCrEa,MAAW,EAAE,UAAO,gBAAqF;CAGpH,EAAO,QAAQ;EACb,OAAO;EACP,OAAO;EACP,KAAK;EACL,SAAS;EACT,YAAY;EACZ,QAAQ,CAAC,IAAK,CAAK;CACrB,CAAC;CAED,EAAO,aAAa;EAClB,OAAO;EACP,OAAO;EACP,KAAK;EACL,SAAS;EACT,YAAY;EACZ,QAAQ,CAAC,IAAK,CAAK;CACrB,CAAC;CAKD,EAAO,cAAc;EACnB,OAAO;EACP,OAAO;EACP,KAAK;EACL,SAAS;EACT,YAAY;EACZ,QAAQ;GAAC;GAAa;GAAW;GAAU;GAAQ;GAAmB;EAAK;EAC3E,OAAO,OAAO,GAAG,GAAM,EAAE,MAAG,cAAW;GACrC,IAAI;IACF,MAAM,EAAK,YAAY,EAAE,YAAY;KACnC;KACA,MAAM;KACN,cAAc,EAAE,GAAG;MAAC;MAAc;MAAY;KAAkB,CAAC;IACnE,CAAC;GACH,SAAS,GAAgB;IAGvB,IAAI,EAAE,aAAiB,EAAK,aAAa,EAAY,GAAO,EAAU,eAAe,IACnF,MAAM;IAER,MAAM,EAAK,gBAAgB;KAAE;KAAM,MAAM,EAAE;KAAO,cAAc,EAAE,CAAC;IAAE,CAAC;GACxE;EACF;EACA,YAAY,EAAE,aAAU,gBAAa,iBAAc,6BAA0B,CAC3E,EAAe;GACb,MAAM;GACN,YAAY;GACZ,UAAU,EAAU,KAAK,CAAmB,KAAK,EAAU,KAAK,CAAY;GAC5E,OAAO,GAAkB,CAAW;GACpC,kBAAkB,EAAM,KAAK,CAAmB;EAClD,CAAC,CACH;EACA,iBAAiB,EAAE,iBAAc,cAAW,CAC1C,EAAe;GACb,MAAM;GACN,YAAY,EAAY,KAAK,CAAY;GACzC,UAAU,EAAU,KAAK,CAAY;GACrC,SAAS,EAAS,KAAK,CAAY;GACnC,OAAO,GAAO,KAAK,CAAY;GAC/B,kBAAkB,GAAkB,KAAK,CAAY;GACrD,kBAAkB,EAAM,KAAK,CAAY;EAC3C,CAAC,GACD,EAAe;GACb,MAAM;GACN,YAAY,EAAY,KAAK,CAAY;GACzC,UAAU,EAAU,KAAK,CAAY;GACrC,OAAO;GACP,kBAAkB,EAAM,KAAK,CAAY;EAC3C,CAAC,CACH;CACF,CAAC;CAMD,EAAM,SAAS;EACb,QAAQ,CAAC,IAAY,EAAU,iBAAiB,CAAC;EACjD,QAAQ,OAAO,GAAG,GAAM,EAAE,MAAG,SAAM,cAAW,cAAW;GACvD,MAAM,EAAK,gBACT;IAAE;IAAM,MAAM;IAAI,cAAc,EAAE;KAAE,GAAG;KAAG,WAAW,EAAE,aAAa;IAAU,CAAC;GAAE,GACjF,CACF;EACF;CACF,CAAC;CACD,EAAM,YAAY;CAClB,EAAM,aAAa;CACnB,EAAM,UAAU,EACd,QAAQ,CAAC,IAAe,EAAU,iBAAiB,CAAC,EACtD,CAAC;CACD,EAAM,oBAAoB,EAAE,QAAQ,CAAC,EAAU,iBAAiB,CAAC,EAAE,CAAC;CACpE,EAAM,SAAS,EACb,MAAM;EAAE,SAAS,MAAM,EAAE;EAAW,SAAS,OAAU,EAAE,WAAW,GAAS,CAAI,EAAE;CAAG,EACxF,CAAC;CAMD,EAAM,SAAS,EACb,MAAM;EACJ,SAAS,MAAM,EAAE,UAAU;EAC3B,SAAS,MAAU,OAAO,KAAS,YAAY,IAAO,EAAE,QAAQ,EAAK,IAAI,CAAC;CAC5E,EACF,CAAC;CAID,EAAM,QAAQ;EACZ,QAAQ,CAAC,IAAY,CAAK;EAC1B,MAAM;GAAE,SAAS,MAAM,EAAE;GAAK,SAAS,OAAU,EAAE,KAAK,GAAS,CAAI,EAAE;EAAG;CAC5E,CAAC;CACD,EAAM,cAAc;EAClB,QAAQ;GAAC;GAAW;GAAQ;EAAK;EACjC,MAAM;GAAE,SAAS,MAAM,EAAE;GAAK,SAAS,OAAU,EAAE,KAAK,GAAS,CAAI,EAAE;EAAG;CAC5E,CAAC;CACD,EAAM,mBAAmB,EACvB,QAAQ;EAAC;EAAW;EAAY,EAAS,SAAS,EAAE;EAAG,EAAS,UAAU;EAAG;CAAK,EACpF,CAAC;CAID,EAAM,oBAAoB;EACxB,QAAQ;GAAC;GAAa;GAAW;GAAU;GAAQ;GAAmB;EAAK;EAC3E,MAAM;GACJ,SAAS,OAAO;IAAE,WAAW,EAAE;IAAW,OAAO,EAAE;GAAM;GACzD,SAAS,MACP,GAAyB,CAAI,IAAI;IAAE,WAAW,EAAK,aAAa;IAAI,OAAO,EAAK;GAAM,IAAI,EAAE,WAAW,GAAG;EAC9G;CACF,CAAC;CACD,EAAM,yBAAyB;EAC7B,QAAQ;GAAC;GAAa;GAAU;GAAmB,EAAU,aAAa;EAAC;EAC3E,MAAM;GACJ,SAAS,OAAO,EAAE,QAAQ,EAAE,OAAO;GACnC,SAAS,MAAU,GAA8B,CAAI,IAAI,EAAE,QAAQ,EAAK,OAAO,IAAI,CAAC;EACtF;CACF,CAAC;CACD,EAAM,qBAAqB;EACzB,QAAQ;GAAC;GAAa;GAAU;EAAiB;EACjD,MAAM;GACJ,SAAS,OAAO,EAAE,WAAW,EAAE,UAAU;GACzC,SAAS,OAAU,EAAE,WAAW,GAA+B,CAAI,IAAK,EAAK,aAAa,KAAM,GAAG;EACrG;CACF,CAAC;CACD,EAAM,yBAAyB,EAC7B,QAAQ,CAAC,GAAa,EAAS,cAAc,EAAE,CAAC,EAClD,CAAC;CACD,EAAM,sBAAsB,EAAE,QAAQ,CAAC,CAAW,EAAE,CAAC;CAIrD,EAAM,UAAU;EACd,QAAQ,CAAC,IAAK,EAAU,WAAW,CAAC;EACpC,YAAY,MAAM,EAAE,cAAc;EAClC,MAAM;GAAE,SAAS,MAAM,EAAE;GAAM,SAAS,OAAU,EAAE,QAAK;EAAG;CAC9D,CAAC;AACH,GCpGa,YAAgC;CAC3C,UAAU,CAAC;CACX,0BAAU,IAAI,IAAI;CAClB,wBAAwB,CAAC;AAC3B,IAaa,KAAiB,GAAyB,MAAyC;CAC9F,IAAI,IAAQ,EAAM,SAAS,MAAM,MAAM,EAAE,mBAAmB,CAAc;CAQ1E,OAPK,MAIH,IAAQ;EAAE;EAAgB,SAAS;GAAE,IAAI;GAAgB,MAAM;GAAa,OAAO,CAAC;EAAE;CAAE,GACxF,EAAM,SAAS,KAAK,CAAK,IAEpB,EAAM;AACf,GASa,MAAkB,GAAyB,MAAuC;CAC7F,IAAI,IAAW,EAAM,SAAS,IAAI,CAAS;CAK3C,OAJK,MACH,IAAW;EAAE,sBAAM,IAAI,IAAI;EAAG,2BAAW,IAAI,IAAI;EAAG,uBAAO,IAAI,IAAI;CAAE,GACrE,EAAM,SAAS,IAAI,GAAW,CAAQ,IAEjC;AACT,GASa,KACX,GACA,GACA,MACyE;CACzE,IAAM,IAAU,EAAS,MAAM,IAAI,CAAU;CAC7C,IAAI,CAAC,GAAS;CACd,IAAM,IAAO,EAAQ,MAAM,EAAQ;CAC/B,OAAM,SAAS,gBACnB,OAAO;EAAE;EAAS;CAAK;AACzB,GCvJa,MACX,GACA,GACA,MACqB;CACrB,IAAM,IAAU,EAAc,GAAO,CAAS;CAE9C,QAAQ,EAAM,MAAd;EACE,KAAK,QAEH,OADA,EAAQ,MAAM,KAAK;GAAE,MAAM;GAAQ,WAAW,EAAM;GAAW,KAAK,EAAM;EAAI,CAAC,GACxE;EAET,KAAK,cASH,OARA,EAAQ,MAAM,KACZ,EAAe;GACb,MAAM;GACN,UAAU,EAAM;GAChB,KAAK,EAAM;GACX,OAAO,EAAM;EACf,CAAC,CACH,GACO;EAET,KAAK,mBAUH,OATA,EAAQ,MAAM,KACZ,EAAe;GACb,MAAM;GACN,UAAU,EAAM;GAChB,WAAW,EAAM;GACjB,OAAO,EAAM;GACb,UAAU,EAAM;EAClB,CAAC,CACH,GACO;CAEX;AACF,GCpCa,MACX,GACA,GACA,MACqB;CACrB,IAAI,EAAM,WAAW,OAAO;CAE5B,IAAM,IAAU,EAAc,GAAO,CAAS,GAKxC,IAAW,EAAe;EAC9B,MAAM,EAAM;EACZ,IAAI,EAAM;EACV,MAAM,EAAM;CACd,CAAC;CAED,IAAI,EAAM,OAAO,KAAA,GAAW;EAC1B,IAAM,IAAM,EAAQ,MAAM,WAAW,MAAM,EAAE,SAAS,EAAM,QAAQ,QAAQ,KAAK,EAAE,OAAO,EAAM,EAAE;EAClG,IAAI,MAAQ,IAEV,OADA,EAAQ,MAAM,KAAO,GACd;CAEX;CAGA,OADA,EAAQ,MAAM,KAAK,CAAQ,GACpB;AACT,GCDa,KAAY,MAMvB,EAAe;CACb,MAAM;CACN,YAAY,EAAO;CACnB,UAAU,EAAO;CACjB,OAAO,EAAO;CACd,kBAAkB,EAAO;AAC3B,CAAC,GAaU,MAAsB,GAA4B,MAAiD;CAC9G,IAAM,IAAO,EAAS,CAAI;CAE1B,QAAQ,EAAM,MAAd;EACE,KAAK,yBACH,OAAO,EAAe;GACpB,GAAG;GACH,OAAO;GACP,OAAO,EAAK;GACZ,QAAQ,EAAM;GACd,aAAa,EAAM;EACrB,CAAC;EAGH,KAAK,qBACH,OAAO;GACL,GAAG;GACH,OAAO;GACP,OAAO,EAAK;GACZ,WAAW,EAAM;EACnB;EAGF,KAAK,sBACH,OAAO;GACL,GAAG;GACH,OAAO;GACP,OAAO,EAAK;GACZ,UAAU;IAAE,IAAI;IAAI,UAAU;GAAM;EACtC;EAGF,KAAK,yBACH,OAAO;GACL,GAAG;GACH,OAAO;GACP,OAAO,EAAK;GACZ,UAAU,EAAE,IAAI,EAAM,WAAW;EACnC;CAEJ;AACF,GClEa,MACX,GACA,GACA,MACqB;CAKrB,IAAM,IAAiB,EAAK;CAC5B,IAAI,MAAmB,KAAA,GAErB,OADA,EAAM,SAAS,KAAK;EAAE,gBAAgB,EAAQ;EAAI;CAAQ,CAAC,GACpD;CAET,IAAM,IAAW,EAAM,SAAS,MAAM,MAAM,EAAE,mBAAmB,CAAc;CAS/E,OARI,MAAa,KAAA,IACf,EAAM,SAAS,KAAK;EAAE;EAAgB;CAAQ,CAAC,IAK/C,EAAS,QAAQ,MAAM,KAAK,GAAG,EAAQ,KAAK,GAEvC;AACT,GAYa,MACX,GACA,MACqB;CACrB,IAAM,EAAE,eAAY,cAAW,EAAM;CACrC,OAAO,GAAc,GAAO,EAAM,gBAAgB,GAAY;EAAE,MAAM;EAAe;CAAO,CAAC;AAC/F,GASa,MACX,GACA,MACqB;CACrB,IAAM,EAAE,eAAY,eAAY,EAAM;CACtC,OAAO,GAAc,GAAO,EAAM,gBAAgB,GAAY;EAAE,MAAM;EAAqB;CAAQ,CAAC;AACtG,GAWa,MACX,GACA,MACqB;CACrB,IAAM,EAAE,eAAY,aAAU,cAAW,EAAM;CAC/C,OAAO,GAAc,GAAO,EAAM,gBAAgB,GAAY;EAC5D,MAAM;EACN;EACA,GAAI,MAAW,KAAA,IAAY,CAAC,IAAI,EAAE,UAAO;CAC3C,CAAC;AACH,GAWM,MACJ,GACA,GACA,GACA,MACqB;CACrB,IAAM,IAAQ,GAAU,GAAO,GAAgB,CAAU;CAMzD,OALI,IACF,GAAgB,GAAO,GAAY,CAAU,IAE7C,EAAM,uBAAuB,KAAK;EAAE,sBAAsB;EAAgB;EAAY;CAAW,CAAC,GAE7F;AACT,GAUM,MACJ,GACA,GACA,MACS;CACT,QAAQ,EAAW,MAAnB;EACE,KAAK;GACH,EAAM,QAAQ,MAAM,EAAM,QAAQ,aAAa,GAAmB,EAAM,MAAM;IAC5E,MAAM;IACN;IACA,QAAQ,EAAW;GACrB,CAAC;GACD;EAEF,KAAK;GACH,EAAM,QAAQ,MAAM,EAAM,QAAQ,aAAa,GAAmB,EAAM,MAAM;IAC5E,MAAM;IACN;IACA,WAAW,EAAW;GACxB,CAAC;GACD;EAEF,KAAK;GACH,EAAM,QAAQ,MAAM,EAAM,QAAQ,aAAa,GAC7C,EAAM,MACN,EAAW,UACX,EAAW,MACb;GACA;CAEJ;AACF,GAQa,MAA2B,MAAkC;CACxE,IAAM,IAAmD,CAAC;CAC1D,KAAK,IAAM,KAAW,EAAM,wBAAwB;EAClD,IAAM,IAAQ,GAAU,GAAO,EAAQ,sBAAsB,EAAQ,UAAU;EAC/E,IAAI,CAAC,GAAO;GACV,EAAK,KAAK,CAAO;GACjB;EACF;EACA,GAAgB,GAAO,EAAQ,YAAY,EAAQ,UAAU;CAC/D;CACA,EAAM,yBAAyB;AACjC,GAEM,MAAa,GAAyB,GAAwB,MAAgD;CAClH,IAAM,IAAQ,EAAM,SAAS,MAAM,MAAM,EAAE,mBAAmB,CAAc;CAC5E,IAAI,CAAC,GAAO;CACZ,IAAM,IAAW,GAAe,GAAO,CAAc,GAC/C,IAAQ,EAAY,EAAM,SAAS,GAAU,CAAU;CACxD,OACL,OAAO;EAAE,SAAS,EAAM;EAAS,SAAS,EAAM;EAAS,MAAM,EAAM;CAAK;AAC5E,GAiBM,MACJ,GACA,GACA,MAEI,IACK;CACL,GAAG,EAAS,CAAI;CAChB,OAAO;CACP,OAAO,WAAW,IAAO,EAAK,QAAQ,KAAA;CACtC,UAAU;EACR,IAAI,cAAc,KAAQ,EAAK,WAAW,EAAK,SAAS,KAAK;EAC7D,UAAU;EACV,GAAI,MAAW,KAAA,IAAY,CAAC,IAAI,EAAE,UAAO;CAC3C;AACF,IAEK,GAAmB,GAAM;CAC9B,MAAM;CACN,YAAY,EAAK;CACjB,GAAI,MAAW,KAAA,IAAY,CAAC,IAAI,EAAE,UAAO;AAC3C,CAAC,GC3OG,MAAwB,GAAyB,GAAmB,MAA6C;CACrH,IAAI,MAAa,KAAA,GAAW;CAC5B,IAAM,IAAU,EAAM,SAAS,MAAM,MAAM,EAAE,mBAAmB,CAAS,GAAG;CAC5E,AAAI,MAAS,EAAQ,WAAW;AAClC,GASa,MACX,GACA,GAIA,MACqB;CACrB,QAAQ,EAAM,MAAd;EACE,KAAK,SAAS;GAQZ,IAAM,IAAU,EAAc,GAAO,CAAS;GAG9C,OAFI,EAAM,cAAc,KAAA,MAAW,EAAQ,KAAK,EAAM,YAClD,EAAM,oBAAoB,KAAA,MAAW,EAAQ,WAAW,EAAM,kBAC3D;EACT;EACA,KAAK,cAGH,OADA,EAD8B,GAAO,CACrC,EAAQ,MAAM,KAAK,EAAE,MAAM,aAAa,CAAC,GAClC;EAET,KAAK,eAAe;GAGlB,IAAM,IAAW,EAAM,SAAS,IAAI,CAAS;GAK7C,OAJI,MACF,EAAS,KAAK,MAAM,GACpB,EAAS,UAAU,MAAM,IAEpB;EACT;EACA,KAAK,UAGH,OAFA,GAAqB,GAAO,GAAW,EAAM,eAAe,GAErD;EAET,KAAK;EACL,KAAK,SAGH,OAAO;EAET,KAAK,oBAEH,OADA,GAAqB,GAAO,GAAW,EAAM,eAAe,GACrD;CAEX;AACF,GCpEa,MACX,GACA,GAIA,MACqB;CACrB,IAAM,IAAU,EAAc,GAAO,CAAS,GACxC,IAAW,GAAe,GAAO,CAAS,GAE1C,IAAS,EAAM,KAAK,WAAW,OAAO,GACtC,IAAW,IAAS,SAAS,aAC7B,IAAY,IAAS,EAAS,OAAO,EAAS;CAEpD,QAAQ,EAAM,MAAd;EACE,KAAK;EACL,KAAK,mBAGH,OAFA,EAAU,IAAI,EAAM,IAAI,EAAQ,MAAM,MAAM,GAC5C,EAAQ,MAAM,KAAK;GAAE,MAAM;GAAU,MAAM;EAAG,CAAC,GACxC;EAET,KAAK;EACL,KAAK,mBAAmB;GACtB,IAAM,IAAM,EAAU,IAAI,EAAM,EAAE;GAClC,IAAI,MAAQ,KAAA,GAAW,OAAO;GAC9B,IAAM,IAAO,EAAQ,MAAM;GAI3B,OAHI,GAAM,SAAS,MACjB,EAAK,QAAQ,EAAM,QAEd;EACT;EACA,KAAK;EACL,KAAK,iBAEH,OADA,EAAU,OAAO,EAAM,EAAE,GAClB;CAEX;AACF,GCnCa,MACX,GACA,GAIA,MACqB;CACrB,IAAM,IAAU,EAAc,GAAO,CAAS,GACxC,IAAW,GAAe,GAAO,CAAS;CAEhD,QAAQ,EAAM,MAAd;EACE,KAAK,oBAAoB;GACvB,IAAM,IAAY,EAAQ,MAAM;GAGhC,OAFA,EAAQ,MAAM,KAAK;IAAE,GAAG,EAAS,CAAK;IAAG,OAAO;IAAmB,OAAO,KAAA;GAAU,CAAC,GACrF,EAAS,MAAM,IAAI,EAAM,YAAY;IAAE;IAAW,WAAW;GAAG,CAAC,GAC1D;EACT;EACA,KAAK,oBAAoB;GACvB,IAAM,IAAU,EAAS,MAAM,IAAI,EAAM,UAAU;GACnD,IAAI,CAAC,GAAS,OAAO;GACrB,EAAQ,aAAa,EAAM;GAE3B,IAAM,IAAc,EAAU,EAAQ,SAAS,GAEzC,IAAQ,EAAY,GAAS,GAAU,EAAM,UAAU;GAO7D,OANK,MACL,EAAQ,MAAM,EAAM,QAAQ,aAAa;IACvC,GAAG,EAAS,EAAM,IAAI;IACtB,OAAO;IACP,OAAO;GACT,IALmB;EAOrB;EACA,KAAK,wBAAwB;GAC3B,IAAM,IAAQ,EAAY,GAAS,GAAU,EAAM,UAAU;GAO7D,OANK,MACL,EAAQ,MAAM,EAAM,QAAQ,aAAa;IACvC,GAAG,EAAS,EAAM,IAAI;IACtB,OAAO;IACP,OAAO,EAAM;GACf,IALmB;EAOrB;EACA,KAAK,oBAAoB;GACvB,IAAM,IAAQ,EAAY,GAAS,GAAU,EAAM,UAAU;GAC7D,IAAI,GACF,EAAQ,MAAM,EAAM,QAAQ,aAAa;IACvC,GAAG,EAAS,EAAM,IAAI;IACtB,OAAO;IACP,OAAO,EAAM;IACb,WAAW,EAAM;GACnB;QACK;IACL,IAAM,IAAY,EAAQ,MAAM;IAOhC,AANA,EAAQ,MAAM,KAAK;KACjB,GAAG,EAAS,CAAK;KACjB,OAAO;KACP,OAAO,EAAM;KACb,WAAW,EAAM;IACnB,CAAC,GACD,EAAS,MAAM,IAAI,EAAM,YAAY;KAAE;KAAW,WAAW;IAAG,CAAC;GACnE;GACA,OAAO;EACT;CACF;AACF,GC1DM,MAAqB,GAAyB,MAAgD;CAClG,KAAK,IAAM,KAAS,EAAM,UAAU;EAClC,IAAM,IAAW,EAAM,SAAS,IAAI,EAAM,cAAc;EACxD,IAAI,CAAC,GAAU;EACf,IAAM,IAAQ,EAAY,EAAM,SAAS,GAAU,CAAU;EAC7D,IAAI,GAAO,OAAO;GAAE,SAAS,EAAM;GAAS,SAAS,EAAM;GAAS,MAAM,EAAM;EAAK;CACvF;AAEF,GASa,MACX,GACA,GAIA,MACqB;CASrB,IAAI,EAAM,SAAS,2BAA2B,EAAM,SAAS,qBAAqB;EAChF,IAAM,IAAQ,GAAkB,GAAO,EAAM,UAAU;EAGvD,OAFK,MACL,EAAM,QAAQ,MAAM,EAAM,QAAQ,aAAa,GAAmB,EAAM,MAAM,CAAK,IADhE;CAGrB;CAKA,IAAM,IAAU,EAAc,GAAO,CAAS,GAGxC,IAAQ,EAAY,GAFT,GAAe,GAAO,CAEJ,GAAU,EAAM,UAAU;CAI7D,OAHK,MAEL,EAAQ,MAAM,EAAM,QAAQ,aAAa,GAAmB,EAAM,MAAM,CAAK,IAF1D;AAIrB,GCRa,MACX,GACA,GACA,MACqB;CACrB,IAAI,EAAM,cAAc,SAAS;EAC/B,IAAM,IAAQ,EAAM;EACpB,QAAQ,EAAM,MAAd;GACE,KAAK;IACH,GAAgB,GAAO,EAAM,SAAS,CAAI;IAC1C;GAEF,KAAK,cAIH;GAEF,KAAK;IACH,GAAqB,GAAO,CAAK;IACjC;GAEF,KAAK;IACH,GAA0B,GAAO,CAAK;IACtC;GAEF,KAAK;IACH,GAAyB,GAAO,CAAK;IACrC;EAEJ;CACF,OACE,GAAU,GAAO,EAAM,OAAO,CAAI;CAUpC,OAJI,EAAM,uBAAuB,SAAS,KACxC,GAAwB,CAAK,GAGxB;AACT,GAMM,MAAa,GAAyB,GAAqB,MAAwC;CACvG,IAAM,IAAY,EAAK;CACvB,IAAI,MAAc,KAAA,GAEhB,OAAO;CAGT,QAAQ,EAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,oBACH,OAAO,GAAc,GAAO,GAAO,CAAS;EAG9C,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,iBACH,OAAO,GAAoB,GAAO,GAAO,CAAS;EAGpD,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,oBACH,OAAO,GAAc,GAAO,GAAO,CAAS;EAG9C,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,yBACH,OAAO,GAAe,GAAO,GAAO,CAAS;EAG/C,KAAK;EACL,KAAK;EACL,KAAK,mBACH,OAAO,GAAgB,GAAO,GAAO,CAAS;EAGhD,SAIE,OAHI,EAAM,KAAK,WAAW,OAAO,IACxB,GAAa,GAAO,GAAO,CAAS,IAEtC;CAEX;AACF,GC9Ia,IAAiB,GAAuC,EAAE;CAErE,YAAY;CACZ,SAAS;EAAE;EAAM;EAAM,cD0JG,MAA+D,EAAW;CC1JjE;CACnC,QAAQ;CACR,OAAO;CACP,iBAAiB;AACnB,CAAC,GCgCK,MAAgB,OAA6B;CACjD,YAAY,GAAgB,GAAgB,MAAqB;EAC/D,EAAO,MAAM,GAAQ,EAAE,QAAQ,EAAQ,CAAC;CAC1C;CACA,iBAAiB;AACnB,IAMM,KACJ,EAAK,SACL,cAYW,KAAb,cAA6C,GAAgC;CAK3E,YAAY,GAAgB;EAC1B,MAAM,GAAa,CAAM,CAAC;CAC5B;AACF,GC/CY,KAAL,yBAAA,GAAA;QAKL,EAAA,QAAA,SAMA,EAAA,QAAA,SAKA,EAAA,OAAA,QAMA,EAAA,OAAA,QAMA,EAAA,QAAA,SAKA,EAAA,SAAA;AACF,EAAA,CAAA,CAAA,GAuBa,MAAiB,GAAiB,GAAiB,MAAyB;CACvF,IAAM,IAAgB,IAAU,cAAc,KAAK,UAAU,CAAO,MAAM,IACpE,IAAmB,qBAAI,IAAI,KAAK,GAAE,YAAY,EAAE,IAAI,EAAM,QAAQ,EAAE,YAAY,EAAE,sBAAsB,IAAU;CAExH,QAAQ,GAAR;EACE,KAAA;EACA,KAAA;GACE,QAAQ,IAAI,CAAgB;GAC5B;EAEF,KAAA;GACE,QAAQ,KAAK,CAAgB;GAC7B;EAEF,KAAA;GACE,QAAQ,KAAK,CAAgB;GAC7B;EAEF,KAAA;GACE,QAAQ,MAAM,CAAgB;GAC9B;EAEF,KAAA,UACE;CAEJ;AACF,GAsBa,MAAc,MAGlB,IAAI,GAFQ,EAAQ,cAAc,IAEJ,EAAQ,QAAQ,GAkBjD,KAAoB,IAAI,IAA8B;CAC1D,CAAA,SAAA,CAAqC;CACrC,CAAA,SAAA,CAAqC;CACrC,CAAA,QAAA,CAAmC;CACnC,CAAA,QAAA,CAAmC;CACnC,CAAA,SAAA,CAAqC;CACrC,CAAA,UAAA,CAAuC;AACzC,CAAC,GAKK,KAAN,MAAM,EAAgC;CAKpC,YAAY,GAAqB,GAAiB,GAAsB;EAEtE,AADA,KAAK,WAAW,GAChB,KAAK,WAAW;EAEhB,IAAM,IAAc,GAAkB,IAAI,CAAK;EAC/C,IAAI,MAAgB,KAAA,GAClB,MAAM,IAAI,EAAK,UAAU,+CAA+C,KAAS,EAAU,iBAAiB,GAAG;EAGjH,KAAK,eAAe;CACtB;CAEA,MAAM,GAAiB,GAA4B;EACjD,KAAK,OAAO,GAAA,SAAA,GAA+C,CAAO;CACpE;CAEA,MAAM,GAAiB,GAA4B;EACjD,KAAK,OAAO,GAAA,SAAA,GAA+C,CAAO;CACpE;CAEA,KAAK,GAAiB,GAA4B;EAChD,KAAK,OAAO,GAAA,QAAA,GAA6C,CAAO;CAClE;CAEA,KAAK,GAAiB,GAA4B;EAChD,KAAK,OAAO,GAAA,QAAA,GAA6C,CAAO;CAClE;CAEA,MAAM,GAAiB,GAA4B;EACjD,KAAK,OAAO,GAAA,SAAA,GAA+C,CAAO;CACpE;CAEA,YAAY,GAA6B;EAGvC,IAAM,IACJ,CAAC,GAAG,GAAkB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAW,MAAU,KAAK,YAAY,IAAI,MAAA;EAEtF,OAAO,IAAI,EAAc,KAAK,UAAU,GAAe,KAAK,cAAc,CAAO,CAAC;CACpF;CAEA,OAAe,GAAiB,GAAiB,GAA6B,GAA4B;EACxG,AAAI,KAAe,KAAK,gBACtB,KAAK,SAAS,GAAS,GAAO,KAAK,cAAc,CAAO,CAAC;CAE7D;CAEA,cAAsB,GAA8C;EAKlE,OAJK,KAAK,WAIH,IAAU;GAAE,GAAG,KAAK;GAAU,GAAG;EAAQ,IAAI,KAAK,WAHhD,KAAW,KAAA;CAItB;AACF,GCtOa,MAAc,OACxB,EAAK,SAAS,kBAAkB,EAAK,KAAK,WAAW,OAAO,MAAM,gBAAgB,KAAQ,WAAW,GCgBlG,MAAmB,MACvB,EAAO,SAAS,YAAY,EAAO,SAAS,WAAW,EAAO,SAAS,SA4B5D,MACX,GACA,GACA,MACoB;CACpB,IAAM,IAAyE,CAAC,GAG1E,IAA8B,CAAC,GAC/B,IAAS,IAAI,eAA6B;EAC9C,QAAQ,MAAe;GACrB,EAAO,aAAa;EACtB;EACA,cAAc;GACZ,EAAS;EACX;CACF,CAAC,GACK,EAAE,kBAAe;CACvB,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,oFACA,EAAU,0BACV,GACF;CAMF,IAAI;CAGJ,EAAW,MACR,MAAO;EACN,IAAgB;CAClB,SACM,CAEN,CACF;CAEA,IAAI,IAAU,IACR,UAAuB;EAC3B,KAAK,IAAM,KAAS,GAAa,EAAM;EACvC,EAAY,SAAS;CACvB,GAGM,KAAU,MAA6B;EACvC,QACJ;OAAU;GACV,IAAI;IACF,EAAO;GACT,QAAQ,CAER;GACA,EAAS;EANC;CAOZ,GACM,UAAoB;EACxB,QAAa;GACX,EAAW,MAAM;EACnB,CAAC;CACH,GACM,KAAS,MAAiC;EAC9C,QAAa;GACX,EAAW,MAAM,CAAM;EACzB,CAAC;CACH;CA+BA,OA7BA,EAAY,KACV,EAAQ,KAAK,GAAG,WAAW,MAAU;EAC/B,MAAM,wBAAwB,GAClC,KAAK,IAAM,KAAU,EAAM,QAAQ;GACjC,IAAI;IACF,EAAW,QAAQ,CAAM;GAC3B,QAAQ;IACN,EAAM;IACN;GACF;GACA,IAAI,GAAgB,CAAM,GAAG;IAC3B,EAAM;IACN;GACF;EACF;CACF,CAAC,GACD,EAAQ,KAAK,GAAG,QAAQ,MAAU;EAIhC,AAAI,EAAM,SAAS,SAAS,MAAkB,KAAA,KAAa,EAAM,UAAU,KACzE,EAAM;CAEV,CAAC,GACD,EAAQ,GAAG,UAAU,MAAW;EAC9B,EAAM,CAAM;CACd,CAAC,CACH,GAEO;EAAE;EAAQ;CAAM;AACzB,GC7FM,KAAqB,aAmHrB,MACJ,MAC+F;CAC/F,IAAI,GACE,IAAO,IAAI,SAAe,MAAY;EAC1C,IAAc;CAChB,CAAC,GAEK,IAAc,IAAI,gBAAsB,EAC5C,aAAa;EACX,EAAY;CACd,EACF,CAAC,GAKK,IAAiB,IAAI,gBAAgB;CAU3C,OAJA,EAAO,OAAO,EAAY,UAAU;EAAE,QAAQ,EAAe;EAAQ,eAAe;CAAK,CAAC,EAAE,YAAY;EACtG,EAAY;CACd,CAAC,GAEM;EACL,QAAQ,EAAY;EACpB;EACA,OAAO,MAA2B;GAChC,EAAe,MAAM,CAAM;EAC7B;CACF;AACF,GAkBM,MAAyB,MAC7B,EAAI,SAAS,eACb,EAAI,MAAM,MACP,MACC,GAAW,CAAC,MACX,EAAE,UAAU,qBAAqB,EAAE,UAAU,qBAAqB,EAAE,UAAU,qBACnF,GAOI,KAAyB,IAAI,IAAI;CAAC;CAAmB;CAAmB;AAAoB,CAAC,GAkC7F,MACJ,GACA,MACkB;CAClB,IAAM,IAAwB,CAAC;CAC/B,KAAK,IAAM,KAAW,GAAU;EAC9B,IAAI,EAAQ,SAAS,aAAa;EAMlC,IAAM,IAAY,EAAc,MAAM,MAAM,EAAE,QAAQ,OAAO,EAAQ,EAAE;EACvE,IAAI,CAAC,GAAW;EAChB,IAAM,EAAE,mBAAgB,SAAS,MAAgB;EAEjD,KAAK,IAAM,KAAe,EAAQ,OAAO;GACvC,IAAI,CAAC,GAAW,CAAW,GAAG;GAM9B,IAAM,IAAW,EAAY,MAAM,MAChC,MAAoD,GAAW,CAAC,KAAK,EAAE,eAAe,EAAY,UACrG;GAMA,IAAI,EAAY,UAAU,yBAAyB,CAAC,KAAY,EAAS,UAAU,uBAAuB;IACxG,EAAO,KACL,EAAe,2BAA2B,GAAgB;KACxD,YAAY,EAAY;KACxB,UAAU,EAAY,SAAS;KAC/B,GAAI,EAAY,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,EAAY,SAAS,OAAO;IAC7F,CAAC,CACH;IACA;GACF;GAOI,EAAY,UAAU,sBAAsB,EAAY,UAAU,kBAElE,KAAY,CAAC,GAAuB,IAAI,EAAS,KAAK,MAEtD,EAAY,UAAU,qBACxB,EAAO,KACL,EAAe,iBAAiB,GAAgB;IAC9C,YAAY,EAAY;IACxB,QAAQ,EAAY;GACtB,CAAC,CACH,IAEA,EAAO,KACL,EAAe,sBAAsB,GAAgB;IACnD,YAAY,EAAY;IACxB,SAAS,EAAY;GACvB,CAAC,CACH;EAEJ;CACF;CACA,OAAO;AACT,GAYM,MAA0B,GAA6C,MAAyC;CACpH,IAAM,IAAM,EAAc,WAAW,MAAM,EAAE,QAAQ,OAAO,CAAQ;CAChE,WAAO,IACX,OAAO,EAAc,IAAM,IAAI;AACjC,GA2Ba,MACX,GACA,MACkB;CAElB,IAAM,IAAM,GAAa,OAAO,IAC1B,IAAU,GAAa,SAAS,WAAW,MAAM,KAAK,UAAU,GAChE,IAAc,GAAa,aAQ7B,IAAa,IACX,IAAU,IAAI,GAAqC,GAAW,EAAE,UAAU,GAAS,OAAO,CAAC,CAAC,GAE5F,KAAgB,MAAyB;EAE7C,AADA,IAAa,GACb,EAAQ,KAAK,aAAa,CAAK;CACjC;CAoQA,OAAO;EACL,qBAjQyD,MAAS;GAClE,IAAM,EAAE,aAAU,gBAAa,YAAS,iBAAc,GAOhD,IAAgB,EAAQ,KAAK,YAAY,GACzC,IAAoB,IAAI,IAAI,EAAc,KAAK,MAAM,CAAC,EAAE,QAAQ,IAAI,EAAE,cAAc,CAAC,CAAC,GACtF,KAAa,MAAyC,EAAkB,IAAI,CAAQ,GAiBpF,IAAc,EAAS,GAAG,EAAE,GAC5B,IAAoB,CAAC,CAAC,KAAe,EAAkB,IAAI,EAAY,EAAE,GACzE,IAAiB,MAAY,oBAAoB,GAAa,SAAS,eAAe,GActF,IACJ,MAAY,oBAAoB,CAAC,KAAa,GAAa,SAAS,SAAS,EAAS,GAAG,EAAE,IAAI,KAAA,GAG3F,IACJ,KAAoB,GAAsB,CAAgB,KAAK,EAAkB,IAAI,EAAiB,EAAE,IACpG,EAAiB,KACjB,KAAA,GAGF,GACA;GAEJ,IAAI,MAAY,wBAAwB,GAEtC,AADA,IAAc,CAAC,GACf,IAAU;QACL;IACL,IAAI,EAAS,WAAW,GACtB,MAAM,IAAI,EAAK,UACb,+EACA,EAAU,iBACV,GACF;IAQF,AAJA,IAAc,CAAC,EAAS,GAAG,EAAE,CAAiB,GAI9C,IAAU,IAAqB,EAAS,MAAM,GAAG,EAAE,IAAI,EAAS,MAAM,GAAG,EAAE;GAC7E;GAMA,IAAI,GACA;GAEJ,AAAI,MAAY,oBAAoB,KAAa,CAAC,KAIhD,IAAS,EAAU,CAAS,GAC5B,IAAS,GAAuB,GAAe,CAAS,KAC/C,MAGT,IAAS,EAAU,CAAkB,GACrC,IAAS,GAAuB,GAAe,CAAkB;GAGnE,IAAI,GACA;GAEJ,IAAI,GAAa,4BAA4B;IAC3C,IAAM,IAAW,EAAY,2BAA2B;KACtD,QAAQ,EAAK;KACb;KACA;KACA;KACA,UAAU;KACV;KACA;IACF,CAAC;IAED,AADA,IAAW,EAAS,QAAQ,CAAC,GAC7B,KAAc,EAAS;GACzB,OAEE,AADA,IAAW,CAAC,GACZ,KAAc,KAAA;GAGhB,IAAM,IAAwB,CAAC;GAM/B,IALI,MAAW,KAAA,MAAW,EAAS,SAAS,IACxC,MAAW,KAAA,MAAW,EAAS,SAAS,IAIxC,GAAgB;IAGlB,IAAM,IAAU,EAAU,EAAY,EAAE,GAClC,IAAM,MAAY,KAAA,IAAY,KAAA,IAAY,EAAQ,KAAK,MAAM,CAAO;IAC1E,AAAI,MAAK,EAAS,QAAQ,EAAI;GAChC;GAgBA,IAAI;GACJ,IAAI,GAAgB;IAClB,IAAM,IAAS,GAAyB,GAAe,CAAQ;IAC/D,IAAM,MAAM,EAAQ,KAAK,KAAK,GAAQ,CAAQ;GAChD,OAAO,IAAI,MAAY,sBAAsB;IAC3C,IAAI,MAAc,KAAA,GAChB,MAAM,IAAI,EAAK,UACb,4EACA,EAAU,iBACV,GACF;IAGF,IAAM,IAAe,EAAU,CAAS;IACxC,IAAI,MAAiB,KAAA,GACnB,MAAM,IAAI,EAAK,UACb,8CAA8C,KAC9C,EAAU,iBACV,GACF;IAEF,IAAM,MAAM,EAAQ,KAAK,WAAW,GAAc,CAAQ;GAC5D,OAAO;IACL,IAAM,IAAS,EAAY,KAAK,MAAM,EAAe,kBAAkB,CAAC,CAAC;IACzE,IAAM,MAAM,EAAQ,KAAK,KAAK,GAAQ,CAAQ;GAChD;GASA,IAAM,IAAY,GAAsB,GAAS,EAAI,OAAO,EAAI,mBAAmB;GAEnF,IAAI,GAAa;IACf,IAAM,UAAsB;KAM1B,AAHA,EAAS,OAAO,GAGhB,EAAU,MAAM;IAClB;IASA,AAAI,EAAY,UACd,EAAQ,IAER,EAAY,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC;GAEjE;GAKA,IAAM,EAAE,WAAQ,SAAM,aAAS,GAAmB,EAAU,MAAM;GAIlE,AAHA,EAAa,EAAI,GAGjB,EAAU,WAAW;IACnB,EAAa,EAAK;GACpB,CAAC;GAWD,IAAM,IAAW;IAAE,GAAG;IAAU,GAAG,EAAI,aAAa,EAAE,OAAO;GAAE;GA6B/D,OA5BA,EAAQ,GAAK;IACX,QAAQ;IACR,SAAS;KAAE,gBAAgB;KAAoB,GAAG;IAAY;IAC9D,MAAM,KAAK,UAAU,CAAQ;IAC7B,GAAI,IAAc,EAAE,eAAY,IAAI,CAAC;GACvC,CAAC,EACE,MAAM,MAAa;IAClB,AAAK,EAAS,MACZ,GACE,IAAI,EAAK,UACP,gCAAgC,EAAI,YAAY,OAAO,EAAS,MAAM,EAAE,GAAG,EAAS,cACpF,EAAU,mBACV,EAAS,MACX,CACF;GAEJ,CAAC,EACA,OAAO,MAAmB;IACzB,GACE,IAAI,EAAK,UACP,gCAAgC,EAAI,WAAW,EAAa,CAAK,KACjE,EAAU,mBACV,KACA,EAAW,CAAK,CAClB,CACF;GACF,CAAC,GAEI;EACT;EAUE,yBAAyB,QAAQ,QAAQ,IAAI;EAE7C,OAAO,YAAY,EAAQ,MAAM;EAEjC,IAAI,YAAqB;GACvB,OAAO;EACT;EAEA,oBAAoB,OAClB,EAAQ,GAAG,aAAa,CAAQ,SACnB;GACX,EAAQ,IAAI,aAAa,CAAQ;EACnC;CAEJ;AACF,GChsBa,KAAU,SCkBjB,KAAW,mBASX,MACJ,GAIA,MACkC;CAClC,IAAM,IAAW;CAEjB,OADA,EAAS,QAAQ,SAAS;EAAE,GAAG,EAAS,QAAQ;EAAQ,GAAG;CAAO,GAC3D,EAAE,QAAQ,EAAE,OAAO,GAAW,CAAM,EAAE,EAAE;AACjD,GAQM,MAAe,MAAqE;CACxF,IAAM,IAAa,GAAO,YACpB,IAAiC,GAAG,KAAW,GAAQ;CAE7D,OADI,MAAY,EAAO,KAAc,KAC9B;AACT,GAOM,MAAc,MAClB,OAAO,QAAQ,CAAM,EAClB,KAAK,CAAC,GAAM,OAAa,GAAG,EAAK,GAAG,GAAS,EAC7C,KAAK,GAAG,GA2BA,MACX,GACA,MACkC,GAAa,GAAQ,GAAY,CAAK,CAAC,GCzD9D,KAA8C;CACzD;CACA;CACA;CACA;CACA;AACF,GAeM,KAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GAkBa,MAAuB,MAA6E;CAC/G,IAAI,MAAe,KAAA,KAAa,EAAW,WAAW,GAAG;CACzD,IAAM,IAAY,IAAI,IAAsB,CAAC,GAAG,IAAgB,GAAG,CAAU,CAAC,GACxE,IAAU,GAAW,QAAQ,MAAS,EAAU,IAAI,CAAI,CAAC,GACzD,IAAU,CAAC,GAAG,CAAS,EAAE,QAAQ,MAAS,CAAC,GAAW,SAAS,CAAI,CAAC,EAAE,SAAS;CACrF,OAAO,CAAC,GAAG,GAAS,GAAG,CAAO;AAChC,GCrBM,MAAS,GAAY,MACzB,IAAI,SAAe,GAAS,MAAW;CACrC,IAAI,GAAQ,SAAS;EACnB,EAAO,IAAI,EAAK,UAAU,kCAAkC,EAAU,iBAAiB,GAAG,CAAC;EAC3F;CACF;CACA,IAAM,IAAgD,iBAAiB;EAErE,AADA,GAAQ,oBAAoB,SAAS,CAAO,GAC5C,EAAQ;CACV,GAAG,CAAE;CAGL,AAAI,OAAO,KAAU,YAAU,EAAM,MAAM;CAC3C,IAAM,UAAsB;EAE1B,AADA,aAAa,CAAK,GAClB,EAAO,IAAI,EAAK,UAAU,kCAAkC,EAAU,iBAAiB,GAAG,CAAC;CAC7F;CACA,GAAQ,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC;AAC3D,CAAC,GAaG,KAAqB,OACzB,GACA,GACA,GACA,GACA,MACmE;CACnE,IAAI;CACJ,KAAK,IAAI,IAAU,GAAG,KAAW,GAAY,KAAW;EACtD,IAAI,GAAQ,SACV,MAAM,IAAI,EAAK,UACb,gDACA,EAAU,iBACV,KACA,EAAW,CAAS,CACtB;EAEF,IAAI;GACF,OAAO,MAAM,EAAU;EACzB,SAAS,GAAO;GAEd,IADA,IAAY,GACR,MAAY,GAAY;GAC5B,IAAM,IAAU,IAAmB,KAAK;GAMxC,AALA,GAAQ,MAAM,sEAAsE;IAClF,SAAS,IAAU;IACnB;IACA;GACF,CAAC,GACD,MAAM,GAAM,GAAS,CAAM;EAC7B;CACF;CACA,MAAM,IAAI,EAAK,UACb,iCAAiC,EAAa,CAAS,KACvD,EAAU,oBACV,KACA,EAAW,CAAS,CACtB;AACF,GAkBa,KAAmB,OAC9B,GACA,MACgC;CAChC,IAAM,EAAE,cAAW,iBAAc,IAAM,WAAQ,gBAAa,GAAG,oBAAiB,KAAK,cAAW;CAEhG,IAAI,GAAQ,SACV,MAAM,IAAI,EAAK,UAAU,0CAA0C,EAAU,iBAAiB,GAAG;CAGnG,MAAM,EAAQ,OAAO;CAErB,IAAM,IAA4C;EAAE,OAAO;EAAW;CAAY,GAE9E,IAAqE,MAAM,SAEvE,EAAQ,QAAQ,CAAa,GACnC,GACA,GACA,GACA,CACF,GACI,IAAe;CA2CnB,OAAO;EAAE,eArCH,MAAgB,KAAA,KAChB,GAAQ,UAAgB,KACvB,IACE,EAAY,QAAQ,IADD;EAmCV,kBA/B4D;GAC5E,IAAI,MAAgB,KAAA,GAAW;GAC/B,IAAI,GAAQ,SACV,MAAM,IAAI,EAAK,UAAU,0CAA0C,EAAU,iBAAiB,GAAG;GAGnG,IAAI,CAAC,GAEH,OADA,IAAe,IACR,EAAY;GAGrB,IAAI,CAAC,EAAY,QAAQ,GAAG;IAC1B,IAAc,KAAA;IACd;GACF;GAEA,IAAM,IAAkE,MAAM,GAC5E,YAAa,MAAM,GAAa,KAAK,KAAM,KAAA,GAC3C,GACA,GACA,GACA,CACF;GACA,IAAI,CAAC,GAAU;IACb,IAAc,KAAA;IACd;GACF;GAEA,OADA,IAAc,GACP,EAAS;EAClB;CAEuB;AACzB,GCvIa,MACX,GACA,GACA,GACA,MAC6C;CAC7C,IAAI,MAAW,KAAA,GAAW,OAAO,CAAC;CAClC,IAAM,IAAyC,CAAC,GAC5C,IAAU,EAAK,wBAAwB,CAAM,GAC3C,oBAAO,IAAI,IAAY,GACzB,IAAO;CACX,OAAO,MAAY,KAAA,IAAW;EAG5B,IAAM,IAAM,EAAQ,SAAS,QAAQ,EAAQ,QAAQ,EAAQ;EAC7D,IAAI,EAAK,IAAI,CAAG,GAAG;EACnB,IAAI,EAAQ,SAAS,SAAS,EAAQ,UAAU,GAAc;GAG5D,IAAI,MAAY,KAAA,KAAa,KAAQ,GAAS;GAC9C,KAAQ;EACV;EAEA,AADA,EAAK,IAAI,CAAG,GACZ,EAAM,QAAQ,CAAO;EACrB,IAAM,IAAW,EAAQ;EACzB,IAAI,MAAa,KAAA,GAAW;EAC5B,IAAU,EAAK,wBAAwB,CAAQ;CACjD;CACA,OAAO;AACT,GASM,MACJ,GACA,MACW;CACX,IAAI,IAAQ;CACZ,KAAK,IAAM,KAAQ,GAAO,AAAI,EAAK,SAAS,SAAS,EAAK,UAAU,KAAc;CAClF,OAAO;AACT,GAUM,MAAoB,GAAmB,MAAmC;CAC9E,IAAM,IAAU,EAAW,CAAK;CAChC,OAAO,IAAI,EAAK,UACd,aAAa,EAAU,IAAI,EAAa,CAAK,KAC7C,GAAS,QAAQ,EAAU,oBAC3B,GAAS,cAAc,KACvB,CACF;AACF,GA6Ca,KAAb,MAAgH;CAsC9G,YAAY,GAAmE;EAM7E,yBAR0B,IAG1B,KAAK,QAAQ,EAAQ,MACrB,KAAK,WAAW,EAAQ,SACxB,KAAK,SAAS,EAAQ,OACtB,KAAK,WAAW,EAAQ,SACxB,KAAK,wBAAwB,EAAQ,sBACrC,KAAK,UAAU,EAAQ,QAAQ,YAAY,EAAE,WAAW,YAAY,CAAC;CACvE;CAQA,UAAkB,GAAiC;EAEjD,AADA,KAAK,SAAS,MAAM,CAAI,GACxB,KAAK,MAAM,gBAAgB,CAAI;CACjC;CAqCA,eAAe,GAMqB;EAClC,IAAM,EAAE,iBAAc,UAAO,qBAAkB,cAAW,cAAW,GAC/D,IAAS,KAAK,SACd,IAAc,IAAI,IAAI,CAAgB,GACtC,IAAgB,EAAY,MAE5B,oBAAmB,IAAI,IAAiC,GAKxD,IAAoB,IAAI,gBAAgB;EAE9C,OAAO,IAAI,SAAiC,GAAS,MAAW;GAC9D,IAAI,IAAU,IAIV,GAEA,GACA,GAGE,UAAsB;IAI1B,AAHI,KAAgB,EAAe,GAC/B,MAAU,KAAA,KAAW,aAAa,CAAK,GAC3C,EAAkB,MAAM,GACxB,EAAO,oBAAoB,SAAS,CAAW;GACjD,GAEM,UAA0B;IAC1B,MACJ,IAAU,IACV,EAAQ,GACR,EACE,IAAI,EAAK,UACP,sCAAsC,EAAM,iBAC5C,EAAU,iBACV,GACF,CACF;GACF,GAEM,UAAuB;IAC3B,IAAI,GAAS;IAEb,AADA,IAAU,IACV,EAAQ;IAIR,IAAM,IAAS,CAAC,GAAG,EAAiB,OAAO,CAAC,EAAE,SAAS,CAAe,GAClE,GACA;IACJ,KAAK,IAAM,KAAK,GACd,IAAI,MAAiB,KAAA,GAAW;KAE9B,AADA,IAAe,EAAoB,CAAC,GACpC,IAAgB,EAAE;KAClB;IACF;IAOF,AALA,GAAQ,MAAM,sDAAsD;KAClE;KACA;KACA,OAAO,EAAO;IAChB,CAAC,GACD,EAAQ;KAAE,aAAa;KAAQ;KAAc;IAAc,CAAC;GAC9D,GAIM,KAAY,MAAoC;IACpD,IAAI,GAAS,OAAO;IAEpB,IAAM,IADU,EAAoB,CACpB,EAAQ;IAGxB,OAFI,CAAC,KAAW,CAAC,EAAY,IAAI,CAAO,KAAK,EAAiB,IAAI,CAAO,IAAU,MACnF,EAAiB,IAAI,GAAS,CAAC,GACxB,EAAiB,QAAQ;GAClC;GAGA,IADA,EAAO,iBAAiB,SAAS,GAAa,EAAE,MAAM,GAAK,CAAC,GACxD,EAAO,SAAS;IAClB,EAAY;IACZ;GACF;GAKA,KAAK,IAAM,KAAM,GAAkB;IACjC,IAAM,IAAc,KAAK,MAAM,yBAAyB,CAAE;IAE1D,IAAI,KAAe,EAAS,CAAW,KAAK,CAAC,GAAS;KACpD,EAAS;KACT;IACF;GACF;GAiDA,AA1CA,IAAiB,KAAK,MAAM,GAAG,iBAAiB,MAAQ;IACtD,AAAI,EAAS,CAAG,KAAK,CAAC,KAAS,EAAS;GAC1C,CAAC,GASD,KAAK,yBACG,GACN,EAAkB,QAClB,KAAK,uBACL,8BACF,EAAE,OAAO,MAAmB;IACtB,MACJ,IACE,aAAiB,EAAK,YAAY,IAAQ,GAAiB,gCAAgC,CAAK,GAClG,GAAQ,KAAK,6EAA6E,EACxF,OAAO,EAAa,CAAK,EAC3B,CAAC;GACH,CAAC,GAID,IAAQ,iBAAiB;IACnB,MACJ,IAAU,IACV,EAAQ,GACR,EACE,IAAI,EAAK,UACP,2CAA2C,OAAO,EAAiB,IAAI,EAAE,MAAM,OAAO,CAAa,EAAE,+BAA+B,EAAa,UAAU,OAAO,CAAS,EAAE,KAC7K,EAAU,oBACV,KACA,CACF,CACF;GACF,GAAG,CAAS,GAGR,OAAO,KAAU,YAAU,EAAM,MAAM;EAC7C,CAAC;CACH;CA6BA,MAAM,iBACJ,GACA,GACA,GACA,GACA,GACA,GAC4D;EAC5D,IAAI,EAAO,SACT,MAAM,IAAI,EAAK,UACb,oCAAoC,EAAM,iBAC1C,EAAU,iBACV,GACF;EAKF,OAFA,MAAM,KAAK,kBAAkB,GAAO,GAAyB,GAAQ,GAAS,CAAY,GAEnF,KAAK,qBAAqB,GAAO,GAAyB,GAAS,CAAgB;CAC5F;CAkBA,qBACE,GACA,GACA,GACA,GACmD;EACnD,IAAM,IAAO,KAAK,OACZ,IAAQ,GAAkB,GAAM,GAAQ,GAAS,CAAK,GACtD,IAAU,EAAK,WAAW,CAAK,GAC/B,IAAuB,CAAC;EAC9B,KAAK,IAAM,KAAQ,GACjB,KAAK,IAAM,KAAK,KAAK,OAAO,YAAY,EAAK,UAAU,GAAG;GACxD,IAAI,MAAqB,KAAA,KAAa,EAAE,mBAAmB,GACzD,OAAO;IAAE;IAAU,YAAY,GAAS,cAAc,KAAK,OAAO,KAAK;GAAE;GAE3E,EAAS,KAAK,EAAE,OAAO;EACzB;EAGF,IAAI,MAAY,KAAA,KAAa,CAAC,EAAM,MAAM,MAAM,EAAE,SAAS,SAAS,EAAE,UAAU,CAAK,GACnF,KAAK,IAAM,KAAK,KAAK,OAAO,YAAY,EAAQ,UAAU,GACxD,EAAS,KAAK,EAAE,OAAO;EAI3B,OAAO;GAAE;GAAU,YAAY,GAAS,cAAc,KAAK,OAAO,KAAK;EAAE;CAC3E;CAaA,SAAS,GAAe,GAA4B,GAAuC;EACzF,OAAO,KAAK,qBAAqB,GAAO,GAAQ,KAAA,GAAW,CAAgB,EAAE;CAC/E;CAwBA,MAAc,mBACZ,GACA,GACA,GACA,GACiC;EACjC,IAAI,IAAY,IACZ,GACE,IAAO,KAAK,mBAAmB,QAAQ,QAAQ,GAC/C,KAAQ,YAA2B;GACvC,UAAM,EAAK,YAAY,CAEvB,CAAC,GACG,OAAK,qBAAqB,EAAO,WAAW,EAAW,IAC3D,IAAI;IACF,IAAY,MAAM,KAAK,mBAAmB,GAAY,GAAQ,CAAU;GAC1E,SAAS,GAAO;IACd,IAAa,GAAiB,GAAgB,CAAK;GACrD;EACF,GAAG;EAGH,IAFA,KAAK,kBAAkB,GACvB,MAAM,GACF,MAAe,KAAA,GAAW,MAAM;EACpC,OAAO,EAAE,aAAU;CACrB;CAmBA,MAAc,mBACZ,GACA,GACA,GACkB;EAClB,AAAI,KAAK,YAAY,KAAA,MACnB,KAAK,UAAU,MAAM,GAAiB,KAAK,UAAU;GACnD,WAAW;GACX,aAAa;GACb,QAAQ,KAAK;EACf,CAAC;EAEH,IAAM,IAAS,KAAK;EACpB,OAAO,EAAO,QAAQ,KAAK,CAAC,EAAW,IAAG;GACxC,IAAI,EAAO,SAAS,OAAO;GAC3B,IAAM,IAAQ,MAAM,EAAO,KAAK;GAGhC,IAAI,CAAC,GAAO;GAGZ,KAAK,IAAM,KAAQ,EAAM,WAAW,GAClC,KAAK,UAAU,CAAI;GAKrB,IAAI,MAAe,KAAA,GAAW;IAC5B,IAAM,IAAS,EAAM,GAAG,EAAE;IAC1B,IAAI,GAAQ,cAAc,KAAA,KAAa,EAAO,YAAY,KAAK,IAAI,IAAI,GAAY;GACrF;EACF;EAEA,OAAO,CAAC,EAAO,QAAQ,KAAK,CAAC,EAAO;CACtC;CAiBA,MAAc,kBACZ,GACA,GACA,GACA,GACA,GACe;EAGf,IAAM,UAA4B;GAChC,IAAM,IAAO,KAAK;GASlB,IAAI,KAAgB,EAAK,WAAW,CAAK,GAAG,gBAAgB,KAAA,GAAW,OAAO;GAC9E,IAAI,MAAW,KAAA,GAAW,OAAO;GACjC,IAAI,EAAK,wBAAwB,CAAM,MAAM,KAAA,GAAW,OAAO;GAC/D,IAAM,IAAQ,GAAkB,GAAM,GAAQ,GAAS,CAAK,GACtD,IAAO,EAAM,IACb,IAAc,MAAS,KAAA,KAAa,EAAK,yBAAyB,KAAA,GAIlE,IACJ,MAAY,KAAA,KACZ,GAAe,GAAO,CAAK,KAAK,KAChC,MAAS,KAAA,MACR,EAAK,SAAS,SAAS,EAAK,UAAU;GACzC,OAAO,CAAC,KAAe,CAAC;EAC1B;EAIA,IAAI,CAAC,EAAW,KAAK,KAAK,mBAAmB;EAE7C,IAAI;EACJ,IAAI;GAGF,CAAC,iBAAgB,MAAM,KAAK,yBAAyB,CAAC,EAAW,GAAG,GAAQ,KAAA,GAAW,mBAAmB;EAC5G,SAAS,GAAO;GAKd,MAJA,KAAK,SAAS,MAAM,uDAAuD;IACzE;IACA,OAAO,EAAa,CAAK;GAC3B,CAAC,GACK;EACR;EAIA,IAHI,MAAW,KAAK,oBAAoB,KAGpC,EAAO,WAAW,EAAW,GAC/B,MAAM,IAAI,EAAK,UAAU,+CAA+C,EAAU,iBAAiB,GAAG;CAE1G;AACF,GAQa,MAMX,MACsD,IAAI,GAAU,CAAO,GClqBhE,MAAyB,MAYR;CAC5B,IAAM,IAA4B;GAC/B,IAAc,EAAK;GACnB,IAA0B,EAAK;CAClC;CAUA,OATI,EAAK,UAAU,KAAA,MAAW,EAAE,KAAiB,EAAK,QAClD,EAAK,gBAAgB,KAAA,MAAW,EAAE,KAAwB,EAAK,cAC/D,EAAK,WAAQ,EAAE,KAAiB,EAAK,SACrC,EAAK,WAAQ,EAAE,KAAkB,EAAK,SACtC,EAAK,gBAAa,EAAE,KAAyB,EAAK,cAClD,EAAK,iBAAc,EAAE,KAAwB,EAAK,eAClD,EAAK,kBAAkB,KAAA,MAAW,EAAE,KAA0B,EAAK,gBACnE,EAAK,wBAAwB,KAAA,MAAW,EAAE,KAAiC,EAAK,sBAChF,EAAK,iBAAc,EAAE,KAAmB,EAAK,eAC1C;AACT,GA6Ba,MAAyB,MAYR;CAC5B,IAAM,IAA4B;GAC/B,IAAgB,EAAK;GACrB,IAAuB,EAAK;CAC/B;CAUA,OATI,EAAK,WAAW,KAAA,MAAW,EAAE,KAAqB,EAAK,SACvD,EAAK,WAAW,KAAA,MAAW,EAAE,KAAiB,EAAK,SACnD,EAAK,WAAW,KAAA,MAAW,EAAE,KAAkB,EAAK,SACpD,EAAK,gBAAgB,KAAA,MAAW,EAAE,KAAyB,EAAK,cAChE,EAAK,iBAAiB,KAAA,MAAW,EAAE,KAAwB,EAAK,eAChE,EAAK,kBAAkB,KAAA,MAAW,EAAE,KAA0B,EAAK,gBACnE,EAAK,wBAAwB,KAAA,MAAW,EAAE,KAAiC,EAAK,sBAChF,EAAK,cAAc,KAAA,MAAW,EAAE,KAAqB,OAAO,EAAK,SAAS,IAC1E,EAAK,iBAAiB,KAAA,MAAW,EAAE,KAAwB,EAAK,eAC7D;AACT,GAkBa,MAAsB,MACjC,MAAA,kBAA4B,MAAA,oBAA8B,MAAA,mBAA6B,MAAA,cAY5E,MAAoB,MAAoD;CACnF,IAAM,IAAU,EAAQ,IAClB,IAAa,MAAY,KAAA,IAAY,MAAa,OAAO,CAAO,GAChE,IAAO,OAAO,SAAS,CAAU,IAAI,IAAa,EAAU,0BAC5D,IAAU,EAAA,oBAAiC,2BAE3C,IAAa,KAAQ,OAAS,IAAO,MAAQ,KAAK,MAAM,IAAO,GAAG,IAAI;CAC5E,OAAO,IAAI,EAAK,UAAU,GAAS,GAAM,CAAU;AACrD,GAmBa,MACX,GACA,GACA,GACA,MACkC;CAClC,IAAM,IAAQ,EAAQ;CACtB,IAAI,CAAC,GAAO;CAEZ,IAAM,IAAW,EAAA,oBAAiC,IAC5C,IAAU,MAAc,KAAA,IAAY,CAAC,IAAI,EAAE,aAAU;CAE3D,IAAI,MAAA,gBAA0B;EAC5B,IAAM,IAAS,EAAQ,IACjB,IAAS,EAAQ,IACjB,IAAc,EAAQ;EAC5B,OAAO;GACL,MAAM;GACN;GACA;GACA;GACA,cAAc,EAAA,oBAAiC;GAC/C,GAAG;GACH,GAAI,MAAW,KAAA,KAAa,EAAE,UAAO;GACrC,GAAI,MAAW,KAAA,KAAa,EAAE,UAAO;GACrC,GAAI,MAAgB,KAAA,KAAa,EAAE,eAAY;EACjD;CACF;CAEA,IAAI,MAAA,kBACF,OAAO;EAAE,MAAM;EAAW;EAAO;EAAU;EAAQ,cAAc,EAAA,oBAAiC;EAAI,GAAG;CAAQ;CAGnH,IAAI,MAAA,iBACF,OAAO;EAAE,MAAM;EAAU;EAAO;EAAU;EAAQ,cAAc,EAAA,oBAAiC;EAAI,GAAG;CAAQ;CAGlH,IAAI,MAAA,cAAwB;EAE1B,IAAM,IAAU,EAAA,iBAA8B,YACxC,IAAe,EAAA,oBAAiC;EAatD,OAZI,MAAW,UACN;GACL,MAAM;GACN;GACA;GACA;GACA;GACA;GACA,GAAG;GACH,OAAO,GAAiB,CAAO;EACjC,IAEK;GAAE,MAAM;GAAO;GAAO;GAAU;GAAQ;GAAc;GAAQ,GAAG;EAAQ;CAClF;AAGF,GCtMM,MACJ,GACA,GACA,MACkC;CAClC,IAAM,IAAU,EAAoB,CAAM,GACpC,IAAS,EAAO,QAKhB,IAAY,EAAO;CAEzB,IAAI,GAAmB,EAAO,IAAI,GAAG;EACnC,IAAM,IAAQ,GAAkB,EAAO,MAAM,GAAS,GAAQ,CAAS;EAEvE,OADI,KAAO,EAAK,kBAAkB,CAAK,GAChC;CACT;CAEA,IAAM,EAAE,WAAQ,eAAY,EAAQ,OAAO,CAAM;CACjD,CAAI,EAAO,SAAS,KAAK,EAAQ,SAAS,KAAK,EAAA,cAC7C,EAAK,aAAa;EAAE;EAAQ;CAAQ,GAAG,GAAS,GAAQ,GAAW,EAAO,QAAQ,MAAM;AAG5F,GAQa,MACX,GACA,OACiB,EACjB,QAAQ,MAA+D,GAAiB,GAAM,GAAS,CAAM,EAC/G,IChFa,MAA2B,GAAgB,GAAQ,MAAiC;CAC/F,IAAI,EAAI,IAAI,CAAG,KAAK,EAAI,OAAO,GAAO;CACtC,IAAM,IAAS,EAAI,KAAK,EAAE,KAAK,EAAE;CAC7B,UAAW,KAAA,GAEf,OADA,EAAI,OAAO,CAAM,GACV;AACT,GCNM,MAAwB,MAAqF;CACjH,IAAI;CAgBJ,OAAO;EAAE,SAdP,MAAW,KAAA,IAEP,IAAI,cAAoB,CAAC,CAAC,IAC1B,EAAO,UACL,QAAQ,QAAQ,IAChB,IAAI,SAAe,MAAY;GAI7B,AAHA,UAAiB;IACf,EAAQ;GACV,GACA,EAAO,iBAAiB,SAAS,GAAU,EAAE,MAAM,GAAK,CAAC;EAC3D,CAAC;EAIS,eAHU;GAC1B,AAAI,KAAY,KAAQ,EAAO,oBAAoB,SAAS,CAAQ;EACtE;CAC0B;AAC5B,GAea,KAAa,OACxB,GACA,GACA,GACA,GACA,GACA,MAC0B;CAC1B,GAAQ,MAAM,eAAe;CAE7B,IAAM,IAAS,EAAO,UAAU,GAC1B,IAAQ,GAAqB,CAAM,GAErC,IAAiC,YACjC;CAEJ,IAAI;EAEF,SAAa;GAGX,IAAM,IAAS,MAAM,QAAQ,KAAK,CAAC,EAAO,KAAK,GAAG,EAAM,QAAQ,WAAW,WAAoB,CAAC,CAAC;GAEjG,IAAI,MAAW,aAAa;IAS1B,AARA,IAAS,aACT,GAAQ,MAAM,+CAA+C,GACzD,KACF,MAAM,EAAY,OAAO,MAAoB,EAAQ,cAAc,CAAM,CAAC,GAK5E,MAAM,EAAQ,cAAc;IAC5B;GACF;GAEA,IAAM,EAAE,SAAM,aAAU;GACxB,IAAI,GAAM;IAQR,AAFA,MAAM,EAAQ,cAAc,GAC5B,MAAM,EAAQ,MAAM,GACpB,GAAQ,MAAM,gCAAgC;IAC9C;GACF;GAEA,MAAM,EAAQ,cAAc,GAAO,IAAsB,CAAK,CAAC;EACjE;CACF,SAAS,GAAO;EAGd,AAFA,IAAS,SACT,IAAc,aAAiB,QAAQ,IAAY,MAAM,OAAO,CAAK,CAAC,GACtE,GAAQ,MAAM,8BAA8B,EAAE,OAAO,EAAY,QAAQ,CAAC;EAC1E,IAAI;GACF,MAAM,EAAQ,MAAM;EACtB,QAAQ,CAIR;CACF,UAAU;EAER,AADA,EAAM,QAAQ,GACd,EAAO,YAAY;CACrB;CAEA,OAAO;EAAE;EAAQ,OAAO;CAAY;AACtC,GCtBM,KAAN,MAA8C;CAK5C,YAAY,GAA+B,GAAiB;EAE1D,mCAJ6B,IAAI,IAA4B,GAG7D,KAAK,WAAW,GAChB,KAAK,UAAU,GAAQ,YAAY,EAAE,WAAW,aAAa,CAAC;CAChE;CAEA,MAAM,SACJ,GACA,GACA,GACA,GACe;EACf,KAAK,SAAS,MAAM,iCAAiC;GAAE;GAAO;EAAS,CAAC;EAExE,IAAM,IAAa,KAAsB,IAAI,gBAAgB,GACvD,IAAmB,KAAY;EACrC,KAAK,YAAY,IAAI,GAAO;GAAE;GAAY,UAAU;EAAiB,CAAC;EAUtE,IAAM,IAAe,GAAU,iBAAiB,IAE1C,IAAU,GAAsB;GACpC;GACA,aAAa;GACb,QAAQ,IAAe,KAAA,IAAY,GAAU;GAC7C,QAAQ,IAAe,KAAA,IAAY,GAAU;GAC7C,aAAa,IAAe,KAAA,IAAY,GAAU;GAClD,cAAc,GAAU;GACxB,eAAe,GAAU;GACzB,qBAAqB,GAAU;EACjC,CAAC;EAOD,AALA,MAAM,KAAK,SAAS,QAAQ;GAC1B,MAAM,IAAe,IAAmB;GACxC,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAQ,EAAE;EACvC,CAAC,GAED,KAAK,SAAS,MAAM,6CAA6C,EAAE,SAAM,CAAC;CAC5E;CAEA,MAAM,WACJ,GACA,GACA,GACA,GACe;EAGf,AAFA,KAAK,SAAS,MAAM,mCAAmC,EAAE,SAAM,CAAC,GAChE,MAAM,KAAK,iBAAiB,GAAmB,GAAO;GAAE;GAAc;GAAe;EAAoB,CAAC,GAC1G,KAAK,SAAS,MAAM,iDAAiD,EAAE,SAAM,CAAC;CAChF;CAEA,MAAM,OACJ,GACA,GACA,GACA,GACA,GACA,GACe;EACf,KAAK,SAAS,MAAM,+BAA+B;GAAE;GAAO;EAAO,CAAC;EAIpE,IAAM,IAAmB,MAAW,WAAW,IAAQ;GAAE,WAAW,EAAM;GAAM,cAAc,EAAM;EAAQ,IAAI,CAAC;EAQjH,AAPA,MAAM,KAAK,iBAAiB,GAAe,GAAO;GAChD;GACA;GACA;GACA;GACA,GAAG;EACL,CAAC,GACD,KAAK,SAAS,MAAM,yCAAyC;GAAE;GAAO;EAAO,CAAC;CAChF;CAkBA,MAAc,iBACZ,GACA,GACA,GAQe;EAEf,IAAM,IAAU,GAAsB;GAAE;GAAO,aADtB,KAAK,YAAY,IAAI,CAAK,GAAG,YAAY;GACY,GAAG;EAAY,CAAC;EAE9F,AADA,MAAM,KAAK,SAAS,QAAQ;GAAE,MAAM;GAAW,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAQ,EAAE;EAAE,CAAC,GACvF,KAAK,YAAY,OAAO,CAAK;CAC/B;CAEA,YAAY,GAAmC;EAC7C,OAAO,KAAK,YAAY,IAAI,CAAK,GAAG;CACtC;CAEA,QAAc;EACZ,KAAK,SAAS,MAAM,8BAA8B,EAAE,YAAY,KAAK,YAAY,KAAK,CAAC;EACvF,KAAK,IAAM,KAAS,KAAK,YAAY,OAAO,GAC1C,EAAM,WAAW,MAAM;EAEzB,KAAK,YAAY,MAAM;CACzB;AACF,GAYa,MAAoB,GAA+B,MAC9D,IAAI,GAAkB,GAAS,CAAM,GCvN1B,KAAmB,OAAO,GAA2C,MAAkC;CAClH,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,aAAa,EAAO,oCAAoC,EAAO,KAC/D,EAAU,iBACV,GACF;CAEF,OAAO;AACT,GAYa,KAAmB,OAC9B,GACA,GACA,GACA,MACkB;CACd,UAAmB,KAAA,GACvB,IAAI;EACF,MAAM,EAAQ,OAAO;CACvB,SAAS,GAAO;EACd,GAAQ,MAAM,GAAG,EAAU,kCAAkC,EAAE,SAAM,CAAC;CACxE;AACF,GAYa,MAAoB,MAAkD;CACjF,IAAM,EAAE,YAAS,eAAY;CAC7B,OACE,MAAY,YAAY,MAAY,eAAe,MAAY,cAAe,MAAY,cAAc,CAAC;AAE7G,GAUa,MAAuB,GAAsC,MAAiC;CACzG,IAAM,EAAE,eAAY;CACpB,OAAO,IAAI,EAAK,UACd,aAAa,EAAK,6BAA6B,IAAU,MAAY,aAAa,qBAAqB,GAAG,IAC1G,EAAU,uBACV,KACA,EAAY,MACd;AACF,GC1Ea,MACX,MACkC,CAClC,GAAG,EAAQ,OAAO,KAAK,OAAwC;CAAE,WAAW;CAAS;AAAM,EAAE,GAC7F,GAAG,EAAQ,QAAQ,KAAK,OAAwC;CAAE,WAAW;CAAU;AAAM,EAAE,CACjG,GC2Ca,KAAb,MAA6B;;kBACyB,CAAC,iBACpC;;CAQjB,IAAI,QAAiB;EACnB,OAAO,KAAK;CACd;CA0BA,OACE,GACA,GACA,GACA,GACA,GACa;EAIb,IAAM,IAAQ,KAAK,aAAa,GAAQ,GAAW,KAAK,SAAS,CAAC,IAAI,GAAQ,GAAS,CAAQ;EAG/F,OAFI,MAAU,KAAA,IAAkB,YAC5B,KAAK,UACF,MAAU,KAAK,SAAS,SAAS,IADhB,gBACoC;CAC9D;CAQA,OAAO,GAAqF;EAC1F,KAAK,IAAM,KAAS,KAAK,UACvB,KAAK,IAAM,KAAS,EAAM,QAAQ,EAAM,GAAO,EAAM,QAAQ,EAAM,SAAS;CAEhF;CAQA,QAAc;EACZ,KAAK,SAAS;EACd,KAAK,IAAM,KAAS,KAAK,UAAU,EAAM,OAAO,SAAS;CAC3D;CAYA,aACE,GACA,GACA,GACA,GACA,GACoB;EAGpB,KAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;GAClD,IAAM,IAAQ,KAAK,SAAS;GAC5B,IAAI,CAAC,GAAO;GACZ,IAAI,EAAM,WAAW,GASnB,OALI,MAAY,KAAA,MAAc,KAAW,EAAM,kBAAkB,CAAC,KAChE,UAEF,EAAM,OAAO,KAAK,GAAG,CAAM,GACvB,MAAY,KAAA,MAAW,EAAM,iBAAiB,IAC3C;GAET,IAAI,EAAM,SAAS,GAEjB,OADA,KAAK,SAAS,OAAO,IAAI,GAAG,GAAG;IAAE;IAAQ;IAAW,QAAQ,CAAC,GAAG,CAAM;IAAG,gBAAgB,KAAW;GAAO,CAAC,GACrG,IAAI;EAEf;EAGA,OADA,KAAK,SAAS,QAAQ;GAAE;GAAQ;GAAW,QAAQ,CAAC,GAAG,CAAM;GAAG,gBAAgB,KAAW;EAAO,CAAC,GAC5F;CACT;AACF,GCxFa,KAAwB,MACnC,EAAK,SAAS,QAAQ,EAAK,QAAQ,EAAK,gBAQpC,MAA2B,MAC/B,EAAK,SAAS,QAAQ,EAAK,cAAc,EAAK,QAQ1C,MAAqB,GAAqB,GAAQ,MAAmB;CACzE,IAAI,IAAM,EAAI,IAAI,CAAG;CAKrB,AAJK,MACH,oBAAM,IAAI,IAAI,GACd,EAAI,IAAI,GAAK,CAAG,IAElB,EAAI,IAAI,CAAK;AACf,GAQM,MAA0B,GAAqB,GAAQ,MAAmB;CAC9E,IAAM,IAAM,EAAI,IAAI,CAAG;CAClB,MACL,EAAI,OAAO,CAAK,GACZ,EAAI,SAAS,KAAG,EAAI,OAAO,CAAG;AACpC,GAgIa,KAAb,MAIwD;CA0FtD,YAAY,GAA0D,GAAgB;EAGpF,kCApF4B,IAAI,IAAwD,mDAU9C,IAAI,IAAoB,uBAQU,CAAC,uCAQ/C,IAAI,IAAqC,4CAQpC,IAAI,IAAyB,sBAG5C,6BAGO,wCAWL,IAAI,IAA0D,+BACvD,yCASE,IAAI,IAAiC,iBAQrD,sBAUwB,CAAC,GAGxC,KAAK,SAAS,GACd,KAAK,UAAU,GACf,KAAK,WAAW,IAAI,GAAqC,CAAM;CACjE;CAsBA,cACE,GACA,GACQ;EACR,IAAM,IAAK,GAAW,EAAE,IAAI,GACtB,IAAK,GAAW,EAAE,IAAI;EAM5B,OALI,MAAO,KAAA,KAAa,MAAO,KAAA,IAAkB,EAAE,YAAY,EAAE,YAC7D,MAAO,KAAA,IAAkB,IACzB,MAAO,KAAA,KACP,IAAK,IAAW,KAChB,IAAK,IAAW,IACb,EAAE,YAAY,EAAE;CACzB;CAMA,kBAA0B,GAA4D;EAIpF,IAHoB,GAAW,EAAS,IAGpC,MAAgB,KAAA,GAAW;GAC7B,KAAK,aAAa,KAAK,CAAQ;GAC/B;EACF;EAEA,IAAI,IAAK,GACL,IAAK,KAAK,aAAa;EAC3B,OAAO,IAAK,IAAI;GACd,IAAM,IAAO,IAAK,MAAQ,GACpB,IAAU,KAAK,aAAa;GAClC,IAAI,CAAC,GAAS;GACd,AAAI,KAAK,cAAc,GAAS,CAAQ,KAAK,IAC3C,IAAK,IAAM,IAEX,IAAK;EAET;EACA,KAAK,aAAa,OAAO,GAAI,GAAG,CAAQ;CAC1C;CAMA,kBAA0B,GAA4D;EACpF,IAAM,IAAM,KAAK,aAAa,QAAQ,CAAQ;EAC9C,AAAI,MAAQ,MAAI,KAAK,aAAa,OAAO,GAAK,CAAC;CACjD;CAWA,YACE,GACA,GACA,GACM;EAIN,AAHA,KAAK,WAAW,IAAI,GAAK,CAAK,GAC9B,KAAK,kBAAkB,GAAsB,CAAG,GAChD,KAAK,kBAAkB,CAAK,GAC5B,KAAK;CACP;CASA,eAAuB,GAAyD;EAG9E,AAFA,KAAK,kBAAkB,CAAK,GAC5B,KAAK,kBAAkB,CAAK,GAC5B,KAAK;CACP;CAWA,UACE,GACA,GACA,GACA,GACM;EACN,KAAK,IAAM,KAAS,GAClB,IAAI;GACF,EAAM,KAAK,aAAa,KAAK,OAAO,KAAK,EAAM,KAAK,YAAY,GAAO;IAAE,QAAQ,KAAU;IAAI;GAAU,CAAC;EAC5G,SAAS,GAAO;GACd,KAAK,QAAQ,MAAM,gCAAgC;IAAE,KAAK,EAAQ,EAAM,IAAI;IAAG;IAAW,KAAK;GAAM,CAAC;EACxG;CAEJ;CAoCA,eACE,GACA,GACA,GACA,GACA,GACA,GACM;EAGN,IAAI,MAAW,KAAA,KAAa,EAAO,WAAW,GAAG;GAE/C,AADI,MAAW,KAAA,KAAa,EAAO,SAAS,MAAG,EAAM,aAAa,KAClE,KAAK,UAAU,GAAO,GAAQ,GAAQ,CAAS;GAC/C;EACF;EAEA,IAAM,IAAO,EAAM,IAAI,OAAO,GAAQ,GAAW,GAAQ,GAAS,CAAQ;EAC1E,IAAI,MAAS,WAAW;GAKtB,KAAK,QAAQ,MAAM,kEAAkE;IACnF,KAAK,EAAQ,EAAM,IAAI;IACvB;IACA;IACA,OAAO,EAAM,IAAI;GACnB,CAAC;GACD;EACF;EACA,IAAI,EAAM,cAAc,CAAC,EAAM,IAAI,OAAO;GAOxC,AADA,EAAM,aAAa,IACnB,KAAK,QAAQ,CAAK;GAClB;EACF;EACA,IAAI,MAAS,UAAU;GACrB,KAAK,QAAQ,CAAK;GAClB;EACF;EAUA,AANI,EAAM,IAAI,SACZ,KAAK,QAAQ,KAAK,yFAAyF;GACzG,KAAK,EAAQ,EAAM,IAAI;GACvB;EACF,CAAC,GAEH,KAAK,UAAU,GAAO,GAAQ,GAAQ,CAAS;CACjD;CAkBA,QAAgB,GAAyD;EACvE,IAAI,IAAa,KAAK,OAAO,KAAK;EAQlC,AAPA,EAAM,IAAI,QAAQ,GAAO,GAAQ,MAAc;GAC7C,IAAI;IACF,IAAa,KAAK,OAAO,KAAK,GAAY,GAAO;KAAE;KAAQ;IAAU,CAAC;GACxE,SAAS,GAAO;IACd,KAAK,QAAQ,MAAM,8BAA8B;KAAE,KAAK,EAAQ,EAAM,IAAI;KAAG;KAAW,KAAK;IAAM,CAAC;GACtG;EACF,CAAC,GACD,EAAM,KAAK,aAAa;CAC1B;CAcA,gBAAwB,GAAmD,GAAqC;EAC1G,MAAc,KAAA,MACd,IAAY,EAAM,mBAAgB,EAAM,iBAAiB,IACzD,IAAY,KAAK,WACnB,KAAK,SAAS,GACd,KAAK,iBAAiB;CAE1B;CAYA,iBAAyB,GAAyD;EAChF,IAAM,IAAO,EAAM;EACf,EAAK,SAAS,UACd,EAAM,IAAI,SAAS,EAAM,eACxB,EAAM,iBACP,EAAK,MAAM,WAAW,YAAY,EAAK,MAAM,WAAW,gBAC5D,EAAM,cAAc,IACpB,KAAK,YAAY,KAAK,EAAK,KAAK;CAClC;CAWA,mBAAiC;EAC/B,OAAO,KAAK,YAAY,SAAS,IAAG;GAClC,IAAM,IAAM,KAAK,YAAY,IACvB,IAAQ,MAAQ,KAAA,IAAY,KAAA,IAAY,KAAK,WAAW,IAAI,CAAG;GACrE,IAAI,CAAC,KAAS,EAAM,IAAI,OAAO;IAC7B,KAAK,YAAY,MAAM;IACvB;GACF;GACA,IAAI,EAAM,iBAAA,QAAsC,KAAK,QAAQ;GAO7D,AANA,KAAK,YAAY,MAAM,GACvB,EAAM,cAAc,IAIpB,EAAM,IAAI,MAAM,GAChB,KAAK,QAAQ,MAAM,yEAAyE;IAC1F;IACA,gBAAgB,EAAM;GACxB,CAAC;EACH;CACF;CAMA,kBAA0B,GAAmC,GAAwB;EACnF,GAAY,KAAK,cAAc,GAAe,CAAQ;CACxD;CAEA,uBAA+B,GAAmC,GAAwB;EACxF,GAAiB,KAAK,cAAc,GAAe,CAAQ;CAC7D;CAYA,aAAqB,GAAyD;EAC5E,IAAM,IAAuB,EAAK;EAClC,OAAO,MAAyB,KAAA,IAAY,KAAA,IAAY,KAAK,yBAAyB,IAAI,CAAoB;CAChH;CAaA,gBAAwB,GAAsD;EAC5E,IAAI,IAAU,GACR,IAAU,IAAI,IAAY,CAAC,EAAQ,CAAO,CAAC,CAAC;EAClD,OAAO,EAAQ,WAAW,KAAA,KACpB,GAAQ,IAAI,EAAQ,MAAM,IADK;GAEnC,IAAM,IAAa,KAAK,WAAW,IAAI,EAAQ,MAAM;GACrD,IAAI,GAAY,KAAK,SAAS,WAAW,EAAW,KAAK,yBAAyB,EAAQ,sBACxF;GAGF,AADA,IAAU,EAAW,MACrB,EAAQ,IAAI,EAAQ,CAAO,CAAC;EAC9B;EACA,OAAO;CACT;CAiBA,iBAAyB,GAA2D;EAClF,AAAI,KAAK,yBAAyB,KAAK,uBACrC,KAAK,cAAc,MAAM,GACzB,KAAK,uBAAuB,KAAK;EAEnC,IAAM,IAAS,KAAK,cAAc,IAAI,CAAG;EACzC,IAAI,GAAQ,OAAO;EAEnB,IAAM,IAAQ,KAAK,WAAW,IAAI,CAAG;EACrC,IAAI,CAAC,GAAO,OAAO,CAAC;EAKpB,IAAI,IAAW,EAAM;EACrB,AAAI,EAAS,SAAS,YACpB,IAAW,KAAK,gBAAgB,CAAQ;EAO1C,IAAM,IAAY,EAAS,sBACrB,IAAyD,CAAC,GAC1D,IAAgB,KAAK,aAAa,IAAI,CAAS;EACrD,IAAI,GACF,KAAK,IAAM,KAAY,GAAe;GACpC,IAAM,IAAa,KAAK,WAAW,IAAI,CAAQ;GAC/C,AAAI,KAAc,KAAK,aAAa,EAAW,MAAM,CAAQ,KAC3D,EAAS,KAAK,CAAU;EAE5B;EAGF,EAAS,MAAM,GAAG,MAAM,KAAK,cAAc,GAAG,CAAC,CAAC;EAIhD,KAAK,IAAM,KAAO,GAChB,KAAK,cAAc,IAAI,EAAQ,EAAI,IAAI,GAAG,CAAQ;EAGpD,OADA,KAAK,cAAc,IAAI,GAAK,CAAQ,GAC7B;CACT;CAWA,aAAqB,GAAqC,GAAkD;EAE1G,IADI,EAAK,SAAS,EAAS,QACvB,EAAK,yBAAyB,EAAS,sBAAsB,OAAO;EAExE,IAAI,EAAK,SAAS,OAAO,OAAO;EAEhC,IAAM,IAAc,EAAQ,CAAQ;EACpC,IAAI,EAAQ,CAAI,MAAM,GAAa,OAAO;EAC1C,IAAI,IAAyC,GACvC,IAAU,IAAI,IAAY,CAAC,EAAQ,CAAO,CAAC,CAAC;EAClD,OAAO,EAAQ,SAAS,WAAW,EAAQ,WAAW,KAAA,IAAW;GAC/D,IAAI,EAAQ,WAAW,GAAa,OAAO;GAC3C,IAAI,EAAQ,IAAI,EAAQ,MAAM,GAAG;GACjC,IAAM,IAAS,KAAK,WAAW,IAAI,EAAQ,MAAM;GACjD,IAAI,CAAC,GAAQ;GAEb,AADA,IAAU,EAAO,MACjB,EAAQ,IAAI,EAAQ,CAAO,CAAC;EAC9B;EACA,OAAO;CACT;CAUA,aAAa,GAAqB;EAChC,IAAM,IAAQ,KAAK,WAAW,IAAI,CAAG;EACrC,IAAI,CAAC,GAAO,OAAO;EAEnB,IAAI,EAAM,KAAK,SAAS,SACtB,OAAO,EAAQ,KAAK,gBAAgB,EAAM,IAAI,CAAC;EAKjD,IAAM,IADQ,KAAK,iBAAiB,CACvB,EAAM,IAAI;EACvB,OAAO,IAAO,EAAQ,CAAI,IAAI;CAChC;CAeA,aAAa,oBAAkC,IAAI,IAAoB,GAAoC;EACzG,KAAK,QAAQ,MAAM,6BAA6B;EAChD,IAAM,IAA0C,CAAC,GAC3C,oBAAc,IAAI,IAAY,GAC9B,oBAAiB,IAAI,IAAoB;EAE/C,KAAK,IAAM,KAAY,KAAK,cAAc;GACxC,IAAM,IAAO,EAAS,MAChB,IAAM,EAAQ,CAAI,GAIlB,IAAY,KAAK,aAAa,CAAI;GACxC,IAAI,MAAc,KAAA,KAAa,CAAC,EAAY,IAAI,CAAS,GACvD;GAIF,IAAM,IAAQ,KAAK,iBAAiB,CAAG;GACvC,IAAI,EAAM,SAAS,GAAG;IACpB,IAAM,IAAe,KAAK,aAAa,CAAG,GACtC,IAAc,EAAe,IAAI,CAAY;IACjD,IAAI,MAAgB,KAAA,GAAW;KAC7B,IAAM,IAAe,EAAW,IAAI,CAAY;KAChD,IAAI,MAAiB,KAAA,KAAa,EAAM,MAAM,MAAM,EAAQ,EAAE,IAAI,MAAM,CAAY,GAClF,IAAc;UACT;MACL,IAAM,IAAS,EAAM,GAAG,EAAE;MAC1B,IAAI,CAAC,GAAQ;MACb,IAAc,EAAQ,EAAO,IAAI;KACnC;KACA,EAAe,IAAI,GAAc,CAAW;IAC9C;IACA,IAAI,MAAQ,GACV;GAEJ;GAGA,AADA,EAAY,IAAI,CAAG,GACnB,EAAO,KAAK,CAAI;EAClB;EAEA,OAAO;CACT;CAEA,WAAW,GAAiD;EAC1D,KAAK,QAAQ,MAAM,6BAA6B,EAAE,SAAM,CAAC;EACzD,IAAM,IAAO,KAAK,WAAW,IAAI,CAAK,GAAG;EACzC,OAAO,GAAM,SAAS,QAAQ,IAAO,KAAA;CACvC;CAEA,QAAQ,GAAwD;EAE9D,OADA,KAAK,QAAQ,MAAM,0BAA0B,EAAE,OAAI,CAAC,GAC7C,KAAK,WAAW,IAAI,CAAG,GAAG;CACnC;CAEA,wBAAwB,GAAmE;EACzF,KAAK,QAAQ,MAAM,0CAA0C,EAAE,kBAAe,CAAC;EAC/E,IAAM,IAAM,KAAK,yBAAyB,IAAI,CAAc;EAC5D,OAAO,MAAQ,KAAA,IAAY,KAAA,IAAY,KAAK,WAAW,IAAI,CAAG,GAAG;CACnE;CAEA,aAAa,GAAqD;EAChE,IAAM,IAAS,KAAK,kBAAkB,IAAI,CAAmB;EAC7D,IAAI,CAAC,GAAQ,OAAO,CAAC;EACrB,IAAM,IAAiC,CAAC;EACxC,KAAK,IAAM,KAAS,GAAQ;GAC1B,IAAM,IAAO,KAAK,WAAW,IAAI,CAAK,GAAG;GACzC,AAAI,GAAM,SAAS,SAAO,EAAO,KAAK,CAAI;EAC5C;EACA,OAAO;CACT;CAEA,gBAAgB,GAA8C;EAE5D,OADA,KAAK,QAAQ,MAAM,kCAAkC,EAAE,OAAI,CAAC,GACrD,KAAK,iBAAiB,CAAG,EAAE,KAAK,MAAM,EAAE,IAAI;CACrD;CAMA,aACE,GACA,GACA,GACA,GACA,GACM;EACN,IAAM,IAAY,EAAQ,IACpB,IAAiB,EAAQ,IAOzB,IACJ,MAAc,KAAA,KACd,MAAmB,KAAA,KACnB,EAAA,SAAyB,UACzB,EAAO,OAAO,SAAS,IACnB,IACA,KAAA;EAEN,IAAI,MAAc,KAAA,KAAa,MAA4B,KAAA,GAAW;GACpE,KAAK,QAAQ,KAAK,8EAA8E;GAChG;EACF;EAGA,IAAM,IAAqC,GAAc,CAAM,GAOzD,IAAc,KAA2B;EAC/C,IAAI,EAAI,WAAW,KAAK,MAAgB,KAAA,KAAa,CAAC,KAAK,WAAW,IAAI,CAAW,GACnF;EAOF,IAAM,IAAmB,KAAK;EAQ9B,AANI,MAA4B,KAAA,IAErB,MAAc,KAAA,KACvB,KAAK,iBAAiB,GAAW,GAAQ,GAAS,GAAQ,GAAW,CAAO,IAF5E,KAAK,mBAAmB,GAAyB,GAAS,GAAQ,GAAW,GAAS,CAAG,GAKvF,KAAK,uBAAuB,KAAkB,KAAK,SAAS,KAAK,QAAQ;CAC/E;CAcA,mBACE,GACA,GACA,GACA,GACA,GACA,GACM;EACN,IAAI,IAAQ,KAAK,WAAW,IAAI,CAAc;EAsB9C,AArBK,IAKM,EAAM,KAAK,SAAS,WAAW,KAAU,CAAC,EAAM,KAAK,WAE9D,KAAK,QAAQ,MAAM,+CAA+C;GAAE;GAAgB;EAAO,CAAC,GAC5F,EAAM,KAAK,SAAS,GACpB,KAAK,eAAe,CAAK,MARzB,IAAQ,KAAK,4BAA4B,GAAgB,GAAS,CAAM,GACxE,KAAK,YAAY,GAAgB,GAAO,EAAM,KAAK,oBAAoB,GACvE,KAAK,yBAAyB,IAAI,GAAgB,CAAc,GAChE,KAAK,QAAQ,MAAM,2CAA2C,EAAE,kBAAe,CAAC,IAQlF,KAAK,gBAAgB,GAAO,CAAS,GAIrC,KAAK,eAAe,GAAO,GAAK,GAAQ,GAAgB,GAAS,EAAQ,OAAmB,MAAM,GAKlG,KAAK,SAAS,KAAK,UAAU;GAC3B,OAAO,KAAA;GACP,qBAAqB;GACrB;GACA;GACA,QAAQ,CAAC;EACX,CAAC;CACH;CAkBA,iBACE,GACA,GACA,GACA,GACA,GACA,GACM;EACN,IAAM,IAAiB,EAAQ,IAGzB,IAAsB,EAAQ,IAE9B,IAAqC,GAAc,CAAM,GACzD,IAAU,EAAO,SAEnB,IAAM,KAAK,WAAW,IAAI,CAAS;EAKvC,IAAI,CAAC,KAAO,MAAmB,KAAA,GAAW;GACxC,IAAM,IAAa,KAAK,yBAAyB,IAAI,CAAc,GAC7D,IAAU,MAAe,KAAA,IAAY,KAAA,IAAY,KAAK,WAAW,IAAI,CAAU;GACrF,AAAI,GAAS,KAAK,SAAS,SAAS,EAAQ,KAAK,gBAAgB,KAAA,MAAW,IAAM;EACpF;EAEA,AAAK,IAKM,KAAU,EAAI,KAAK,SAAS,SAAS,CAAC,EAAI,KAAK,gBAExD,KAAK,QAAQ,MAAM,8CAA8C;GAAE,OAAO;GAAW;EAAO,CAAC,GAC7F,EAAI,KAAK,cAAc,GACvB,KAAK,eAAe,CAAG,MARvB,IAAM,KAAK,sBAAsB,GAAW,GAAS,CAAM,GAC3D,KAAK,YAAY,GAAW,GAAK,EAAI,KAAK,oBAAoB,GAC9D,KAAK,eAAe,EAAI,MAAM,CAAS,GACvC,KAAK,QAAQ,MAAM,wCAAwC,EAAE,OAAO,EAAU,CAAC;EASjF,IAAM,IAAW,EAAQ,EAAI,IAAI;EAWjC,AAVI,KAAgB,KAAK,yBAAyB,IAAI,GAAgB,CAAQ,GAE9E,KAAK,gBAAgB,GAAK,CAAS,GAMnC,KAAK,eAAe,GAAK,GAAK,GAAQ,GAAgB,GAAS,EAAQ,OAAmB,MAAM,GAEhG,KAAK,SAAS,KAAK,UAAU;GAAE,OAAO;GAAU;GAAqB;GAAgB;GAAQ,QAAQ;EAAQ,CAAC;CAChH;CAUA,eAAuB,GAAqC,GAAqB;EAC3E,EAAK,yBAAyB,KAAA,KAClC,GAAY,KAAK,mBAAmB,EAAK,sBAAsB,CAAK;CACtE;CAEA,kBAAkB,GAAgC;EAChD,KAAK,QAAQ,MAAM,oCAAoC;GAAE,MAAM,EAAM;GAAM,OAAO,EAAM;EAAM,CAAC;EAM/F,IAAM,IAAmB,KAAK;EAC9B,QAAQ,EAAM,MAAd;GACE,KAAK;IACH,KAAK,eAAe,CAAK;IACzB;GAEF,KAAK;IACH,KAAK,iBAAiB,CAAK;IAC3B;GAEF,KAAK;IACH,KAAK,gBAAgB,CAAK;IAC1B;GAEF,KAAK;IACH,KAAK,aAAa,CAAK;IACvB;EAEJ;EAEA,AADA,KAAK,SAAS,KAAK,OAAO,CAAK,GAC3B,KAAK,uBAAuB,KAAkB,KAAK,SAAS,KAAK,QAAQ;CAC/E;CAUA,eAAuB,GAAoD;EACzE,IAAM,IAAW,KAAK,WAAW,IAAI,EAAM,KAAK;EAChD,IAAI,GAAU,KAAK,SAAS,OAAO;GACjC,IAAM,IAAO,EAAS;GA4BtB,IAvBI,EAAK,MAAM,WAAW,gBACxB,EAAK,QAAQ,EAAE,QAAQ,SAAS,IAE9B,EAAM,UAAU,CAAC,EAAK,gBACxB,EAAK,cAAc,EAAM,QACzB,KAAK,eAAe,CAAQ,IAW1B,EAAK,yBAAyB,KAAA,KAAa,EAAM,WAAW,KAAA,MAC9D,EAAK,uBAAuB,EAAM,QAClC,KAAK,uBAAuB,KAAA,GAAW,EAAM,KAAK,GAClD,KAAK,kBAAkB,EAAK,sBAAsB,EAAM,KAAK,GAC7D,KAAK,eAAe,GAAM,EAAM,KAAK,GACrC,KAAK,uBAEH,EAAK,WAAW,KAAA,KAAa,EAAM,WAAW,KAAA,GAAW;IAC3D,IAAM,IAAY,KAAK,yBAAyB,IAAI,EAAM,MAAM;IAChE,AAAI,MAAc,KAAA,KAAa,MAAc,EAAM,UACjD,EAAK,SAAS,GACd,KAAK;GAET;GAoBA,AAnBI,EAAK,8BAA8B,KAAA,KAAa,EAAM,gBAAgB,KAAA,MACxE,EAAK,4BAA4B,EAAM,aACvC,KAAK,uBAQH,EAAK,iBAAiB,MAAM,EAAM,iBAAiB,OACrD,EAAK,eAAe,EAAM,eAM5B,EAAS,eAAe,IACxB,KAAK,gBAAgB,GAAU,EAAM,SAAS,GAC9C,KAAK,iBAAiB,CAAQ;EAChC,OAAO,IAAI,CAAC,GAAU;GACpB,IAAM,IAAM,KAAK,wBAAwB,CAAK;GAG9C,AAFA,KAAK,YAAY,EAAM,OAAO,GAAK,EAAI,KAAK,oBAAoB,GAChE,KAAK,eAAe,EAAI,MAAM,EAAM,KAAK,GACzC,KAAK,gBAAgB,GAAK,EAAM,SAAS;EAC3C;CACF;CAUA,iBAAyB,GAAsD;EAC7E,IAAM,IAAM,KAAK,WAAW,IAAI,EAAM,KAAK;EAC3C,AAAI,GAAK,KAAK,SAAS,UACrB,EAAI,KAAK,QAAQ,EAAE,QAAQ,YAAY,GACvC,EAAI,KAAK,YAAY,EAAM,QAC3B,KAAK,gBAAgB,GAAK,EAAM,SAAS;CAE7C;CAaA,gBAAwB,GAAqD;EAC3E,IAAM,IAAM,KAAK,WAAW,IAAI,EAAM,KAAK;EAC3C,AAAI,GAAK,KAAK,SAAS,SAAS,EAAI,KAAK,MAAM,WAAW,gBACxD,EAAI,KAAK,QAAQ,EAAE,QAAQ,SAAS,GACpC,KAAK,gBAAgB,GAAK,EAAM,SAAS;CAE7C;CAgBA,aAAqB,GAAkD;EACrE,IAAM,IAAM,KAAK,WAAW,IAAI,EAAM,KAAK;EAC3C,AAAI,GAAK,KAAK,SAAS,UACrB,EAAI,KAAK,QAAQ,EAAM,WAAW,UAAU;GAAE,QAAQ;GAAS,OAAO,EAAM;EAAM,IAAI,EAAE,QAAQ,EAAM,OAAO,GAC7G,EAAI,KAAK,YAAY,EAAM,QAC3B,KAAK,gBAAgB,GAAK,EAAM,SAAS,GACzC,KAAK,iBAAiB,CAAG;CAE7B;CAEA,OAAO,GAAmB;EACxB,IAAM,IAAQ,KAAK,WAAW,IAAI,CAAG;EAChC,MAEL,KAAK,QAAQ,MAAM,kBAAkB,EAAE,OAAI,CAAC,GAE5C,KAAK,uBAAuB,EAAM,KAAK,sBAAsB,CAAG,GAChE,KAAK,kBAAkB,CAAK,GAC5B,KAAK,WAAW,OAAO,CAAG,GAEtB,EAAM,KAAK,SAAS,SAAS,EAAM,KAAK,yBAAyB,KAAA,KACnE,GAAiB,KAAK,mBAAmB,EAAM,KAAK,sBAAsB,CAAG,GAM/E,KAAK,sBACL,KAAK,SAAS,KAAK,QAAQ;CAC7B;CAcA,sBACE,GACA,GACA,GAC4C;EAC5C,IAAM,IAAc,EAAQ;EAC5B,OAAO,KAAK,cAAc;GACxB;GACA,sBAAsB,EAAQ;GAG9B,QAAQ,IAAc,KAAK,yBAAyB,IAAI,CAAW,IAAI,KAAA;GACvE,2BAA2B,EAAQ;GACnC,UAAU,EAAA,oBAAiC;GAC3C,cAAc,EAAA,oBAAiC;GAC/C,aAAa;GAGb,cAAc;EAChB,CAAC;CACH;CAYA,UACE,GACA,IAAe,IAC6B;EAC5C,OAAO;GACL;GACA,WAAW,KAAK;GAChB,KAAK,IAAI,GAAqC;GAC9C,gBAAgB;GAChB;GACA,aAAa;GACb,YAAY;EACd;CACF;CAiBA,cAAsB,GASyB;EAC7C,IAAM,IAA6B;GACjC,MAAM;GACN,OAAO,EAAO;GACd,sBAAsB,EAAO;GAC7B,QAAQ,EAAO;GACf,2BAA2B,EAAO;GAClC,UAAU,EAAO;GACjB,cAAc,EAAO;GACrB,OAAO,EAAE,QAAQ,SAAS;GAC1B,YAAY,KAAK,OAAO,KAAK;GAC7B,aAAa,EAAO;GACpB,WAAW,KAAA;EACb;EAEA,OAAO,KAAK,UAAU,GAAM,EAAO,YAAY;CACjD;CASA,4BACE,GACA,GACA,GAC4C;EAC5C,IAAM,IAAc,EAAQ,IACtB,IAA+B;GACnC,MAAM;GACN;GACA,sBAAsB,EAAQ;GAG9B,QAAQ;GACR,YAAY,KAAK,OAAO,KAAK;GAC7B;EACF;EACA,OAAO,KAAK,UAAU,CAAI;CAC5B;CASA,wBACE,GAC4C;EAC5C,IAAM,IAAc,EAAM;EAC1B,OAAO,KAAK,cAAc;GACxB,OAAO,EAAM;GACb,sBAAsB,EAAM;GAC5B,QAAQ,IAAc,KAAK,yBAAyB,IAAI,CAAW,IAAI,KAAA;GACvE,2BAA2B,EAAM;GACjC,UAAU,EAAM;GAChB,cAAc,EAAM;GACpB,aAAa,EAAM;GAEnB,cAAc;EAChB,CAAC;CACH;CAWA,GACE,GACA,GAKY;EAEZ,IAAM,IAAK;EAEX,OADA,KAAK,SAAS,GAAG,GAAO,CAAE,SACb;GACX,KAAK,SAAS,IAAI,GAAO,CAAE;EAC7B;CACF;CAQA,gBAAgB,GAAgC;EAC9C,KAAK,QAAQ,MAAM,gCAAgC;EAEnD,IAAM,IADU,EAAoB,CACpB,EAAQ;EAIxB,AAHI,MAAY,KAAA,KAAa,CAAC,KAAK,cAAc,IAAI,CAAO,KAC1D,KAAK,cAAc,IAAI,GAAS,CAAG,GAErC,KAAK,SAAS,KAAK,gBAAgB,CAAG;CACxC;CAEA,yBAAyB,GAAkD;EACzE,OAAO,KAAK,cAAc,IAAI,CAAO;CACvC;AACF,GAea,MACX,GACA,MAC8C,IAAI,GAA0C,GAAO,CAAM,GC/7CrG,KAAwB,KAqCxB,KAAN,MAK0D;CA+DxD,YAAY,GAAsE;EAChF,uCA1DiC,IAAI,IAA2B,sDASnB,IAAI,IAAoB,2CAWnC,IAAI,IAAiC,0BAsCvE,KAAK,SAAS,EAAQ;EAKtB,IAAM,IAAsC,EAAE,GADtB,GAAc,EAAQ,QAAQ,EAAQ,KACb,EAAgB,GAE3D,IAAQ,GAAoB,EAAQ,YAAY;EAgCtD,AA/BI,MAAO,EAAe,QAAQ,IAClC,KAAK,WAAW,EAAQ,OAAO,SAAS,IAAI,EAAQ,aAAa,CAAc,GAC/E,KAAK,UAAU,EAAQ,QAAQ,YAAY,EAAE,WAAW,eAAe,CAAC,GACxE,KAAK,WAAW,EAAQ,SACxB,KAAK,cAAc,GAAiB,KAAK,UAAU,KAAK,OAAO,GAC/D,KAAK,6BAA6B,EAAQ,6BAA6B,KACvE,KAAK,wBAAwB,EAAQ,wBAAwB,MAC7D,KAAK,QAAQ,GACX,KAAK,QACL,KAAK,WAAW,GAAW,EAAE,UAAU,GAAS,OAAO,CAAC,CAC1D,GACA,KAAK,WAAW,GAAkB,KAAK,OAAO,KAAK,OAAO,cAAc,CAAC,GACzE,KAAK,aAAa,KAAK,iBAAiB,GAExC,KAAK,oBAAoB,MAA6B;GACpD,KAAK,sBAAsB,CAAG;EAChC,GASA,KAAK,mBAAmB,KAAK,SAAS,UAAU,YAChD,KAAK,yBAAyB,MAAyC;GACrE,KAAK,0BAA0B,CAAW;EAC5C,GACA,KAAK,SAAS,GAAG,KAAK,qBAAqB,GAE3C,KAAK,SAAS,MAAM,wCAAwC;CAC9D;CASA,mBAA8E;EAC5E,OAAO,GAAwD;GAC7D,MAAM,KAAK;GACX,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,sBAAsB,KAAK;EAC7B,CAAC;CACH;CAOA,IAAI,WAAkC;EACpC,OAAO,KAAK,SAAS;CACvB;CAGA,IAAI,SAAqC;EACvC,OAAO,KAAK,SAAS;CACvB;CAQA,UAAyB;EA2BvB,OA1BI,KAAK,WAAA,WACA,QAAQ,OAAO,IAAI,EAAK,UAAU,wCAAwC,EAAU,eAAe,GAAG,CAAC,IAE5G,KAAK,kBAAwB,KAAK,mBAEtC,KAAK,SAAS,MAAM,gCAAgC,GAKpD,KAAK,kBAAkB,KAAK,SAAS,UAAU,KAAK,gBAAgB,EAAE,WAC9D;GACJ,KAAK,SAAS,MAAM,wDAAwD;EAC9E,IACC,MAAmB;GAClB,IAAM,IAAU,IAAI,EAAK,UACvB,mCAAmC,EAAa,CAAK,KACrD,EAAU,0BACV,KACA,EAAW,CAAK,CAClB;GAGA,MAFA,KAAK,SAAS,MAAM,iDAAiD,GACrE,KAAK,WAAW,CAAO,GACjB;EACR,CACF,GACO,KAAK;CACd;CAOA,IAAI,OAAmC;EACrC,OAAO,KAAK;CACd;CAGA,UAAU,GAAwB,GAAoE;EAEpG,OADA,KAAK,SAAS,MAAM,oCAAoC,EAAE,cAAc,EAAW,aAAa,CAAC,GAC1F,KAAK,WAAW,GAAY,KAAW,CAAC,CAAC;CAClD;CAGA,MAAM,QAAuB;EACvB,SAAK,WAAA,UAMT;GALA,KAAK,SAAA,UACL,KAAK,SAAS,MAAM,8BAA8B,GAC9C,KAAK,mBACP,KAAK,SAAS,YAAY,KAAK,gBAAgB,GAEjD,KAAK,SAAS,IAAI,KAAK,qBAAqB;GAC5C,KAAK,IAAM,KAAO,KAAK,gBAAgB,OAAO,GAC5C,EAAI,WAAW,MAAM;GASvB,AAPA,KAAK,gBAAgB,MAAM,GAC3B,KAAK,4BAA4B,MAAM,GACvC,KAAK,iBAAiB,MAAM,GAC5B,KAAK,YAAY,MAAM,GAEvB,MAAM,GAAiB,KAAK,UAAU,KAAK,iBAAiB,KAAK,SAAS,qBAAqB,GAE/F,KAAK,SAAS,MAAM,6CAA6C;EAXrB;CAY9C;CAMA,MAAc,qBAAqB,GAAyC;EAC1E,IAAM,IAAU,EAAoB,CAAG,GACjC,IAAQ,EAAQ,IAChB,IAAsB,EAAQ;EAMpC,IAAI,CAAC,KAAS,CAAC,GAAqB;GAClC,KAAK,SAAS,KAAK,yFAAyF,EAC1G,QAAQ,EAAI,OACd,CAAC;GACD;EACF;EAMA,IAAM,IACJ,MAAU,IAAsB,KAAK,4BAA4B,IAAI,CAAmB,IAAI,KAAA,IACxF,IAAM,IAAgB,KAAK,gBAAgB,IAAI,CAAa,IAAI,KAAA;EAEtE,IAAI,CAAC,GAAK;GAOR,AAAI,MAAwB,KAAA,KAC1B,KAAK,sBAAsB,GAAqB,CAAG;GAErD;EACF;EAEA,MAAM,KAAK,oBAAoB,GAAK,CAAG;CACzC;CAUA,sBAA8B,GAA6B,GAAgC;EACzF,IAAM,IAAU,GAAkB,KAAK,kBAAkB,GAAqB,EAAqB;EAQnG,AAPI,MAAY,KAAA,KACd,KAAK,SAAS,KAAK,6FAA6F;GAC9G,4BAA4B;GAC5B,OAAO;EACT,CAAC,GAEH,KAAK,iBAAiB,IAAI,GAAqB,CAAG,GAClD,KAAK,SAAS,MAAM,sEAAsE;GACxF;GACA,QAAQ,EAAI;EACd,CAAC;CACH;CAWA,MAAc,oBAAoB,GAAoB,GAA4C;EAChG,IAAM,IAAW,KAAK,iBAAiB,IAAI,CAAmB;EAC1D,MAAa,KAAA,MACjB,KAAK,iBAAiB,OAAO,CAAmB,GAChD,KAAK,SAAS,MAAM,wEAAwE;GAC1F,OAAO,EAAI;GACX;EACF,CAAC,GACD,MAAM,KAAK,oBAAoB,GAAK,CAAQ;CAC9C;CAUA,MAAc,oBAAoB,GAAoB,GAAyC;EAC7F,IAAM,EAAE,aAAU;EAClB,KAAK,SAAS,MAAM,0DAA0D,EAAE,SAAM,CAAC;EAEvF,IAAM,IAAyB;GAAE,SAAS;GAAK;EAAM;EAErD,IAAI;GACF,IAAI,EAAI,YAEF,CAAC,MADiB,EAAI,SAAS,CAAO,GAC5B;IACZ,KAAK,SAAS,MAAM,0EAA0E,EAC5F,SACF,CAAC;IACD;GACF;GAGF,AADA,EAAI,WAAW,MAAM,GACrB,KAAK,SAAS,MAAM,4DAA4D,EAAE,SAAM,CAAC;EAC3F,SAAS,GAAO;GACd,IAAM,IAAU,IAAI,EAAK,UACvB,oCAAoC,EAAM,4BAA4B,EAAa,CAAK,KACxF,EAAU,qBACV,KACA,EAAW,CAAK,CAClB;GAEA,AADA,KAAK,SAAS,MAAM,6DAA6D,EAAE,SAAM,CAAC,IACzF,EAAI,WAAW,KAAK,YAAY,CAAO;EAC1C;CACF;CAOA,0BAAkC,GAA4C;EAC5E,IAAI,KAAK,WAAA,UAAgC;EAEzC,IAAM,EAAE,YAAS,eAAY;EAG7B,IAAI,MAAY,cAAc,CAAC,KAAK,kBAAkB;GACpD,KAAK,mBAAmB;GACxB;EACF;EAEA,IAAI,CAAC,GAAiB,CAAW,GAAG;EAEpC,KAAK,SAAS,MAAM,4EAA4E;GAC9F;GACA;GACA,UAAU,EAAY;EACxB,CAAC;EAED,IAAM,IAAgB,GAAoB,GAAa,UAAU;EAMjE,KAAK,IAAM,KAAO,KAAK,gBAAgB,OAAO,GAC5C,EAAI,WAAW,MAAM;EAmBvB,AAZA,KAAK,QAAQ,GACX,KAAK,QACL,KAAK,WAAW,GAAW,EAAE,UAAU,GAAS,OAAO,CAAC,CAC1D,GACA,KAAK,WAAW,GAAkB,KAAK,OAAO,KAAK,OAAO,cAAc,CAAC,GAGzE,KAAK,aAAa,KAAK,iBAAiB,GAKxC,KAAK,WAAW,CAAa;CAC/B;CAuBA,UAAkB,GAAiC;EAEjD,AADA,KAAK,SAAS,MAAM,CAAI,GACxB,KAAK,MAAM,gBAAgB,CAAI;CACjC;CAMA,sBAA8B,GAAgC;EAC5D,IAAI;GAMF,IAFA,KAAK,UAAU,CAAG,GAEd,EAAI,SAAA,aAAuB;IAE7B,KAAK,qBAAqB,CAAG,EAAE,OAAO,MAAmB;KACvD,IAAM,IAAU,IAAI,EAAK,UACvB,mCAAmC,EAAa,CAAK,KACrD,EAAU,qBACV,KACA,EAAW,CAAK,CAClB;KAEA,AADA,KAAK,SAAS,MAAM,mEAAmE,GACvF,KAAK,WAAW,CAAO;IACzB,CAAC;IACD;GACF;EACF,SAAS,GAAO;GACd,IAAM,IAAU,IAAI,EAAK,UACvB,sCAAsC,EAAa,CAAK,KACxD,EAAU,0BACV,KACA,EAAW,CAAK,CAClB;GAEA,AADA,KAAK,SAAS,MAAM,iEAAiE,GACrF,KAAK,WAAW,CAAO;EACzB;CACF;CAMA,MAAc,kBAAkB,GAA+B;EAC7D,OAAO,GAAiB,KAAK,iBAAiB,CAAM;CACtD;CAMA,WAAmB,GAAwB,GAAmE;EAO5G,IAAI,IAAQ,EAAQ,SAAS,OAAO,WAAW,GAMzC,IAAkB,EAAQ,UAAU,KAAA,GAIpC,IAAe,EAAQ,gBAAgB,OAAO,WAAW,GACzD,IAA4B,KAAK,4BACjC,EAAE,cAAW,gBAAa,aAAU,SAAS,GAAY,QAAQ,MAAmB,GAEpF,IAAa,IAAI,gBAAgB,GACnC,IAAA,eAKE,IAAS,IAAiB,YAAY,IAAI,CAAC,EAAW,QAAQ,CAAc,CAAC,IAAI,EAAW,QAM5F,IAA8B;GAClC;GACA;GACA;GACA;GACA;GACA,SAAS;EACX;EACA,KAAK,gBAAgB,IAAI,GAAO,CAAY;EAI5C,IAAM,IAAS,KAAK,SACd,IAAa,KAAK,aAClB,IAAQ,KAAK,QACb,KAAU,KAAK,UACf,IAAiB,KAAK,iBACtB,IAA6B,KAAK,6BAClC,IAAkB,KAAK,kBACvB,IAAmB,KAAK,kBAAkB,KAAK,IAAI,GAGnD,UAAwE,KAAK,YAC7E,KAAqB,KAAK,oBAAoB,KAAK,IAAI,GACvD,IAAe,EAAW,cAQ5B,IACA,GACA,GACA,GACA,GACA,GACA,KAAuB,IACvB,IAcE,WAA2D,KAAK,OAChE,IAA0B,EAC9B,IAAI,WAAW;GACb,IAAI,MAAgC,KAAA,GAAW,OAAO,CAAC;GACvD,IAAM,IAAO,GAAQ,EAAE,wBAAwB,CAA2B;GAC1E,IAAI,CAAC,GAAM,OAAO,CAAC;GACnB,IAAM,IAAe,EAAK,SAAS,UAAU,EAAK,SAAS,EAAK,aAC1D,IAAe,EAAK,SAAS,UAAU,EAAK,SAAS,KAAA;GAC3D,OAAO,EAAM,YAAY,EAAK,UAAU,EAAE,KAAK,OAAO;IACpD,MAAM;IACN,SAAS,EAAE;IACX,gBAAgB,EAAE;IAClB,UAAU,EAAK;IACf,QAAQ;IACR,SAAS,CAAC;IACV,QAAQ;GACV,EAAE;EACJ,EACF,GASI,GAQE,UAA4B;GAEhC,AADA,EAAe,OAAO,CAAK,GACvB,MAAgC,KAAA,MAClC,EAA2B,OAAO,CAA2B,GAC7D,EAAgB,OAAO,CAA2B;EAEtD,GAWM,KAAmB,OACvB,GACA,GACA,MACkB;GAClB,IAAI;IACF,MAAM,EAAQ;GAChB,SAAS,GAAO;IACd,IAAM,IAAU,IAAI,EAAK,UACvB,qBAAqB,EAAM,WAAW,EAAM,IAAI,EAAa,CAAK,KAClE,EAAU,mBACV,KACA,EAAW,CAAK,CAClB;IAEA,MADA,GAAQ,MAAM,OAAO,EAAO,wBAAwB,KAAS,EAAE,SAAM,CAAC,GAChE;GACR;EACF,GAEM,IAA2C;GAC/C,IAAI,QAAQ;IACV,OAAO;GACT;GACA,IAAI,eAAe;IACjB,OAAO;GACT;GACA,IAAI,cAAc;IAChB,OAAO;GACT;GACA,IAAI,OAAO;IACT,OAAO;GACT;GACA,IAAI,WAAW;IAYb,OAAO,EAAa,EAAE,SAAS,GAAO,GAAyB,CAAmB;GACpF;GAGA,OAAO,YAA2B;IAMhC,IALA,GAAQ,MAAM,gBAAgB;KAAE;KAAO;IAAa,CAAC,GAErD,MAAM,EAAiB,OAAO,GAG1B,EAAO,SACT,MAAM,IAAI,EAAK,UACb,4BAA4B,EAAM,gCAClC,EAAU,iBACV,GACF;IAEF,IAAI,MAAA,eAAgC;IAQpC,IAPA,IAAA,WAOI,KAAgB,IAA4B,GAC9C,IAAI;KACF,IAAM,IAAQ,MAAM,EAAa,EAAE,eAAe;MAChD;MACA;MACA,kBAAkB,CAAC,CAAY;MAC/B,WAAW;MACX;KACF,CAAC;KAED,AADI,EAAM,iBAAiB,KAAA,MAAW,KAAqB,EAAM,eAC7D,EAAM,kBAAkB,KAAA,MAAW,IAAwB,EAAM;IACvE,SAAS,GAAO;KACd,IAAM,IACJ,aAAiB,EAAK,YAClB,IACA,IAAI,EAAK,UACP,kCAAkC,EAAa,CAAK,KACpD,EAAU,oBACV,GACF;KAQN,MAFA,EAAc,GACd,GAAQ,MAAM,0CAA0C;MAAE;MAAO;KAAa,CAAC,GACzE;IACR;IAUF,IAAM,IAAgB;IACtB,IAAI,GAAe;KAKjB,AAJA,KAAmB,EAAc,IACjC,IAAiB,EAAc,IAC/B,IAAiB,EAAc,IAC/B,IAAsB,EAAc,IACpC,IAA8B,EAAc;KAQ5C,IAAM,IAAY,EAAc;KAEhC,AADA,KAAuB,MAAc,KAAA,GACjC,MAAc,KAAA,KAAa,MAAc,MAC3C,EAAe,OAAO,CAAK,GAC3B,IAAQ,GACR,EAAa,QAAQ,GACrB,EAAe,IAAI,GAAO,CAAY;IAE1C;IAgEA,AAxDA,IACE,MAAgC,KAAA,KAChC,KAAK,MAAM,wBAAwB,CAA2B,MAAM,KAAA,IAChE,IACA,GASF,MAAgC,KAAA,MAClC,EAA2B,IAAI,GAA6B,CAAK,GACjE,MAAM,GAAmB,GAAc,CAA2B,IAGpE,MAAM,GAAiB,aAAa,SAAS,YAC3C,EAAW,SAAS,GAAO,IAAkB,GAAY;KAMvD,QAAQ;KACR,QAAQ;KACR,aAAa;KACb;KACA,eAAe;KACf,qBAAqB;KACrB,cAAc;IAChB,CAAC,CACH,GAUK,MACH,GAAQ,EAAE,kBAAkB;KAC1B,MAAM;KACN;KACA,UAAU,MAAoB;KAC9B,QAAQ,KAAA;KACR;KACA,GAAI,MAA4B,KAAA,KAAa,EAAE,QAAQ,EAAwB;KAC/E,GAAI,MAAmB,KAAA,KAAa,EAAE,QAAQ,EAAe;KAC7D,GAAI,MAAwB,KAAA,KAAa,EAAE,aAAa,EAAoB;IAC9E,CAAC,GAGH,GAAQ,MAAM,4BAA4B;KAAE;KAAO;IAAa,CAAC;GACnE;GAEA,kBAAkB,OAAO,MAA2D;IAElF,AADA,GAAQ,MAAM,2BAA2B,EAAE,SAAM,CAAC,GAClD,MAAM,EAAiB,kBAAkB;IAKzC,IAAM,EAAE,gBAAa,MAAM,EAAa,EAAE,iBACxC,GACA,GACA,GACA,GAAS,SACT,KAAmB,IACnB,CACF;IACA,OAAO;GACT;GAGA,MAAM,OAAO,GAAiC,MAA6D;IAKzG,IAJA,GAAQ,MAAM,eAAe,EAAE,SAAM,CAAC,GAEtC,MAAM,EAAiB,MAAM,GAEzB,MAAA,eACF,MAAM,IAAI,EAAK,UACb,oEAAoE,EAAM,IAC1E,EAAU,iBACV,GACF;IAGF,IAAM,IAAmB,EAAW,YAAY,CAAK,GAW/C,IAAkB,GAAY,UAAU,GACxC,IAAkB,GAAY,UAAU,GAOxC,IAAuB,GAEvB,IAAiB,OAAO,WAAW,GACnC,IAAiB,GAAsB;KAC3C,MAAM;KACN;KACA;KACA,aAAa;KACb,QAAQ;KACR,QAAQ;KACR;KACA,eAAe;KACf,qBAAqB;KACrB,aAAa;IACf,CAAC,GAOK,IAAS,MAAM,GAAW,GANhB,EAAM,cAAc,IAAS;KAC3C,QAAQ,EAAE,SAAS,EAAe;KAClC;KACA,WAAW;IACb,CAEwC,GAAS,GAAQ,GAAa,GAAY,qBAAqB,CAAM;IAE7G,IAAI,EAAO,OAAO;KAChB,IAAM,IAAU,IAAI,EAAK,UACvB,mCAAmC,EAAM,IAAI,EAAO,MAAM,WAC1D,EAAU,aACV,KACA,EAAW,EAAO,KAAK,CACzB;KAEA,AADA,GAAQ,MAAM,4BAA4B,EAAE,SAAM,CAAC,GACnD,IAAa,CAAO;IACtB;IAOA,IAAI,EAAO,WAAW,aACpB,IAAI;KACF,MAAM,EAAI,IAAI,EAAE,QAAQ,YAAY,CAAC;IACvC,QAAQ;KACN,GAAQ,MAAM,wCAAwC,EAAE,SAAM,CAAC;IACjE;IAIF,OADA,GAAQ,MAAM,+BAA+B;KAAE;KAAO,QAAQ,EAAO;IAAO,CAAC,GACtE;GACT;GAEA,SAAS,YAA2B;IAKlC,IAJA,GAAQ,MAAM,kBAAkB,EAAE,SAAM,CAAC,GAEzC,MAAM,EAAiB,SAAS,GAE5B,MAAA,eACF,MAAM,IAAI,EAAK,UACb,uEAAuE,EAAM,IAC7E,EAAU,iBACV,GACF;IAIE,UAAA,SACJ;SAAA;KAEA,IAAI;MACF,MAAM,GAAiB,eAAe,WAAW,YAC/C,EAAW,WAAW,GAAO,GAAc,GAAuB,CAA2B,CAC/F;KACF,UAAU;MACR,EAAc;KAChB;KAEA,GAAQ,MAAM,gCAAgC,EAAE,SAAM,CAAC;IAVvD;GAWF;GAGA,KAAK,OAAO,MAAwC;IAClD,IAAM,EAAE,cAAW,GACb,IAAQ,EAAO,WAAW,UAAU,EAAO,QAAQ,KAAA;IAKzD,IAJA,GAAQ,MAAM,cAAc;KAAE;KAAO;IAAO,CAAC,GAE7C,MAAM,EAAiB,KAAK,GAExB,MAAA,eACF,MAAM,IAAI,EAAK,UACb,+DAA+D,EAAM,IACrE,EAAU,iBACV,GACF;IAEE,UAAA,SACJ;SAAA;KAEA,IAAI;MACF,MAAM,GAAiB,WAAW,OAAO,YACvC,EAAW,OAAO,GAAO,GAAQ,GAAc,GAAuB,GAA6B,CAAK,CAC1G;KACF,UAAU;MACR,EAAc;KAChB;KAEA,GAAQ,MAAM,wBAAwB;MAAE;MAAO;KAAO,CAAC;IAVvD;GAWF;EACF;EAEA,OAAO;CACT;AACF,GAaa,MAMX,MACiD,IAAI,GAAoB,CAAO,GC//BrE,KAAb,MAAa,EAAW;CAUtB,YAAoB,GAAsB;EAExC,AADA,KAAK,eAAe,EAAK,cACzB,KAAK,cAAc,EAAK;CAC1B;CAOA,OAAO,SAAS,GAAkC;EAChD,OAAO,IAAI,EAAW,CAAI;CAC5B;CAQA,SAAyB;EACvB,OAAO;GACL,cAAc,KAAK;GACnB,aAAa,KAAK;EACpB;CACF;AACF,GCuBM,MAAuB,GAAqB,MAAsD;CACtG,KAAK,IAAM,KAAO,GAAa;EAC7B,IAAM,IAAU,EAAoB,CAAG,GACjC,IAAiB,EAAQ;EAC/B,IAAI,CAAC,GAAgB;EAErB,IAAM,IAAS,EAAI,QACb,IAAmB,MAAW,oBAAA,cAAuC,GAKrE,IACJ,EAAA,WAA2B,WAC1B,MAAW,oBAAoB,MAAW,oBAAoB,MAAW,mBACtE,IAAS,EAAQ,IACjB,IAAa,MAAW,cAAc,MAAW;EAIvD,CAFI,KAAoB,MAAkB,EAAM,uBAAuB,IAAI,CAAc,IACrF,KAAoB,MAAY,EAAM,0BAA0B,IAAI,CAAc,GAClF,EAAM,uBAAuB,IAAI,CAAc,KAAK,EAAM,0BAA0B,IAAI,CAAc,KACxG,EAAM,yBAAyB,IAAI,CAAc;CAErD;AACF,GAgBM,KAAkB,OAAO,GAAqB,MAAiC;CACnF,IAAM,IAAS,EAAM,gBAAgB;CACrC,OAAO,EAAM,yBAAyB,OAAO,KAAU,EAAM,OAAO,QAAQ,IAAG;EAC7E,EAAM,OAAO,MAAM,oDAAoD;GACrE,WAAW,EAAM,YAAY;GAC7B,WAAW,EAAM,yBAAyB;EAC5C,CAAC;EACD,IAAM,IAAQ,MAAM,EAAM,OAAO,KAAK;EACtC,IAAI,CAAC,GAAO;EAEZ,AADA,EAAM,YAAY,KAAK,GAAG,CAAK,GAC/B,GAAoB,GAAO,CAAK;CAClC;AACF,GAYM,MAAe,GAAqB,MAA+B;CAIvE,IAAM,IAAiB,EAAM,yBAAyB,MAChD,IAAS,KAAK,IAAI,GAAO,KAAK,IAAI,GAAG,IAAiB,EAAM,aAAa,CAAC;CAChF,EAAM,iBAAiB;CAEvB,IAAM,IAAgB,IAAiB,EAAM,eACvC,IAAiB,EAAM,OAAO,QAAQ,GAItC,IADc,EAAM,YAAY,SAAS,EAAM,mBACnB,IAAI,EAAM,YAAY,MAAM,EAAM,gBAAgB,EAAE,WAAW,IAAI,CAAC;CAGtG,OAFA,EAAM,mBAAmB,EAAM,YAAY,QAEpC;EACL;EACA,eAAe,KAAiB;EAChC,MAAM,YAAY;GAChB,IAAI,GACF,OAAO,GAAY,GAAO,CAAK;GAE5B,OAEL,OADA,MAAM,GAAgB,GAAO,CAAK,GAC3B,GAAY,GAAO,CAAK;EACjC;CACF;AACF,GAwBa,KAAc,OACzB,GACA,GACA,MACyB;CACzB,IAAM,IAAQ,GAAS,SAAS;CAEhC,EAAO,MAAM,kBAAkB,EAAE,SAAM,CAAC;CAcxC,IAAM,IAAsB;EAC1B,QAAA,MAPmB,GAAiB,GAAS;GAC7C,WAHgB,IAAQ;GAIxB,aAAa;GACb;EACF,CAAC;EAIC,aAAa,CAAC;EACd,eAAe;EACf,kBAAkB;EAClB,wCAAwB,IAAI,IAAY;EACxC,2CAA2B,IAAI,IAAY;EAC3C,0CAA0B,IAAI,IAAY;EAC1C;CACF;CAGA,OADA,MAAM,GAAgB,GAAO,CAAK,GAC3B,GAAY,GAAO,CAAK;AACjC,GCtEM,MAAkD,MACtD,MAAM,QAAQ,CAAK,IAAI,IAAQ,CAAC,CAAK,GASjC,MAA2B,OAAwC;CACvE,OAAO,EAAI;CACX,UAAU,EAAI;CACd,cAAc,EAAI;CAClB,GAAG,EAAI;AACT,IAMa,KAAb,MAKoC;CA+FlC,YAAY,GAA8D;EAgBxE,yCAjGmC,IAAI,IAAkC,2CAWvC,IAAI,IAA4B,kDAWzB,IAAI,IAA4B,0CAGxC,IAAI,IAAY,+BAGV,CAAC,kCAMO,CAAC,mCAOW,CAAC,iDAG7B,IAAI,IAAY,0BAGvB,2BAM0C,CAAC,8BAWvC,kBAGa,CAAC,uBAQY,CAAC,wBAEjC,8BACK,mBACX,IAGhB,KAAK,QAAQ,EAAQ,MACrB,KAAK,WAAW,EAAQ,SACxB,KAAK,SAAS,EAAQ,OACtB,KAAK,WAAW,EAAQ,SACxB,KAAK,gBAAgB,EAAQ,cAC7B,KAAK,WAAW,EAAQ,SACxB,KAAK,UAAU,EAAQ,OAAO,YAAY,EAAE,WAAW,OAAO,CAAC,GAC/D,KAAK,QAAQ,MAAM,gBAAgB,GACnC,KAAK,WAAW,IAAI,GAA4B,KAAK,OAAO,GAG5D,KAAK,eAAe,KAAK,kBAAkB,GAC3C,KAAK,uBAAuB,KAAK,YAAY,GAG7C,KAAK,QAAQ,KACX,KAAK,MAAM,GAAG,gBAAgB;GAC5B,KAAK,cAAc;EACrB,CAAC,GACD,KAAK,MAAM,GAAG,iBAAiB,MAAQ;GACrC,KAAK,mBAAmB,CAAG;EAC7B,CAAC,GACD,KAAK,MAAM,GAAG,QAAQ,MAAU;GAC9B,KAAK,WAAW,CAAK;EACvB,CAAC,GACD,KAAK,MAAM,GAAG,WAAW,MAAU;GACjC,KAAK,cAAc,CAAK;EAC1B,CAAC,CACH;CACF;CAQA,cAAsB,GAAmC;EACnD,KAAK,uBAKN,EAAM,UAAU,KAAA,KAAa,KAAK,uBAAuB,IAAI,EAAM,KAAK,KACxE,EAAM,wBAAwB,KAAA,KAAa,KAAK,uBAAuB,IAAI,EAAM,mBAAmB,OAYvG,KAAK,0BAA0B,KAAK,aAAa,KAAK,MAAM,EAAE,UAAU,GACxE,KAAK,2BAA2B,KAAK,iBAAiB,KAAK,YAAY,EAAE,MAAM,KAAK,mBAAmB,GACvG,KAAK,SAAS,KAAK,QAAQ;CAC7B;CAMA,cAAwC;EACtC,OAAO,KAAK;CACd;CAEA,OAAkB;EAIhB,OAAO,KAAK,aACT,QAAQ,MAAuC,EAAK,SAAS,KAAK,EAClE,KAAK,MAAS,GAAW,CAAI,CAAC;CACnC;CASA,oBAA6D;EAC3D,IAAM,IAAY,KAAK,kBAAkB;EAEzC,OADI,KAAK,gBAAgB,SAAS,IAAU,IACrC,EAAU,QAAQ,MAAS,CAAC,KAAK,gBAAgB,IAAI,EAAQ,CAAI,CAAC,CAAC;CAC5E;CAOA,oBAAkC;EAGhC,AAFA,KAAK,eAAe,KAAK,kBAAkB,GAC3C,KAAK,uBAAuB,KAAK,YAAY,GAC7C,KAAK,SAAS,KAAK,QAAQ;CAC7B;CAQA,6BAA2C;EACzC,IAAM,IAAQ,KAAK,kBAAkB;EACrC,AAAI,KAAK,gBAAgB,CAAK,MAC5B,KAAK,eAAe,GACpB,KAAK,uBAAuB,CAAK,GACjC,KAAK,SAAS,KAAK,QAAQ;CAE/B;CAUA,qBAA6B,GAA0D;EACrF,IAAM,IAAO,KAAK,MAAM,wBAAwB,CAAc;EAC9D,OAAO,GAAM,SAAS,QAAQ,IAAO,KAAA;CACvC;CAaA,qBACE,GACA,GACwB;EACxB,OAAO,KAAK,MACT,aAAa,CAAyB,EACtC,QAAQ,MAAM,EAAE,8BAA8B,CAAoB,EAClE,UAAU,GAAG,OAAO,EAAE,eAAe,KAAK,cAAc,EAAE,eAAe,GAAG,CAAC;CAClF;CAcA,uBACE,GACA,GACA,GACQ;EACR,IAAM,IAAM,KAAK,wBAAwB,IAAI,CAAoB;EAMjE,OALI,KAAO,EAAI,SAAS,aAElB,CADU,GAAY,GAAG,EAAa,KAAK,MAAM,EAAE,KAAK,CACxD,EAAK,SAAS,EAAI,aAAa,IAAU,EAAI,gBAG5C,EAAa,GAAG,EAAE,GAAG,SAAS;CACvC;CAWA,iBAAyB,GAAkE;EACzF,IAAM,IAAqC,CAAC,GAGtC,oBAAiB,IAAI,IAAY;EAEvC,KAAK,IAAM,KAAQ,GACb,EAAK,SAAS,SAAS,EAAe,IAAI,EAAK,KAAK,KACxD,KAAK,kBAAkB,GAAM,GAAU,CAAc;EAEvD,OAAO;CACT;CAUA,kBACE,GACA,GACA,GACM;EACN,IAAM,IAAM,KAAK,OAAO,YAAY,EAAK,UAAU;EACnD,IAAI,EAAK,SAAS,OAAO;GACvB,EAAI,KAAK,GAAG,CAAG;GACf;EACF;EACA,KAAK,IAAI,IAAI,GAAG,IAAI,EAAI,QAAQ,KAAK;GACnC,IAAM,IAAI,EAAI;GACd,IAAI,CAAC,GAAG;GAGR,IAAM,IAAc,IAAI,IAAI,EAAI,IAAI,IAAI,iBAAiB,KAAA;GACzD,IAAI,MAAgB,KAAA,GAAW;IAC7B,IAAM,IAAe,KAAK,qBAAqB,EAAE,gBAAgB,CAAW;IAC5E,IAAI,EAAa,SAAS,GAAG;KAK3B,KAAK,IAAM,KAAK,GAAc,EAAe,IAAI,EAAE,KAAK;KACxD,IAAM,IAAc,KAAK,uBAAuB,EAAE,gBAAgB,EAAK,OAAO,CAAY;KAC1F,IAAI,MAAgB,EAAK,OAAO;MAG9B,IAAM,IAAS,EAAa,MAAM,MAAM,EAAE,UAAU,CAAW;MAC/D,IAAI,GAAQ;OACV,KAAK,kBAAkB,GAAQ,GAAK,CAAc;OAClD;MACF;KACF;IAEF;GACF;GACA,EAAI,KAAK,CAAC;EACZ;CACF;CAEA,WAAoB;EAClB,OAAO,KAAK,sBAAsB,KAAK,KAAK,gBAAgB,SAAS,KAAK,KAAK;CACjF;CAeA,MAAM,UAAU,IAAQ,IAAmB;EACrC,WAAK,WAAW,KAAK,gBAEzB;GADA,KAAK,gBAAgB,IACrB,KAAK,QAAQ,MAAM,4BAA4B,EAAE,SAAM,CAAC;GAExD,IAAI;IAIF,IAAI,KAAK,uBAAuB,GAAO;KAErC,AADA,KAAK,uBAAuB,GAC5B,KAAK,kBAAkB;KACvB;IACF;IAKA,IAAM,IAAO,IAAQ,KAAK,qBACpB,IAAS,KAAK,iBAAiB,KAAK,kBAAkB,CAAC,EAAE,QACzD,UAA8B,KAAK,iBAAiB,KAAK,kBAAkB,CAAC,EAAE,SAAS;IAG7F,IAAI,KAAK,gBAAgB,SAAS,GAAG;KACnC,IAAM,IAAW,KAAK,uBAAuB,KAAK,iBAAiB,CAAI,GACjE,IAAQ,KAAK,gBAAgB,OAAO,CAAQ;KAClD,KAAK,iBAAiB,CAAK;IAC7B;IAMA,IAAI,EAAc,IAAI,MACpB,MAAM,KAAK,YAAY,IAAO,EAAc,CAAC,GAEzC,KAAK,UAAS;IAGpB,IAAM,IAAQ,KAAK,iBAAiB,KAAK,kBAAkB,CAAC,EAAE;IAK9D,AADA,KAAK,sBAAsB,KAAK,IAAI,GAAG,KAAK,uBAAuB,IAAQ,KAAU,CAAK,GAC1F,KAAK,kBAAkB;GACzB,SAAS,GAAO;IAEd,MADA,KAAK,QAAQ,MAAM,mCAAmC,EAAE,SAAM,CAAC,GACzD;GACR,UAAU;IACR,KAAK,gBAAgB;GACvB;EA/CwD;CAgD1D;CAYA,MAAc,YAAY,GAA+B;EACvD,IAAI,CAAC,KAAK,mBAAmB,CAAC,KAAK,kBAAkB;GACnD,MAAM,KAAK,eAAe,CAAM;GAChC;EACF;EAEA,IAAI,CAAC,KAAK,iBAAiB;EAE3B,IAAI,CAAC,KAAK,kBAAkB,QAAQ,GAAG;GACrC,KAAK,kBAAkB;GACvB;EACF;EAEA,IAAM,IAAW,MAAM,KAAK,iBAAiB,KAAK;EAClD,IAAI,KAAK,WAAW,CAAC,GAAU;GAC7B,AAAK,MAAU,KAAK,kBAAkB;GACtC;EACF;EAEA,MAAM,KAAK,gBAAgB,GAAU,CAAM;CAC7C;CAiBA,uBAA+B,GAAwC,GAAwB;EAC7F,IAAI,IAAW;EACf,KAAK,IAAI,IAAI,EAAM,SAAS,GAAG,KAAK,GAAG,KAAK;GAC1C,IAAM,IAAO,EAAM;GACd,UACL,KAAY,KAAK,OAAO,YAAY,EAAK,UAAU,EAAE,QACjD,KAAY,IAAQ,OAAO;EACjC;EACA,OAAO;CACT;CAMA,MAAM,GAA6C;EACjD,KAAK,QAAQ,MAAM,wBAAwB,EAAE,kBAAe,CAAC;EAC7D,IAAM,IAAO,KAAK,MAAM,wBAAwB,CAAc;EAC9D,IAAI,CAAC,GAAM;EACX,IAAI,EAAK,SAAS,OAAO,OAAO,GAAW,CAAI;EAE/C,IAAM,IAAQ,KAAK,kBAAkB,EAAK,cAAc;EACxD,OAAO,IAAQ,GAAW,CAAK,IAAI,KAAA;CACrC;CASA,kBAA0B,GAA+D;EACvF,IAAM,IAAU,KAAK,MAAM,aAAa,CAAmB;EAC3D,IAAI,EAAQ,WAAW,GAAG;EAC1B,IAAI,EAAQ,WAAW,GAAG,OAAO,EAAQ;EAGzC,IAAM,IAAY,KAAK,MAAM,aAAa,EAAQ,IAAI,SAAS,EAAE,GAC3D,IAAM,KAAK,iBAAiB,IAAI,CAAS,GACzC,IAAc,KAAO,EAAI,SAAS,YAAY,EAAI,gBAAgB,KAAA;EACxE,IAAI,MAAgB,KAAA,GAAW;GAC7B,IAAM,IAAS,EAAQ,MAAM,MAAM,EAAE,UAAU,CAAW;GAC1D,IAAI,GAAQ,OAAO;EACrB;EAEA,OAAO,EAAQ,UAAU,GAAG,OAAO,EAAE,eAAe,KAAK,cAAc,EAAE,eAAe,GAAG,CAAC,EAAE,GAAG,EAAE;CACrG;CAEA,IAAI,GAAoC;EACtC,KAAK,QAAQ,MAAM,sBAAsB,EAAE,SAAM,CAAC;EAClD,IAAM,IAAM,KAAK,MAAM,WAAW,CAAK;EACvC,OAAO,IAAM,GAAW,CAAG,IAAI,KAAA;CACjC;CAYA,gBAAgB,GAAmD;EACjE,IAAM,IAAS,KAAK,2BAA2B,CAAc;EAC7D,IAAI,GAAQ;GAMV,IAAM,IAAW,EAAO,QAAQ,SAAS,MAAW;IAClD,IAAM,IAAQ,KAAK,MAAM,wBAAwB,EAAO,4BAA4B;IACpF,IAAI,CAAC,GAAO,OAAO,CAAC;IACpB,IAAM,IAAQ,KAAK,OAChB,YAAY,EAAM,UAAU,EAC5B,MAAM,MAAM,EAAE,mBAAmB,EAAO,4BAA4B;IACvE,OAAO,IAAQ,CAAC,EAAM,OAAO,IAAI,CAAC;GACpC,CAAC;GAED,IAAI,EAAS,SAAS,GAAG;IACvB,IAAM,IAAQ,KAAK,sBAAsB,CAAM,GACzC,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAO,EAAS,SAAS,CAAC,CAAC,GAC1D,IAAW,EAAS;IAC1B,OAAO;KACL,aAAa,EAAS,SAAS;KAC/B;KACA,OAAO;KACP;IACF;GACF;EACF;EAQA,IAAM,IAAQ,KAAK,MAAM,wBAAwB,CAAc;EAC/D,IAAI,GAAO;GACT,IAAM,IAAQ,KAAK,OAAO,YAAY,EAAM,UAAU,EAAE,MAAM,MAAM,EAAE,mBAAmB,CAAc;GACvG,IAAI,MAAU,KAAA,GACZ,OAAO;IAAE,aAAa;IAAO,UAAU,CAAC,EAAM,OAAO;IAAG,OAAO;IAAG,UAAU,EAAM;GAAQ;EAE9F;EAOA,OAAO;GAAE,aAAa;GAAO,UAAU,CAAC;GAAG,OAAO;GAAG,UAAU,KAAA;EAAU;CAC3E;CAGA,cAAc,GAAwB,GAAqB;EACzD,KAAK,QAAQ,MAAM,gCAAgC;GAAE;GAAgB;EAAM,CAAC;EAC5E,IAAM,IAAS,KAAK,2BAA2B,CAAc;EAC7D,IAAI,CAAC,GAAQ;EACb,IAAM,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAO,EAAO,QAAQ,SAAS,CAAC,CAAC,GAChE,IAAW,EAAO,QAAQ;EAC3B,MACD,EAAO,SAAS,aAClB,KAAK,kBAAkB,IAAI,EAAO,WAAW;GAAE,MAAM;GAAQ,aAAa,EAAS;EAAc,CAAC,GAClG,KAAK,QAAQ,MAAM,wCAAwC;GACzD;GACA,OAAO;GACP,aAAa,EAAS;EACxB,CAAC,KACQ,EAAO,SAAS,oBAGzB,KAAK,wBAAwB,IAAI,EAAO,WAAW;GAAE,MAAM;GAAQ,eAAe,EAAS;EAAc,CAAC,GAC1G,KAAK,QAAQ,MAAM,+CAA+C;GAChE;GACA,OAAO;GACP,eAAe,EAAS;GACxB,QAAQ,EAAO;EACjB,CAAC,MAED,KAAK,iBAAiB,IAAI,EAAO,WAAW;GAAE,MAAM;GAAQ,eAAe,EAAS;EAAc,CAAC,GACnG,KAAK,QAAQ,MAAM,2CAA2C;GAC5D;GACA,OAAO;GACP,eAAe,EAAS;GACxB,WAAW,EAAO;EACpB,CAAC,IAEH,KAAK,kBAAkB;CACzB;CASA,sBAA8B,GAAoC;EAChE,IAAI,EAAO,SAAS,WAAW;GAC7B,IAAM,IAAM,KAAK,kBAAkB,IAAI,EAAO,SAAS;GACvD,IAAI,CAAC,GAAK,OAAO,EAAO,QAAQ,SAAS;GACzC,IAAM,IAAM,EAAO,QAAQ,WAAW,MAAM,EAAE,kBAAkB,EAAI,WAAW;GAC/E,OAAO,MAAQ,KAAK,EAAO,QAAQ,SAAS,IAAI;EAClD;EACA,IAAM,IACJ,EAAO,SAAS,mBACZ,KAAK,wBAAwB,IAAI,EAAO,SAAS,IACjD,KAAK,iBAAiB,IAAI,EAAO,SAAS;EAChD,IAAI,CAAC,KAAO,EAAI,SAAS,WAAW,OAAO,EAAO,QAAQ,SAAS;EACnE,IAAM,IAAM,EAAO,QAAQ,WAAW,MAAM,EAAE,kBAAkB,EAAI,aAAa;EACjF,OAAO,MAAQ,KAAK,EAAO,QAAQ,SAAS,IAAI;CAClD;CAWA,2BAAmC,GAAwD;EACzF,IAAM,IAAO,KAAK,MAAM,wBAAwB,CAAc;EAC9D,IAAI,CAAC,GAAM;EAKX,IAAI,EAAK,SAAS,SAAS;GACzB,IAAM,IAAW,KAAK,MAAM,gBAAgB,EAAK,cAAc;GAQ/D,OAPI,EAAS,SAAS,IACb;IACL,MAAM;IACN,WAAW,KAAK,MAAM,aAAa,EAAK,cAAc;IACtD,SAAS,KAAK,iBAAiB,CAAQ;GACzC,IAEF;EACF;EASA,IAAM,IAAc,KAAK,OAAO,YAAY,EAAK,UAAU,GACrD,IAAU,KAAK,2BAA2B,GAAM,GAAa,CAAc;EACjF,IAAI,GAAS,OAAO;EAMpB,IAAM,IAAW,KAAK,MAAM,gBAAgB,EAAK,KAAK;EACtD,IAAI,EAAS,SAAS,KAAK,EAAY,GAAG,CAAC,GAAG,mBAAmB,GAC/D,OAAO;GACL,MAAM;GACN,WAAW,KAAK,MAAM,aAAa,EAAK,KAAK;GAC7C,SAAS,KAAK,iBAAiB,CAAQ;EACzC;CAIJ;CAYA,2BACE,GACA,GACA,GACgC;EAIhC,IADe,EAAY,GAAG,CAAC,GAAG,mBAAmB,KACvC,EAAK,8BAA8B,KAAA,GAAW;GAC1D,IAAM,IAAW,EAAK,2BAChB,IAAQ,KAAK,qBAAqB,CAAQ;GAChD,IAAI,GAAO;IACT,IAAM,IAAY,KAAK,OAAO,YAAY,EAAM,UAAU,GACpD,IAAM,EAAU,WAAW,MAAO,EAAG,mBAAmB,CAAQ,GAChE,IAAc,IAAM,IAAI,EAAU,IAAM,IAAI,iBAAiB,KAAA;IACnE,IAAI,MAAgB,KAAA,GAClB,OAAO,KAAK,mBAAmB,GAAU,EAAM,OAAO,CAAW;GAErE;GACA;EACF;EAGA,IAAM,IAAM,EAAY,WAAW,MAAO,EAAG,mBAAmB,CAAc,GACxE,IAAc,IAAM,IAAI,EAAY,IAAM,IAAI,iBAAiB,KAAA;EACjE,UAAgB,KAAA,GACpB,OAAO,KAAK,mBAAmB,GAAgB,EAAK,OAAO,CAAW;CACxE;CAYA,mBACE,GACA,GACA,GACgC;EAChC,IAAM,IAAe,KAAK,qBAAqB,GAAsB,CAAyB;EAC9F,IAAI,EAAa,WAAW,GAAG;EAC/B,IAAM,IAA0B,CAAC;GAAE,eAAe;GAAY,8BAA8B;EAAqB,CAAC;EAClH,KAAK,IAAM,KAAK,GAAc;GAC5B,IAAM,IAAO,KAAK,OAAO,YAAY,EAAE,UAAU,EAAE,GAAG,CAAC;GACvD,AAAI,KAAM,EAAQ,KAAK;IAAE,eAAe,EAAE;IAAO,8BAA8B,EAAK;GAAe,CAAC;EACtG;EACA,OAAO;GAAE,MAAM;GAAkB,WAAW;GAAsB;EAAQ;CAC5E;CASA,iBAAyB,GAAwD;EAC/E,IAAM,IAA0B,CAAC;EACjC,KAAK,IAAM,KAAK,GAAO;GACrB,IAAM,IAAO,KAAK,OAAO,YAAY,EAAE,UAAU,EAAE,GAAG,CAAC;GACvD,AAAI,KAAM,EAAQ,KAAK;IAAE,eAAe,EAAQ,CAAC;IAAG,8BAA8B,EAAK;GAAe,CAAC;EACzG;EACA,OAAO;CACT;CAOA,MAAM,KAAK,GAA0B,GAA2C;EAE9E,IADA,KAAK,QAAQ,MAAM,qBAAqB,GACpC,KAAK,SACP,MAAM,IAAI,EAAK,UAAU,kCAAkC,EAAU,iBAAiB,GAAG;EAG3F,IAAM,IAAa,GAAuB,CAAK,GAIzC,IAAuB,KAAK,yBAAyB,GAAG,EAAE,GAAG,gBAE7D,IAAS,MAAM,KAAK,cAAc,GAAY,GAAS,CAAoB;EAEjF,OADA,KAAK,qBAAqB,GAAQ,CAAO,GAClC;CACT;CAOA,qBAA6B,GAAmB,GAAwC;EAEtF,IAAI,CAAC,GAAS,QAAQ;EAMtB,IAAM,IAAiB,EAAO,0BAA0B,GAAG,CAAC;EAC5D,IAAI,MAAmB,KAAA,GAAW;EAClC,IAAM,IAAY,KAAK,MAAM,aAAa,CAAc;EAGxD,AADA,KAAK,kBAAkB,IAAI,GAAW;GAAE,MAAM;GAAQ,aAAa;EAAe,CAAC,GACnF,KAAK,kBAAkB;CACzB;CAcA,2BAAmC,GAAmB,GAAoC;EAOxF,IAAM,IAAY,KAAK,qBAAqB,CAAoB;EAChE,IAAI,CAAC,GAAW;EAOhB,IADmB,KAAK,OAAO,YAAY,EAAU,UACjD,EAAW,GAAG,CAAC,GAAG,mBAAmB,GAAsB;GAU7D,AATA,KAAK,wBAAwB,IAAI,GAAsB;IACrD,MAAM;IACN,uBAAuB,EAAO;GAChC,CAAC,GACD,KAAK,QAAQ,MAAM,qFAAqF;IACtG;IACA,SAAS,EAAO;GAClB,CAAC,GACD,KAAK,sCAAsC,GAC3C,KAAK,2BAA2B;GAChC;EACF;EAEA,IAAM,IAAY,KAAK,MAAM,aAAa,EAAU,KAAK;EAgBzD,AAdA,KAAK,iBAAiB,IAAI,GAAW;GACnC,MAAM;GACN,uBAAuB,EAAO;EAChC,CAAC,GACD,KAAK,QAAQ,MAAM,4EAA4E;GAC7F;GACA;GACA,SAAS,EAAO;EAClB,CAAC,GAKD,KAAK,+BAA+B,GACpC,KAAK,2BAA2B;CAClC;CAGA,MAAM,WAAW,GAAmB,GAA2C;EAG7E,IAFA,KAAK,QAAQ,MAAM,6BAA6B,EAAE,aAAU,CAAC,GAEzD,KAAK,SACP,MAAM,IAAI,EAAK,UAAU,wCAAwC,EAAU,iBAAiB,GAAG;EAUjG,IAAM,IAAY,KAAK,qBAAqB,CAAS;EACrD,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,oDAAoD,KACpD,EAAU,iBACV,GACF;EAEF,IAAM,IAAuB,KAAK,iBAAiB,GAAW,CAAS;EACvE,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,2DAA2D,KAC3D,EAAU,iBACV,GACF;EAcF,IAAI,IAAmB;EACvB,AAAI,EAAU,8BAA8B,KAAA,KACzB,KAAK,OAAO,YAAY,EAAU,UAAU,EAAE,GAAG,CAC9D,GAAU,mBAAmB,MAC/B,IAAmB,EAAU;EAIjC,IAAM,IAA2B;GAC/B,GAAG;GACH,QAAQ;EACV,GAQM,IAAa,KAAK,OAAO,iBAAiB,GAAkB,CAAoB,GAChF,IAAS,MAAM,KAAK,cAAc,CAAC,CAAU,GAAG,GAAa,CAAoB;EAEvF,OADA,KAAK,2BAA2B,GAAQ,CAAgB,GACjD;CACT;CAGA,MAAM,KAAK,GAAmB,GAA2B,GAA2C;EAGlG,IAFA,KAAK,QAAQ,MAAM,uBAAuB,EAAE,aAAU,CAAC,GAEnD,KAAK,SACP,MAAM,IAAI,EAAK,UAAU,kCAAkC,EAAU,iBAAiB,GAAG;EAK3F,IAAM,IAAa,KAAK,MAAM,wBAAwB,CAAS;EAC/D,IAAI,CAAC,GACH,MAAM,IAAI,EAAK,UACb,8CAA8C,KAC9C,EAAU,iBACV,GACF;EAEF,IAAM,IAAuB,KAAK,iBAAiB,GAAY,CAAS;EAExE,OAAO,KAAK,KAAK,GAAQ;GACvB,GAAG;GACH,QAAQ;GACR,QAAQ;EACV,CAAC;CACH;CAkBA,iBAAyB,GAA2C,GAAyC;EAC3G,IAAM,IAAU,KAAK,0BACf,IAAS,EAAQ,WAAW,MAAM,EAAE,mBAAmB,CAAW;EACxE,IAAI,IAAS,GACX,OAAO,EAAQ,IAAS,IAAI;EAE9B,IAAI,MAAW,GAAG;EAElB,IAAM,IAAW,KAAK,OAAO,YAAY,EAAW,UAAU,GACxD,IAAM,EAAS,WAAW,MAAM,EAAE,mBAAmB,CAAW;EACtE,IAAI,IAAM,GACR,OAAO,EAAS,IAAM,IAAI;EAE5B,IAAI,MAAQ,KAAK,EAAW,yBAAyB,KAAA,GAAW;GAG9D,IAAM,IAAa,KAAK,MAAM,wBAAwB,EAAW,oBAAoB;GACrF,IAAI,GACF,OAAO,KAAK,OAAO,YAAY,EAAW,UAAU,EAAE,GAAG,EAAE,GAAG;EAElE;CAEF;CAUA,GACE,GACA,GACY;EAEZ,IAAM,IAAK;EAEX,OADA,KAAK,SAAS,GAAG,GAAO,CAAE,SACb;GACX,KAAK,SAAS,IAAI,GAAO,CAAE;EAC7B;CACF;CAMA,QAAc;EACR,UAAK,SAGT;GAFA,KAAK,QAAQ,KAAK,sBAAsB,GACxC,KAAK,UAAU,IACf,KAAK,gBAAgB;GACrB,KAAK,IAAM,KAAS,KAAK,SAAS,EAAM;GASxC,AARA,KAAK,QAAQ,SAAS,GACtB,KAAK,SAAS,IAAI,GAClB,KAAK,kBAAkB,MAAM,GAC7B,KAAK,iBAAiB,MAAM,GAC5B,KAAK,wBAAwB,MAAM,GACnC,KAAK,gBAAgB,MAAM,GAC3B,KAAK,gBAAgB,SAAS,GAC9B,KAAK,sBAAsB,GAC3B,KAAK,WAAW;EAVK;CAWvB;CAMA,MAAc,eAAe,GAA+B;EAG1D,IAAM,IAAY,MAAM,GAAY,KAAK,UAAU,EAAE,OAAO,EAAO,GAAG,KAAK,OAAO;EAC9E,KAAK,WACT,MAAM,KAAK,gBAAgB,GAAW,CAAM;CAC9C;CAWA,MAAc,gBAAgB,GAAmB,GAA+B;EAE9E,IAAM,IAAe,IAAI,IAAI,KAAK,kBAAkB,EAAE,KAAK,MAAM,EAAQ,CAAC,CAAC,CAAC,GAEtE,EAAE,eAAY,gBAAa,MAAM,KAAK,kBAAkB,GAAM,GAAQ,CAAY;EACpF,KAAK,YACT,KAAK,mBAAmB,GACxB,KAAK,kBAAkB,EAAS,QAAQ,GACxC,KAAK,aAAa,GAAY,CAAM;CACtC;CAWA,aAAqB,GAA6C,GAAsB;EACtF,IAAM,IAAW,KAAK,uBAAuB,GAAY,CAAM,GACzD,IAAQ,EAAW,MAAM,CAAQ,GACjC,IAAW,EAAW,MAAM,GAAG,CAAQ;EAC7C,KAAK,IAAM,KAAK,GACd,KAAK,gBAAgB,IAAI,EAAQ,CAAC,CAAC;EAGrC,AADA,KAAK,gBAAgB,KAAK,GAAG,CAAQ,GACrC,KAAK,iBAAiB,CAAK;CAC7B;CAYA,oBAA4B,GAAyB;EACnD,KAAK,qBAAqB;EAC1B,IAAI;GACF,KAAK,IAAM,KAAU,EAAK,aACxB,KAAK,SAAS,MAAM,CAAM;GAK5B,KAAK,IAAM,KAAO,EAAK,aACrB,KAAK,MAAM,gBAAgB,CAAG;EAElC,UAAU;GACR,KAAK,qBAAqB;EAC5B;CACF;CAEA,MAAc,kBACZ,GACA,GACA,GACiF;EACjF,KAAK,oBAAoB,CAAS;EAClC,IAAI,IAAO,GAEL,UAAgC;GACpC,IAAI,IAAQ;GACZ,KAAK,IAAM,KAAK,KAAK,kBAAkB,GAGrC,AAAK,EAAa,IAAI,EAAQ,CAAC,CAAC,MAAG,KAAS,KAAK,OAAO,YAAY,EAAE,UAAU,EAAE;GAEpF,OAAO;EACT;EAEA,OAAO,EAAgB,IAAI,KAAU,EAAK,QAAQ,IAAG;GACnD,IAAM,IAAW,MAAM,EAAK,KAAK;GACjC,IAAI,CAAC,KAAY,KAAK,SAAS;GAE/B,AADA,KAAK,oBAAoB,CAAQ,GACjC,IAAO;EACT;EAGA,OAAO;GAAE,YADU,KAAK,kBAAkB,EAAE,QAAQ,MAAM,CAAC,EAAa,IAAI,EAAQ,CAAC,CAAC,CAC7E;GAAY,UAAU;EAAK;CACtC;CAGA,iBAAyB,GAA8C;EACrE,KAAK,IAAM,KAAK,GACd,KAAK,gBAAgB,OAAO,EAAQ,CAAC,CAAC;EAExC,AAAI,EAAM,SAAS,KACjB,KAAK,kBAAkB;CAE3B;CAMA,uBAA+B,GAA+C;EAC5E,IAAM,IAAW,KAAS,KAAK;EAS/B,AANA,KAAK,uBAAuB,EAAS,KAAK,MAAM,EAAQ,CAAC,CAAC,GAC1D,KAAK,yBAAyB,IAAI,IAAI,KAAK,oBAAoB,GAC/D,KAAK,0BAA0B,EAAS,KAAK,MAAM,EAAE,UAAU,GAI/D,KAAK,2BAA2B,KAAK,iBAAiB,CAAQ,EAAE,MAAM,KAAK,mBAAmB;CAChG;CAEA,gBAA8B;EAQxB,KAAK,uBAUT,KAAK,qBAAqB,GAC1B,KAAK,+BAA+B,GACpC,KAAK,sCAAsC,GAE3C,KAAK,2BAA2B;CAClC;CAUA,qBAAkD;EAChD,IAAM,oBAAW,IAAI,IAAoB;EACzC,KAAK,IAAM,CAAC,GAAW,MAAQ,KAAK,mBAClC,EAAS,IAAI,GAAW,EAAI,WAAW;EAEzC,KAAK,IAAM,CAAC,GAAW,MAAQ,KAAK,kBAC9B,EAAI,SAAS,aACjB,EAAS,IAAI,GAAW,EAAI,aAAa;EAE3C,OAAO;CACT;CAQA,oBAA6D;EAC3D,OAAO,KAAK,MAAM,aAAa,KAAK,mBAAmB,CAAC;CAC1D;CAYA,uBAAqC;EACnC,KAAK,IAAM,KAAO,KAAK,sBAAsB;GAM3C,IALa,KAAK,MAAM,QAAQ,CAG5B,GAAM,SAAS,WACF,KAAK,MAAM,gBAAgB,CACxC,EAAS,UAAU,GAAG;GAC1B,IAAM,IAAY,KAAK,MAAM,aAAa,CAAG;GAC5B,KAAK,kBAAkB,IAAI,CAIxC,KACJ,KAAK,kBAAkB,IAAI,GAAW;IAAE,MAAM;IAAU,aAAa;GAAI,CAAC;EAC5E;CACF;CAWA,iCAA+C;EAC7C,KAAK,IAAM,CAAC,GAAW,MAAQ,KAAK,kBAAkB;GACpD,IAAI,EAAI,SAAS,QAAQ;GACzB,IAAM,IAAQ,KAAK,MAAM,gBAAgB,CAAS,EAAE,QAAQ,MAAiC,EAAE,SAAS,KAAK;GAC7G,IAAI,EAAM,UAAU,GAAG;GACvB,IAAM,IAAS,EAAM,GAAG,EAAE;GACrB,KACL,KAAK,iBAAiB,IAAI,GAAW;IAAE,MAAM;IAAQ,eAAe,EAAO;GAAM,CAAC;EACpF;CACF;CAYA,wCAAsD;EACpD,KAAK,IAAM,CAAC,GAAU,MAAQ,KAAK,yBAAyB;GAC1D,IAAI,EAAI,SAAS,QAAQ;GACzB,IAAM,IAAQ,KAAK,qBAAqB,CAAQ;GAChD,IAAI,CAAC,GAAO;GACZ,IAAM,IAAY,KAAK,OAAO,YAAY,EAAM,UAAU,GACpD,IAAM,EAAU,WAAW,MAAM,EAAE,mBAAmB,CAAQ,GAC9D,IAAc,IAAM,IAAI,EAAU,IAAM,IAAI,iBAAiB,KAAA;GACnE,IAAI,MAAgB,KAAA,GAAW;GAC/B,IAAM,IAAS,KAAK,qBAAqB,GAAU,CAAW,EAAE,GAAG,EAAE;GAChE,KACL,KAAK,wBAAwB,IAAI,GAAU;IAAE,MAAM;IAAQ,eAAe,EAAO;GAAM,CAAC;EAC1F;CACF;CAEA,mBAA2B,GAAgC;EAEzD,IAAM,IAAU,EAAoB,CAAG,GACjC,IAAiB,EAAQ,IACzB,IAAQ,EAAQ;EAEtB,IAAI,CAAC,KAAkB,CAAC,GAAO;GAG7B,KAAK,SAAS,KAAK,gBAAgB,CAAG;GACtC;EACF;EAEA,AAAI,KAAS,KAAK,uBAAuB,IAAI,CAAK,KAChD,KAAK,SAAS,KAAK,gBAAgB,CAAG;CAE1C;CAEA,WAAmB,GAAgC;EAEjD,IAAI,KAAK,uBAAuB,IAAI,EAAM,KAAK,GAAG;GAChD,KAAK,SAAS,KAAK,OAAO,CAAK;GAC/B;EACF;EAKA,AAAI,EAAM,SAAS,WAAW,KAAK,mBAAmB,CAAK,MACzD,KAAK,uBAAuB,IAAI,EAAM,KAAK,GAC3C,KAAK,SAAS,KAAK,OAAO,CAAK;CAEnC;CAQA,mBAA2B,GAAuD;EAChF,IAAM,EAAE,cAAW;EAGnB,IAAI,MAAW,KAAA,GAAW,OAAO;EAOjC,IAAM,IAAa,KAAK,MAAM,wBAAwB,CAAM;EAE5D,OADK,IACE,KAAK,uBAAuB,IAAI,EAAQ,CAAU,CAAC,IADlC;CAE1B;CAEA,gBAAwB,GAAoD;EAC1E,IAAI,EAAS,WAAW,KAAK,qBAAqB,QAAQ,OAAO;EACjE,KAAK,IAAM,CAAC,GAAG,MAAS,EAAS,QAAQ,GAEvC,IADI,EAAQ,CAAI,MAAM,KAAK,qBAAqB,MAC5C,EAAK,eAAe,KAAK,wBAAwB,IAAI,OAAO;EAElE,OAAO;CACT;AACF,GAWa,MACX,MACwD,IAAI,GAAY,CAAO,GC1hD3E,WAA8B,CAAC,GAwB/B,KAAN,MAKmE;CA0DjE,YAAY,GAAuE;gCA9CzD,IAAI,IAAyD,mEAyClD,IAAI,IAGvC;EAMA,IAAM,IAAsC,GAAc,EAAQ,QAAQ,EAAQ,KAAK,GAEjF,IAAQ,GAAoB,EAAQ,YAAY;EAoCtD,IAnCI,MAAO,EAAe,QAAQ,IAClC,KAAK,WAAW,EAAQ,OAAO,SAAS,IAAI,EAAQ,aAAa,CAAc,GAC/E,KAAK,UAAU,EAAQ,QACvB,KAAK,SAAS,EAAQ,OACtB,KAAK,WAAW,EAAQ,UAAU,GAAW,EAAE,UAAU,GAAS,OAAO,CAAC,GAAG,YAAY,EACvF,WAAW,gBACb,CAAC,GAED,KAAK,WAAW,IAAI,GAAqC,KAAK,OAAO,GACrE,KAAK,mBAAmB,KAAK,SAAS,UAAU,YAGhD,KAAK,QAAQ,GAAyC,KAAK,QAAQ,KAAK,OAAO,GAC/E,KAAK,WAAW,GAAkB,KAAK,OAAO,KAAK,OAAO,cAAc,CAAC,GACzE,KAAK,QAAQ,GAAmD;GAC9D,MAAM,KAAK;GACX,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,cAAc,KAAK,cAAc,KAAK,IAAI;GAC1C,QAAQ,KAAK;GACb,eAAe,KAAK,OAAO,OAAO,KAAK,KAAK;EAC9C,CAAC,GACD,KAAK,WAAW,KAAK,OAAO,cAAc,KAAK,QAAQ,GAEvD,KAAK,OAAO,IAAI,KAAK,KAAK,GAG1B,KAAK,OAAO,KAAK,OACjB,KAAK,OAAO,KAAK,OAMb,EAAQ,UAAU;GACpB,IAAI;GACJ,KAAK,IAAM,KAAO,EAAQ,UAAU;IAClC,IAAM,IAAiB,OAAO,WAAW,GACnC,IAAsC;MACzC,IAA0B;MAC1B,IAAc;IACjB;IAGA,AAFI,MAAW,EAAY,KAAiB,IAC5C,KAAK,MAAM,aAAa;KAAE,QAAQ,CAAC,KAAK,OAAO,kBAAkB,CAAG,CAAC;KAAG,SAAS,CAAC;IAAE,GAAG,CAAW,GAClG,IAAY;GACd;EACF;EAeA,AAXA,KAAK,cAAc,MAAqC;GACtD,KAAK,eAAe,CAAW;EACjC,GAMA,KAAK,yBAAyB,MAAyC;GACrE,KAAK,0BAA0B,CAAW;EAC5C,GACA,KAAK,SAAS,GAAG,KAAK,qBAAqB;CAC7C;CAOA,IAAI,WAAkC;EACpC,OAAO,KAAK,SAAS;CACvB;CAGA,IAAI,SAAqC;EACvC,OAAO,KAAK,SAAS;CACvB;CAQA,UAAyB;EAwBvB,OAvBI,KAAK,WAAA,WACA,QAAQ,OAAO,IAAI,EAAK,UAAU,wCAAwC,EAAU,eAAe,GAAG,CAAC,IAE5G,KAAK,kBAAwB,KAAK,mBAEtC,KAAK,QAAQ,MAAM,iCAAiC,GAEpD,KAAK,kBAAkB,KAAK,SAAS,UAAU,KAAK,UAAU,EAAE,WACxD;GACJ,KAAK,QAAQ,MAAM,yDAAyD;EAC9E,IACC,MAAmB;GAClB,IAAM,IAAU,IAAI,EAAK,UACvB,mCAAmC,EAAa,CAAK,KACrD,EAAU,0BACV,KACA,EAAW,CAAK,CAClB;GAGA,MAFA,KAAK,QAAQ,MAAM,kDAAkD,GACrE,KAAK,SAAS,KAAK,SAAS,CAAO,GAC7B;EACR,CACF,GACO,KAAK;CACd;CAaA,mBAA+C;EAC7C,IAAM,IAAW,KAAK,QAAQ,KAAK;EACnC,OAAO,KAAY,MAAa,MAAM,IAAW,KAAA;CACnD;CAEA,MAAc,kBAAkB,GAA+B;EAC7D,OAAO,GAAiB,KAAK,iBAAiB,CAAM;CACtD;CAMA,eAAuB,GAAwC;EACzD,SAAK,WAAA,UAET,IAAI;GAOF,IAAI,EAAY,SAAA,cAAwB;IACtC,IAAM,IAAU,EAAoB,CAAW;IAG/C,KADgB,EAAA,iBAA8B,gBAC/B,SAAS;KACtB,IAAM,IAAU,GAAiB,CAAO;KAMxC,AALA,KAAK,QAAQ,MAAM,wDAAwD;MACzE,OAAO,EAAQ;MACf,cAAc,EAAQ;MACtB,MAAM,EAAQ;KAChB,CAAC,GACD,KAAK,SAAS,KAAK,SAAS,CAAO;IACrC;GACF;GAMA,IAAM,IAAQ,KAAK,SAAS,MAAM,CAAW;GAO7C,IAAI,MAAU,EAAM,SAAS,WAAW,EAAM,SAAS,WAAW;IAChE,IAAM,IAAa,EAAoB,CAAW,EAAE;IACpD,IAAI,MAAe,KAAA,GAAW;KAC5B,IAAM,IAAU,KAAK,kBAAkB,IAAI,CAAU;KACrD,AAAI,MACF,KAAK,kBAAkB,OAAO,CAAU,GAExC,EAAQ,QAAQ,EAAM,KAAK;IAE/B;GACF;GAMA,KAAK,MAAM,gBAAgB,CAAW;EACxC,SAAS,GAAO;GACd,KAAK,SAAS,KACZ,SACA,IAAI,EAAK,UACP,sCAAsC,EAAa,CAAK,KACxD,EAAU,0BACV,KACA,EAAW,CAAK,CAClB,CACF;EACF;CACF;CAOA,0BAAkC,GAA4C;EAC5E,IAAI,KAAK,WAAA,UAAsC;EAE/C,IAAM,EAAE,YAAS,eAAY;EAG7B,IAAI,MAAY,cAAc,CAAC,KAAK,kBAAkB;GACpD,KAAK,mBAAmB;GACxB;EACF;EAEA,IAAI,CAAC,GAAiB,CAAW,GAAG;EAEpC,KAAK,QAAQ,MAAM,sEAAsE;GACvF;GACA;GACA,UAAU,EAAY;EACxB,CAAC;EAED,IAAM,IAAM,GAAoB,GAAa,gBAAgB;EAM7D,KAAK,SAAS,KAAK,SAAS,CAAG;CACjC;CAaA,mBAA2B,GAAiC;EAC1D,KAAK,IAAM,KAAkB,GAAiB;GAM5C,IAAM,IAAO,KAAK,MAAM,wBAAwB,CAAc;GAC9D,AAAI,GAAM,SAAS,WAAW,EAAK,WAAW,KAAA,KAE5C,KAAK,MAAM,OAAO,EAAK,cAAc;EAEzC;CACF;CAOA,aAAqC;EACnC,IAAI,KAAK,WAAA,UACP,MAAM,IAAI,EAAK,UAAU,4CAA4C,EAAU,eAAe,GAAG;EAEnG,KAAK,QAAQ,MAAM,oCAAoC;EACvD,IAAM,IAAO,GAAmD;GAC9D,MAAM,KAAK;GACX,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,cAAc,KAAK,cAAc,KAAK,IAAI;GAC1C,QAAQ,KAAK;GACb,eAAe,KAAK,OAAO,OAAO,CAAI;EACxC,CAAC;EAED,OADA,KAAK,OAAO,IAAI,CAAI,GACb;CACT;CAGA,MAAc,cACZ,GACA,GACA,GACoB;EAQpB,IAPI,KAAK,WAAA,aAGT,MAAM,KAAK,kBAAkB,MAAM,GAI9B,KAAK,WAAA,WACR,MAAM,IAAI,EAAK,UAAU,qCAAqC,EAAU,eAAe,GAAG;EAI5F,IAAM,IAAQ,KAAK,SAAS;EAC5B,IAAI,MAAU,cAAc,MAAU,aACpC,MAAM,IAAI,EAAK,UAAU,8BAA8B,KAAS,EAAU,iBAAiB,GAAG;EAGhG,KAAK,QAAQ,MAAM,gCAAgC;EAEnD,IAAM,IAAiB,GAAa,UAAU,KAAA,GAKxC,IAAQ,GAAa,OAMvB;EACJ,AAAI,GAAa,WAAW,KAAA,KAAa,CAAC,GAAa,WACrD,IAAa;EAGf,IAAM,oBAAkB,IAAI,IAAY,GASlC,IAAqB,CAAC;EAM5B,KAAK,IAAM,KAAS,GAAO;GACzB,IAAM,IAAe,OAAO,WAAW,GAGjC,IAAiB,EAAM,kBAAkB,OAAO,WAAW;GACjE,EAAgB,IAAI,CAAc;GAelC,IAAM,IACJ,EAAM,SAAS,mBAAmB,EAAM,SAAS,gBAAgB,EAAM,mBAAmB,KAAA,IAOtF,IAAS,EAAM,WAAW,GAAa,WAAW,KAAA,IAAY,IAAa,EAAY,SACvF,IAAS,GAAa,QACtB,IAAc,EAAM,SAAS,eAAe,EAAM,SAAS,KAAA,GAE3D,IAAU,GAAsB;IACpC,MAAM;IACN;IACA;IACA,aAAa,KAAK,iBAAiB;IACnC,GAAI,MAAW,KAAA,KAAa,EAAE,UAAO;IACrC,GAAI,MAAW,KAAA,KAAa,EAAE,UAAO;IACrC,GAAI,MAAgB,KAAA,KAAa,EAAE,eAAY;IAC/C;GACF,CAAC;GAWD,AARK,KACH,KAAK,MAAM,aAAa;IAAE,QAAQ,CAAC,CAAK;IAAG,SAAS,CAAC;GAAE,GAAG,CAAO,GAGnE,EAAM,KAAK;IAAE,OAAO;IAAO;IAAgB;IAAc;IAAS;GAAW,CAAC,GAI1E,CAAC,KAAc,GAAa,WAAW,KAAA,KAAa,CAAC,GAAa,UAAU,EAAM,WAAW,KAAA,MAC/F,IAAa;EAEjB;EAOA,IAAM,IAAc,EAAM,GAAG,EAAE;EAC/B,IAAI,MAAgB,KAAA,GAIlB,MAAM,IAAI,EAAK,UACb,sEACA,EAAU,iBACV,GACF;EAEF,IAAM,IAAsB,EAAY,cAClC,IAAa,EAAY,gBAWzB,IAAe,IAAI,SAAiB,GAAS,MAAW;GAC5D,KAAK,kBAAkB,IAAI,GAAY;IAAE;IAAS;GAAO,CAAC;EAC5D,CAAC;EAgDD,OA7CA,EAAa,YAAY,CAEzB,CAAC,GAyCD,OAnCwB,YAAY;GAClC,IAAI;IACF,KAAK,IAAM,KAAQ,GACjB,MAAM,KAAK,SAAS,aAAa,EAAK,OAAO;KAC3C,QAAQ,EAAE,SAAS,EAAK,QAAQ;KAChC,WAAW,EAAK;IAClB,CAAC;GAEL,SAAS,GAAO;IACd,IAAM,IAAQ,EAAW,CAAK,GACxB,IAAe,GAAO,eAAe,OAAO,GAAO,eAAe,KAClE,IAAM,IAAI,EAAK,UACnB,IACI,wEACA,6BAA6B,EAAa,CAAK,KACnD,IAAe,EAAU,yBAAyB,EAAU,mBAC5D,IAAe,MAAM,KACrB,CACF;IASA,MARA,KAAK,SAAS,KAAK,SAAS,CAAG,GAG/B,KAAK,kBAAkB,OAAO,CAAU,GAInC,KAAgB,KAAK,mBAAmB,CAAC,GAAG,CAAe,CAAC,GAC3D;GACR;EACF,GAMM,GAEC;GACL,qBAAqB;GACrB,OAAO;GACP,cAAc;GAQd,QAAQ,YAAY;IAClB,MAAM,KAAK,eAAe;KACxB,qBAAqB;KACrB,GAAI,MAAU,KAAA,KAAa,EAAE,SAAM;IACrC,CAAC;GACH;GACA,2BAA2B,CAAC,GAAG,CAAe;GAC9C,oBAIE,GAAW,SAAS;IAClB,cAAc;IACd,aAAa,KAAK,SAAS;GAC7B,CAAC;EACL;CACF;CAGA,MAAM,OAAO,GAA8B;EACzC,OAAO,KAAK,eAAe,EAAE,SAAM,CAAC;CACtC;CA6BA,MAAc,eAAe,GAAyE;EAIpG,IAHI,KAAK,WAAA,aACT,MAAM,KAAK,kBAAkB,QAAQ,GAEhC,KAAK,WAAA,WAA6D;EACvE,KAAK,QAAQ,MAAM,mCAAmC;GACpD,OAAO,EAAO;GACd,qBAAqB,EAAO;EAC9B,CAAC;EAED,IAAM,IAAkC,GAGrC,IAAkB,OAAO,WAAW,EACvC;EAIA,AAHI,EAAO,UAAU,KAAA,MAAW,EAAQ,KAAiB,EAAO,QAC5D,EAAO,wBAAwB,KAAA,MAAW,EAAQ,KAAiC,EAAO,sBAE9F,MAAM,KAAK,SAAS,QAAQ;GAC1B,MAAM;GACN,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAQ,EAAE;EACvC,CAAC;CACH;CAGA,GAAG,GAAgB,GAAsD;EACvE,IAAI,KAAK,WAAA,UAAsC,OAAO;EAEtD,IAAM,IAAK;EAEX,OADA,KAAK,SAAS,GAAG,GAAO,CAAE,SACb;GACX,KAAK,SAAS,IAAI,GAAO,CAAE;EAC7B;CACF;CAGA,MAAM,QAAuB;EACvB,SAAK,WAAA,UAST;GARA,KAAK,SAAA,UACL,KAAK,QAAQ,KAAK,wBAAwB,GAEtC,KAAK,mBACP,KAAK,SAAS,YAAY,KAAK,UAAU,GAE3C,KAAK,SAAS,IAAI,KAAK,qBAAqB,GAE5C,KAAK,SAAS,IAAI;GAClB,KAAK,IAAM,KAAK,KAAK,QAAQ,EAAE,MAAM;GAIrC,IAHA,KAAK,OAAO,MAAM,GAGd,KAAK,kBAAkB,OAAO,GAAG;IACnC,IAAM,IAAY,IAAI,EAAK,UAAU,6CAA6C,EAAU,eAAe,GAAG;IAC9G,KAAK,IAAM,KAAW,KAAK,kBAAkB,OAAO,GAClD,EAAQ,OAAO,CAAS;IAE1B,KAAK,kBAAkB,MAAM;GAC/B;GAKA,IAAI;IACF,MAAM,KAAK,SAAS,MAAM;GAC5B,QAAQ,CAER;GAEA,MAAM,GAAiB,KAAK,UAAU,KAAK,iBAAiB,KAAK,SAAS,eAAe;EAtBvE;CAuBpB;AACF,GAgBa,MAMX,MAC0D,IAAI,GAAqB,CAAO,GCvtB/E,MACX,MAEA,GAAwB;CAAE,GAAG;CAAS,OAAO;AAAe,CAAC,GASlD,MACX,MAEA,GAAuB;CAAE,GAAG;CAAS,OAAO;AAAe,CAAC,GCuBjD,KAAmB,OAC9B,GACA,MAC8B;CAC9B,IAAI,EAAW,WAAW,YAiBxB,OANA,QAAQ,QAAQ,CAAY,EAAE,YAAY,CAE1C,CAAC,GACG,EAAW,WAAW,UACjB;EAAE,QAAQ;EAAS,OAAO,GAAa,EAAW,KAAK;CAAE,IAE3D,EAAE,QAAQ,EAAW,OAAO;CAErC,IAAI;EAGF,OADI,MADiB,MACN,eAAqB,EAAE,QAAQ,UAAU,IACjD,EAAE,QAAQ,WAAW;CAC9B,SAAS,GAAO;EAQd,OADI,GAAkB,CAAK,IAAU,EAAE,QAAQ,YAAY,IACpD;GAAE,QAAQ;GAAS,OAAO,GAAa,CAAK;EAAE;CACvD;AACF,GAUM,MAAgB,MAAmC;CACvD,IAAI,aAAiB,EAAK,WAAW,OAAO;CAC5C,IAAM,IAAU,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;CACrE,OAAO,IAAI,EAAK,UAAU,2BAA2B,KAAW,EAAU,aAAa,GAAG;AAC5F,GAWM,MAAqB,MACrB,OAAO,KAAU,aAAY,IAAuB,KAC1C,EAA6B,SAC3B"}
|