@ably/ai-transport 0.0.1
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/LICENSE +176 -0
- package/README.md +426 -0
- package/dist/ably-ai-transport.js +1388 -0
- package/dist/ably-ai-transport.js.map +1 -0
- package/dist/ably-ai-transport.umd.cjs +2 -0
- package/dist/ably-ai-transport.umd.cjs.map +1 -0
- package/dist/constants.d.ts +50 -0
- package/dist/core/codec/decoder.d.ts +62 -0
- package/dist/core/codec/encoder.d.ts +56 -0
- package/dist/core/codec/index.d.ts +8 -0
- package/dist/core/codec/lifecycle-tracker.d.ts +74 -0
- package/dist/core/codec/types.d.ts +188 -0
- package/dist/core/transport/client-transport.d.ts +10 -0
- package/dist/core/transport/conversation-tree.d.ts +9 -0
- package/dist/core/transport/decode-history.d.ts +41 -0
- package/dist/core/transport/headers.d.ts +26 -0
- package/dist/core/transport/index.d.ts +4 -0
- package/dist/core/transport/pipe-stream.d.ts +16 -0
- package/dist/core/transport/server-transport.d.ts +7 -0
- package/dist/core/transport/stream-router.d.ts +19 -0
- package/dist/core/transport/turn-manager.d.ts +34 -0
- package/dist/core/transport/types.d.ts +407 -0
- package/dist/errors.d.ts +46 -0
- package/dist/event-emitter.d.ts +65 -0
- package/dist/index.d.ts +11 -0
- package/dist/logger.d.ts +103 -0
- package/dist/react/ably-ai-transport-react.js +823 -0
- package/dist/react/ably-ai-transport-react.js.map +1 -0
- package/dist/react/ably-ai-transport-react.umd.cjs +2 -0
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -0
- package/dist/react/index.d.ts +11 -0
- package/dist/react/use-ably-messages.d.ts +18 -0
- package/dist/react/use-active-turns.d.ts +8 -0
- package/dist/react/use-client-transport.d.ts +7 -0
- package/dist/react/use-conversation-tree.d.ts +20 -0
- package/dist/react/use-edit.d.ts +7 -0
- package/dist/react/use-history.d.ts +19 -0
- package/dist/react/use-messages.d.ts +7 -0
- package/dist/react/use-regenerate.d.ts +7 -0
- package/dist/react/use-send.d.ts +7 -0
- package/dist/utils.d.ts +127 -0
- package/dist/vercel/ably-ai-transport-vercel.js +2331 -0
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -0
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +2 -0
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -0
- package/dist/vercel/codec/accumulator.d.ts +21 -0
- package/dist/vercel/codec/decoder.d.ts +22 -0
- package/dist/vercel/codec/encoder.d.ts +41 -0
- package/dist/vercel/codec/index.d.ts +22 -0
- package/dist/vercel/index.d.ts +3 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +2082 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +2 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -0
- package/dist/vercel/react/index.d.ts +3 -0
- package/dist/vercel/react/use-chat-transport.d.ts +29 -0
- package/dist/vercel/react/use-message-sync.d.ts +19 -0
- package/dist/vercel/transport/chat-transport.d.ts +118 -0
- package/dist/vercel/transport/index.d.ts +36 -0
- package/package.json +123 -0
- package/react/README.md +3 -0
- package/react/index.d.ts +1 -0
- package/react/index.js +1 -0
- package/react/index.umd.cjs +1 -0
- package/src/constants.ts +98 -0
- package/src/core/codec/decoder.ts +402 -0
- package/src/core/codec/encoder.ts +470 -0
- package/src/core/codec/index.ts +28 -0
- package/src/core/codec/lifecycle-tracker.ts +140 -0
- package/src/core/codec/types.ts +249 -0
- package/src/core/transport/client-transport.ts +959 -0
- package/src/core/transport/conversation-tree.ts +434 -0
- package/src/core/transport/decode-history.ts +337 -0
- package/src/core/transport/headers.ts +46 -0
- package/src/core/transport/index.ts +34 -0
- package/src/core/transport/pipe-stream.ts +95 -0
- package/src/core/transport/server-transport.ts +458 -0
- package/src/core/transport/stream-router.ts +118 -0
- package/src/core/transport/turn-manager.ts +147 -0
- package/src/core/transport/types.ts +533 -0
- package/src/errors.ts +58 -0
- package/src/event-emitter.ts +103 -0
- package/src/index.ts +89 -0
- package/src/logger.ts +241 -0
- package/src/react/index.ts +11 -0
- package/src/react/use-ably-messages.ts +37 -0
- package/src/react/use-active-turns.ts +61 -0
- package/src/react/use-client-transport.ts +37 -0
- package/src/react/use-conversation-tree.ts +71 -0
- package/src/react/use-edit.ts +24 -0
- package/src/react/use-history.ts +111 -0
- package/src/react/use-messages.ts +32 -0
- package/src/react/use-regenerate.ts +24 -0
- package/src/react/use-send.ts +25 -0
- package/src/react/vite.config.ts +32 -0
- package/src/tsconfig.json +25 -0
- package/src/utils.ts +230 -0
- package/src/vercel/codec/accumulator.ts +603 -0
- package/src/vercel/codec/decoder.ts +615 -0
- package/src/vercel/codec/encoder.ts +396 -0
- package/src/vercel/codec/index.ts +37 -0
- package/src/vercel/index.ts +12 -0
- package/src/vercel/react/index.ts +4 -0
- package/src/vercel/react/use-chat-transport.ts +60 -0
- package/src/vercel/react/use-message-sync.ts +34 -0
- package/src/vercel/react/vite.config.ts +33 -0
- package/src/vercel/transport/chat-transport.ts +278 -0
- package/src/vercel/transport/index.ts +56 -0
- package/src/vercel/vite.config.ts +33 -0
- package/src/vite.config.ts +31 -0
- package/vercel/README.md +3 -0
- package/vercel/index.d.ts +1 -0
- package/vercel/index.js +1 -0
- package/vercel/index.umd.cjs +1 -0
- package/vercel/react/README.md +3 -0
- package/vercel/react/index.d.ts +1 -0
- package/vercel/react/index.js +1 -0
- package/vercel/react/index.umd.cjs +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ably-ai-transport-vercel-react.js","names":[],"sources":["../../../src/errors.ts","../../../src/vercel/transport/chat-transport.ts","../../../src/constants.ts","../../../src/event-emitter.ts","../../../src/logger.ts","../../../src/utils.ts","../../../src/core/transport/conversation-tree.ts","../../../src/core/transport/decode-history.ts","../../../src/core/transport/headers.ts","../../../src/core/transport/stream-router.ts","../../../src/core/transport/client-transport.ts","../../../src/vercel/codec/accumulator.ts","../../../src/core/codec/decoder.ts","../../../src/core/codec/lifecycle-tracker.ts","../../../src/vercel/codec/decoder.ts","../../../src/core/codec/encoder.ts","../../../src/vercel/codec/encoder.ts","../../../src/vercel/codec/index.ts","../../../src/vercel/transport/index.ts","../../../src/vercel/react/use-chat-transport.ts","../../../src/vercel/react/use-message-sync.ts"],"sourcesContent":["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 // 104000 - 104999 are reserved for AI Transport SDK errors\n\n /**\n * Encoder recovery failed after flush — one or more updateMessage calls\n * could not recover a failed append pipeline.\n */\n EncoderRecoveryFailed = 104000,\n\n /**\n * A transport-level channel subscription callback threw unexpectedly.\n */\n TransportSubscriptionError = 104001,\n\n /**\n * Cancel listener or onCancel hook threw while processing a cancel message.\n */\n CancelListenerError = 104002,\n\n /**\n * A turn lifecycle event (turn-start or turn-end) failed to publish.\n */\n TurnLifecycleError = 104003,\n\n /**\n * An operation was attempted on a transport that has already been closed.\n */\n TransportClosed = 104004,\n\n /**\n * The HTTP POST to the server endpoint failed (network error or non-2xx response).\n */\n TransportSendFailed = 104005,\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 * Vercel chat transport: wraps a core ClientTransport to satisfy the\n * ChatTransport interface that useChat expects.\n *\n * This is a thin adapter — the real logic lives in the core transport.\n * The chat transport maps Vercel's sendMessages/reconnectToStream contract\n * to the core transport's send/cancel methods.\n *\n * useChat manages message state before calling sendMessages:\n * - submit-message: appends the new user message, passes the full array\n * - regenerate-message: truncates after the target, passes the truncated array\n *\n * The adapter uses `trigger` to determine the history/messages split:\n * - submit-message: last message is new (publish to channel), rest is history\n * - regenerate-message: no new messages, entire array is history\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport type { ClientTransport, CloseOptions, SendOptions } from '../../core/transport/types.js';\nimport { ErrorCode } from '../../errors.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 id?: 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 regeneration requests. Identifies which assistant\n * message to regenerate. Undefined for submit-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. */\n messages: AI.UIMessage[];\n /** The msg-id of the message being forked (regenerated or edited). */\n forkOf?: string;\n /** The msg-id of the predecessor in the conversation thread. */\n parent?: string | null;\n}\n\n/** Options for customizing the ChatTransport behavior. */\nexport interface ChatTransportOptions {\n /**\n * Customize the POST body before sending. Called by sendMessages()\n * with the conversation context. Return the body and headers for\n * the HTTP POST.\n *\n * Default: sends all previous messages as `history` in the body.\n * @param context - The conversation context for the current request.\n * @returns The body and headers to use for the HTTP 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.\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(options?: CloseOptions): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Vercel ChatTransport from a core ClientTransport.\n *\n * Maps Vercel's useChat contract to the core transport's methods:\n * - trigger 'submit-message' → transport.send(lastMessage) with history in body\n * - trigger 'regenerate-message' → transport.send([]) with all messages as history\n * - abortSignal → transport.cancel({ all: true })\n * - reconnectToStream → null (observer mode handles in-progress streams)\n * @param transport - The core client transport 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 transport: ClientTransport<AI.UIMessageChunk, AI.UIMessage>,\n chatOptions?: ChatTransportOptions,\n): ChatTransport => ({\n sendMessages: async (opts) => {\n const { messages, abortSignal, trigger, messageId } = opts;\n\n // Determine the history/messages split based on trigger.\n // - submit-message: useChat appended the new user message → last is new\n // - regenerate-message: useChat truncated the array → no new messages\n let newMessages: AI.UIMessage[];\n let history: AI.UIMessage[];\n\n if (trigger === 'regenerate-message') {\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 history = messages.slice(0, -1);\n }\n\n // Compute fork metadata from the conversation tree.\n // For regeneration: forkOf = messageId (the assistant message being regenerated),\n // parent = the parent of that message in the tree.\n let forkOf: string | undefined;\n let parent: string | null | undefined;\n\n if (trigger === 'regenerate-message' && messageId) {\n forkOf = messageId;\n // Look up the parent of the message being regenerated.\n // messageId comes from useChat (UIMessage.id), so use getNodeByKey\n // which resolves via the codec key secondary index.\n const node = transport.getTree().getNodeByKey(messageId);\n if (node) {\n // Use the tree node's msgId (x-ably-msg-id) as forkOf — this is\n // what the server stamps on the wire, not the UIMessage.id.\n forkOf = node.msgId;\n parent = node.parentId;\n }\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 id: 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 const historyWithHeaders = history.map((m) => ({\n message: m,\n headers: transport.getMessageHeaders(m),\n }));\n sendBody = {\n history: historyWithHeaders,\n id: opts.chatId,\n trigger,\n ...(messageId !== undefined && { messageId }),\n ...(forkOf !== undefined && { forkOf }),\n ...(parent !== undefined && { parent }),\n };\n sendHeaders = undefined;\n }\n\n const sendOpts: SendOptions = { body: sendBody, headers: sendHeaders };\n if (forkOf !== undefined) sendOpts.forkOf = forkOf;\n if (parent !== undefined) sendOpts.parent = parent;\n\n const turn = await transport.send(newMessages, sendOpts);\n\n // Wire abort signal to cancel all turns on the channel.\n // In multi-user scenarios, any client can stop any stream — cancelling\n // by specific turnId would only work for the sender.\n if (abortSignal) {\n abortSignal.addEventListener('abort', () => void transport.cancel({ all: true }), {\n once: true,\n });\n }\n\n // Return an empty stream that closes when the turn ends.\n // useChat consumes the returned stream to accumulate the assistant message,\n // but useMessageSync already pushes the transport's authoritative message\n // state into useChat via setMessages. Returning the real event stream would\n // cause useChat to accumulate a duplicate assistant message. Instead, we\n // return a stream that produces no chunks and closes when the turn's stream\n // finishes, so useChat knows when streaming is done without duplicating state.\n const { readable, writable } = new TransformStream<AI.UIMessageChunk>();\n const writer = writable.getWriter();\n // Fire-and-forget: we only care about the close/abort signal, not the piped data.\n // Errors on the turn stream are surfaced via transport.on('error'), not here.\n /* eslint-disable @typescript-eslint/no-empty-function -- swallow: writer.close() rejection after stream teardown is unrecoverable */\n turn.stream\n .pipeTo(\n new WritableStream({\n close: () => {\n writer.close().catch(() => {});\n },\n abort: () => {\n writer.close().catch(() => {});\n },\n }),\n )\n .catch(() => {\n writer.close().catch(() => {});\n });\n /* eslint-enable @typescript-eslint/no-empty-function */\n return readable;\n },\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 (options?: CloseOptions) => transport.close(options),\n});\n","/**\n * Shared constants used by both codec and transport layers.\n *\n * Header constants define the `x-ably-*` wire protocol. Message and event\n * name constants define the transport 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 turn, 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 = 'x-ably-stream';\n\n/** Header: lifecycle status of a streamed message. Only set when x-ably-stream is \"true\". */\nexport const HEADER_STATUS = 'x-ably-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 = 'x-ably-stream-id';\n\n// ---------------------------------------------------------------------------\n// Identity headers (used by transport for turn correlation)\n// ---------------------------------------------------------------------------\n\n/** Header: turn correlation ID. Set on every message in a turn. */\nexport const HEADER_TURN_ID = 'x-ably-turn-id';\n\n/** Header: message identity. Assigned per message (user or assistant). Used for optimistic reconciliation on the client. */\nexport const HEADER_MSG_ID = 'x-ably-msg-id';\n\n/** Header: clientId of the user who initiated the turn. Set by the server on stream messages. */\nexport const HEADER_TURN_CLIENT_ID = 'x-ably-turn-client-id';\n\n/** Header: message role (e.g. \"user\", \"assistant\"). */\nexport const HEADER_ROLE = 'x-ably-role';\n\n// ---------------------------------------------------------------------------\n// Cancel headers\n// ---------------------------------------------------------------------------\n\n/** Header: cancel a specific turn by ID. */\nexport const HEADER_CANCEL_TURN_ID = 'x-ably-cancel-turn-id';\n\n/** Header: cancel all turns belonging to the sender's clientId. */\nexport const HEADER_CANCEL_OWN = 'x-ably-cancel-own';\n\n/** Header: cancel all turns on the channel. */\nexport const HEADER_CANCEL_ALL = 'x-ably-cancel-all';\n\n/** Header: cancel all turns belonging to a specific clientId. */\nexport const HEADER_CANCEL_CLIENT_ID = 'x-ably-cancel-client-id';\n\n// ---------------------------------------------------------------------------\n// Fork / branching headers\n// ---------------------------------------------------------------------------\n\n/** Header: the msg-id of the immediately preceding message in this branch. */\nexport const HEADER_PARENT = 'x-ably-parent';\n\n/** Header: the msg-id of the message this one replaces (creates a fork). */\nexport const HEADER_FORK_OF = 'x-ably-fork-of';\n\n// ---------------------------------------------------------------------------\n// Turn lifecycle headers\n// ---------------------------------------------------------------------------\n\n/** Header: reason a turn ended (on x-ably-turn-end messages). */\nexport const HEADER_TURN_REASON = 'x-ably-turn-reason';\n\n// ---------------------------------------------------------------------------\n// Message / event names\n// ---------------------------------------------------------------------------\n\n/** Message name: client->server cancel signal. */\nexport const EVENT_CANCEL = 'x-ably-cancel';\n\n/** Message name: server publishes this to signal a turn has started. */\nexport const EVENT_TURN_START = 'x-ably-turn-start';\n\n/** Message name: server publishes this to signal a turn has ended. */\nexport const EVENT_TURN_END = 'x-ably-turn-end';\n\n/** Message name: transport-level abort signal (stream cancelled). */\nexport const EVENT_ABORT = 'x-ably-abort';\n\n/** Message name: transport-level error signal. */\nexport const EVENT_ERROR = 'x-ably-error';\n\n// ---------------------------------------------------------------------------\n// Domain header prefix (used by codec implementations)\n// ---------------------------------------------------------------------------\n\n/** Prefix for domain-specific headers. Distinguishes codec-layer headers from transport `x-ably-*` headers. */\nexport const DOMAIN_HEADER_PREFIX = 'x-domain-';\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. The logger parameter ensures listener\n// exceptions are caught and logged rather than crashing.\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 logHandler?: LogHandler;\n logLevel: LogLevel;\n}\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 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 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\nimport { DOMAIN_HEADER_PREFIX } from './constants.js';\n\n/**\n * Extract extras.headers from an Ably InboundMessage.\n * @param message - The Ably message to extract headers from.\n * @returns The headers record, or an empty object if absent.\n */\nexport const getHeaders = (message: Ably.InboundMessage): 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 headers = (extras as { headers?: unknown }).headers;\n if (!headers || typeof headers !== 'object') return {};\n // CAST: Ably wire protocol guarantees headers is Record<string, string>\n // when present, verified by the runtime guards above.\n return headers as Record<string, string>;\n};\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 * Set multiple headers at once, skipping entries whose values are undefined or null.\n * Each value is converted using the same rules as {@link setIfPresent}.\n * @param headers - The headers object to mutate.\n * @param entries - Key-value pairs to set.\n */\nexport const setHeadersIfPresent = (headers: Record<string, string>, entries: Record<string, unknown>): void => {\n for (const [key, value] of Object.entries(entries)) {\n setIfPresent(headers, key, 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/**\n * Build a domain headers record from key-value pairs. Each key is automatically\n * prefixed with {@link DOMAIN_HEADER_PREFIX}. Values that are undefined or null\n * are skipped; strings are set directly; booleans, numbers, and objects are\n * converted using the same rules as {@link setIfPresent}.\n * @param entries - Unprefixed key-value pairs (e.g. `{ toolCallId: 'tc-1' }` becomes `{ 'x-domain-toolCallId': 'tc-1' }`).\n * @returns A new headers record with prefixed keys.\n */\nexport const domainHeaders = (entries: Record<string, unknown>): Record<string, string> => {\n const h: Record<string, string> = {};\n for (const [key, value] of Object.entries(entries)) {\n setIfPresent(h, DOMAIN_HEADER_PREFIX + key, value);\n }\n return h;\n};\n\n/**\n * Read a domain header value from a headers record.\n * @param headers - The headers record to read from.\n * @param key - The unprefixed domain key (e.g. `'toolCallId'` reads `'x-domain-toolCallId'`).\n * @returns The header value, or undefined if absent.\n */\nexport const getDomainHeader = (headers: Record<string, string>, key: string): string | undefined =>\n headers[DOMAIN_HEADER_PREFIX + 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\"/\"false\"), 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 domain headers record.\n * @returns A fluent builder that prefixes each key with the domain header prefix.\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[DOMAIN_HEADER_PREFIX + key] = value;\n return writer;\n },\n bool: (key: string, value: boolean | undefined) => {\n if (value !== undefined) h[DOMAIN_HEADER_PREFIX + key] = String(value);\n return writer;\n },\n json: (key: string, value: unknown) => {\n if (value !== undefined && value !== null) h[DOMAIN_HEADER_PREFIX + key] = JSON.stringify(value);\n return writer;\n },\n build: () => h,\n };\n return writer;\n};\n","/**\n * ConversationTree — materializes a branching conversation from a flat\n * oplog of Ably messages using serial-first ordering.\n *\n * Serial order (the total order assigned by Ably) is the primary mechanism\n * for linear message sequences. `x-ably-parent` and `x-ably-fork-of` headers\n * are only structurally meaningful at branch points — where the user is\n * interacting with a visible message and the client always has it loaded.\n *\n * `upsert()` is the sole mutation method. Messages can arrive in any order\n * (live subscription, history pages, seed data) and the tree produces the\n * correct `flatten()` output once all messages are present.\n *\n * The tree owns conversation state. `flatten()` returns the linear message\n * list for the currently selected branches — this is what the transport's\n * `getMessages()` delegates to.\n */\n\nimport { HEADER_FORK_OF, HEADER_PARENT } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport type { ConversationNode, ConversationTree } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Internal node type\n// ---------------------------------------------------------------------------\n\ninterface InternalNode<TMessage> {\n node: ConversationNode<TMessage>;\n /** Insertion sequence — tiebreaker for null-serial messages. */\n insertSeq: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CT13\nclass DefaultConversationTree<TMessage> implements ConversationTree<TMessage> {\n /** All nodes indexed by msgId (x-ably-msg-id). */\n private readonly _nodeIndex = new Map<string, InternalNode<TMessage>>();\n\n /** Secondary index: codec message key to msgId. Bridges UIMessage.id to x-ably-msg-id. */\n private readonly _codecKeyIndex = new Map<string, string>();\n\n /**\n * All nodes sorted by serial (lexicographic). Null-serial messages\n * (optimistic inserts, seed data) sort after all serial-bearing messages,\n * ordered among themselves by insertion sequence.\n */\n private readonly _sortedList: InternalNode<TMessage>[] = [];\n\n /**\n * Parent index: parentId to set of child msgIds.\n * Nodes with no parent are indexed under the key `null`.\n */\n private readonly _parentIndex = new Map<string | undefined, Set<string>>();\n\n /**\n * Selected sibling index at each fork point, keyed by the msgId of\n * the first sibling in the group (the fork target). Default: last.\n */\n private readonly _selections = new Map<string, number>();\n\n private readonly _getKey: (message: TMessage) => string;\n private readonly _logger: Logger;\n\n /** Monotonically increasing counter for insertion sequence. */\n private _seqCounter = 0;\n\n constructor(getKey: (message: TMessage) => string, logger: Logger) {\n this._getKey = getKey;\n this._logger = logger;\n }\n\n // -------------------------------------------------------------------------\n // Sorted list maintenance\n // -------------------------------------------------------------------------\n\n /**\n * Compare two nodes for sorted list ordering.\n * Serial-bearing nodes sort by serial (lexicographic).\n * Null-serial nodes sort after all serial-bearing nodes.\n * Among null-serial nodes, sort by insertion sequence.\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<TMessage>, b: InternalNode<TMessage>): number {\n const sa = a.node.serial;\n const sb = b.node.serial;\n if (sa === undefined && sb === undefined) return a.insertSeq - b.insertSeq;\n if (sa === undefined) return 1; // a sorts after serial-bearing b\n if (sb === undefined) return -1; // b sorts after serial-bearing a\n if (sa < sb) return -1;\n if (sa > sb) return 1;\n return a.insertSeq - b.insertSeq; // same serial: preserve insertion order\n }\n\n /**\n * Insert a node into sortedList at the correct position via binary search.\n * @param internal - The node to insert.\n */\n private _insertSorted(internal: InternalNode<TMessage>): void {\n const serial = internal.node.serial;\n\n // Fast path: null-serial always appends to end (among other null-serials)\n if (serial === undefined) {\n this._sortedList.push(internal);\n return;\n }\n\n // Binary search for insertion point among serial-bearing nodes.\n let lo = 0;\n let hi = this._sortedList.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const midNode = this._sortedList[mid];\n if (!midNode) break; // unreachable: mid is always in bounds\n if (this._compareNodes(midNode, internal) <= 0) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n this._sortedList.splice(lo, 0, internal);\n }\n\n /**\n * Remove a node from sortedList.\n * @param internal - The node to remove.\n */\n private _removeSorted(internal: InternalNode<TMessage>): void {\n const idx = this._sortedList.indexOf(internal);\n if (idx !== -1) this._sortedList.splice(idx, 1);\n }\n\n // -------------------------------------------------------------------------\n // Parent index maintenance\n // -------------------------------------------------------------------------\n\n private _addToParentIndex(parentId: string | undefined, msgId: string): void {\n let set = this._parentIndex.get(parentId);\n if (!set) {\n set = new Set();\n this._parentIndex.set(parentId, set);\n }\n set.add(msgId);\n }\n\n private _removeFromParentIndex(parentId: string | undefined, msgId: string): void {\n const set = this._parentIndex.get(parentId);\n if (set) {\n set.delete(msgId);\n if (set.size === 0) this._parentIndex.delete(parentId);\n }\n }\n\n // -------------------------------------------------------------------------\n // Sibling grouping\n // -------------------------------------------------------------------------\n\n /**\n * Get the sibling group that `msgId` belongs to.\n *\n * A sibling group is: the original message + all messages whose `forkOf`\n * points to the original (or transitively to a sibling). We find the\n * group root by following `forkOf` chains to the earliest ancestor that\n * has no `forkOf` (or whose `forkOf` target doesn't share the same parent).\n * @param msgId - The msg-id to look up the sibling group for.\n * @returns The ordered list of sibling nodes.\n */\n // Spec: AIT-CT13b\n private _getSiblingGroup(msgId: string): ConversationNode<TMessage>[] {\n const entry = this._nodeIndex.get(msgId);\n if (!entry) return [];\n\n // Find the \"original\" — the message at the root of the fork chain\n // that shares the same parentId. Guard against cycles in forkOf chains.\n let original = entry.node;\n const visitedGroup = new Set<string>([original.msgId]);\n while (original.forkOf) {\n if (visitedGroup.has(original.forkOf)) break; // cycle guard\n const forkTarget = this._nodeIndex.get(original.forkOf);\n if (!forkTarget || forkTarget.node.parentId !== original.parentId) break;\n original = forkTarget.node;\n visitedGroup.add(original.msgId);\n }\n\n // Collect all siblings: nodes with the same parentId that either\n // ARE the original, or have a forkOf chain leading to the original.\n const parentId = original.parentId;\n const originalId = original.msgId;\n const siblings: InternalNode<TMessage>[] = [];\n\n const candidateIds = this._parentIndex.get(parentId);\n if (candidateIds) {\n for (const childId of candidateIds) {\n const childEntry = this._nodeIndex.get(childId);\n if (childEntry && this._isSiblingOf(childEntry.node, originalId)) {\n siblings.push(childEntry);\n }\n }\n }\n\n // Sort by Ably serial (lexicographic). Messages without a serial\n // (optimistic inserts before server relay) sort after all serial-bearing\n // siblings — they represent the user's most recent action.\n siblings.sort((a, b) => this._compareNodes(a, b));\n return siblings.map((s) => s.node);\n }\n\n /**\n * Check if `node` belongs to the sibling group rooted at `originalId`.\n * A node is a sibling if it IS the original or its forkOf chain leads\n * to the original (with the same parentId).\n * @param node - The node to check.\n * @param originalId - The group root to match against.\n * @returns True if the node belongs to the sibling group.\n */\n private _isSiblingOf(node: ConversationNode<TMessage>, originalId: string): boolean {\n if (node.msgId === originalId) return true;\n let current = node;\n const visited = new Set<string>([current.msgId]);\n while (current.forkOf) {\n if (current.forkOf === originalId) return true;\n if (visited.has(current.forkOf)) break; // cycle guard\n const target = this._nodeIndex.get(current.forkOf);\n if (!target) break;\n current = target.node;\n visited.add(current.msgId);\n }\n return false;\n }\n\n /**\n * Get the \"group root\" msgId for a sibling group — the original message\n * that all forks trace back to.\n * @param msgId - Any msg-id in the sibling group.\n * @returns The msg-id of the group root.\n */\n private _getGroupRoot(msgId: string): string {\n const entry = this._nodeIndex.get(msgId);\n if (!entry) return msgId;\n\n let current = entry.node;\n const visited = new Set<string>([current.msgId]);\n while (current.forkOf) {\n if (visited.has(current.forkOf)) break; // cycle guard\n const forkTarget = this._nodeIndex.get(current.forkOf);\n if (!forkTarget || forkTarget.node.parentId !== current.parentId) break;\n current = forkTarget.node;\n visited.add(current.msgId);\n }\n return current.msgId;\n }\n\n // -------------------------------------------------------------------------\n // Public query methods\n // -------------------------------------------------------------------------\n\n flatten(): TMessage[] {\n const result: TMessage[] = [];\n const currentPath = new Set<string>();\n // Track which sibling groups we've already resolved to avoid\n // re-resolving for every member of the group.\n const resolvedGroups = new Map<string, string>(); // groupRootId → selected msgId\n\n for (const internal of this._sortedList) {\n const node = internal.node;\n const { msgId, parentId } = node;\n\n // Step 1: Check parent reachability.\n if (parentId !== undefined && !currentPath.has(parentId)) {\n continue;\n }\n\n // Step 2: Check sibling selection.\n const group = this._getSiblingGroup(msgId);\n if (group.length > 1) {\n const groupRootId = this._getGroupRoot(msgId);\n let selectedId = resolvedGroups.get(groupRootId);\n if (selectedId === undefined) {\n const selectedIdx = this._selections.get(groupRootId) ?? group.length - 1;\n const clamped = Math.max(0, Math.min(selectedIdx, group.length - 1));\n const selected = group[clamped];\n if (!selected) break; // unreachable: clamped is always in bounds\n selectedId = selected.msgId;\n resolvedGroups.set(groupRootId, selectedId);\n }\n if (msgId !== selectedId) {\n continue;\n }\n }\n\n currentPath.add(msgId);\n result.push(node.message);\n }\n\n return result;\n }\n\n getSiblings(msgId: string): TMessage[] {\n return this._getSiblingGroup(msgId).map((n) => n.message);\n }\n\n hasSiblings(msgId: string): boolean {\n return this._getSiblingGroup(msgId).length > 1;\n }\n\n getSelectedIndex(msgId: string): number {\n const group = this._getSiblingGroup(msgId);\n if (group.length <= 1) return 0;\n const groupRootId = this._getGroupRoot(msgId);\n const stored = this._selections.get(groupRootId);\n if (stored !== undefined) return Math.max(0, Math.min(stored, group.length - 1));\n return group.length - 1; // default: latest\n }\n\n // Spec: AIT-CT13c\n select(msgId: string, index: number): void {\n this._logger.debug('ConversationTree.select();', { msgId, index });\n const group = this._getSiblingGroup(msgId);\n if (group.length <= 1) return;\n const groupRootId = this._getGroupRoot(msgId);\n this._selections.set(groupRootId, Math.max(0, Math.min(index, group.length - 1)));\n }\n\n getNode(msgId: string): ConversationNode<TMessage> | undefined {\n return this._nodeIndex.get(msgId)?.node;\n }\n\n getNodeByKey(key: string): ConversationNode<TMessage> | undefined {\n const msgId = this._codecKeyIndex.get(key);\n if (!msgId) return undefined;\n return this._nodeIndex.get(msgId)?.node;\n }\n\n getHeaders(msgId: string): Record<string, string> | undefined {\n return this._nodeIndex.get(msgId)?.node.headers;\n }\n\n // -------------------------------------------------------------------------\n // Mutation\n // -------------------------------------------------------------------------\n\n upsert(msgId: string, message: TMessage, headers: Record<string, string>, serial?: string): void {\n const parentId = headers[HEADER_PARENT] ?? undefined;\n const forkOf = headers[HEADER_FORK_OF] ?? undefined;\n\n // Maintain codec key → msgId secondary index\n this._codecKeyIndex.set(this._getKey(message), msgId);\n\n const existing = this._nodeIndex.get(msgId);\n if (existing) {\n // Update in place — message content may have changed (e.g. streaming).\n // Only update headers if the new headers are non-empty (prevents\n // streaming updates from erasing canonical headers).\n existing.node.message = message;\n if (Object.keys(headers).length > 0) {\n existing.node.headers = { ...headers };\n }\n // Spec: AIT-CT13d\n // Promote serial: optimistic (null) → server-assigned on relay.\n if (serial && !existing.node.serial) {\n this._logger.debug('ConversationTree.upsert(); promoting serial', { msgId, serial });\n existing.node.serial = serial;\n // Re-sort: remove from current position, re-insert at correct position.\n this._removeSorted(existing);\n this._insertSorted(existing);\n }\n return;\n }\n\n this._logger.trace('ConversationTree.upsert(); inserting new node', { msgId, parentId, forkOf });\n\n const node: ConversationNode<TMessage> = {\n message,\n msgId,\n parentId,\n forkOf,\n headers: { ...headers },\n serial,\n };\n\n const internal: InternalNode<TMessage> = { node, insertSeq: this._seqCounter++ };\n this._nodeIndex.set(msgId, internal);\n this._addToParentIndex(parentId, msgId);\n this._insertSorted(internal);\n }\n\n delete(msgId: string): void {\n const entry = this._nodeIndex.get(msgId);\n if (!entry) return;\n\n this._logger.debug('ConversationTree.delete();', { msgId });\n\n const { node } = entry;\n\n // Clean up secondary index\n const codecKey = this._getKey(node.message);\n if (this._codecKeyIndex.get(codecKey) === msgId) {\n this._codecKeyIndex.delete(codecKey);\n }\n\n // Remove from parent index\n this._removeFromParentIndex(node.parentId, msgId);\n\n // Remove from sorted list\n this._removeSorted(entry);\n\n // Remove from primary index\n this._nodeIndex.delete(msgId);\n this._selections.delete(msgId);\n\n // Children are NOT deleted — they become unreachable in flatten()\n // because their parent is no longer on the active path.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a ConversationTree that materializes branching history from a flat oplog.\n * @param getKey - Codec function that returns a stable key for a domain message.\n * @param logger - Logger for diagnostic output.\n * @returns A new {@link ConversationTree} instance.\n */\nexport const createConversationTree = <TMessage>(\n getKey: (message: TMessage) => string,\n logger: Logger,\n): ConversationTree<TMessage> => new DefaultConversationTree(getKey, logger);\n","/**\n * decodeHistory — load conversation history from an Ably channel and\n * return decoded messages as a PaginatedMessages result.\n *\n * Uses a fresh decoder (not shared with the live subscription) to avoid\n * state conflicts. Per-turn accumulators handle interleaved turns correctly.\n *\n * The `limit` option controls the number of **messages** returned,\n * not the number of Ably wire messages fetched. The implementation pages\n * back through Ably history until `limit` complete messages have\n * been assembled. Partial turns (incomplete at the page boundary) are\n * buffered internally and completed when `next()` fetches more pages.\n *\n * Only completed messages appear in `items`. A message is complete when\n * its terminal event (finish/abort/error) has been received.\n *\n * Because Ably history returns newest-first while the decoder requires\n * chronological order, all collected Ably messages are re-decoded from\n * oldest to newest after each page fetch. This handles turns that span\n * page boundaries correctly.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_MSG_ID, HEADER_TURN_ID } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { getHeaders } from '../../utils.js';\nimport type { Codec, DecoderOutput, MessageAccumulator } from '../codec/types.js';\nimport type { LoadHistoryOptions, PaginatedMessages } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Shared state across pages within one history traversal\n// ---------------------------------------------------------------------------\n\ninterface HistoryState<TEvent, TMessage> {\n codec: Codec<TEvent, TMessage>;\n /** All raw Ably messages collected so far, in newest-first order (as received from Ably). */\n rawMessages: Ably.InboundMessage[];\n /** How many completed messages have been returned to the consumer so far. */\n returnedCount: number;\n /** How many raw Ably messages have been returned 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 /** Key function for domain messages (codec.getMessageKey). */\n getMessageKey: (message: TMessage) => string;\n logger: Logger;\n}\n\n/** A completed message paired with its canonical wire headers and serial. */\ninterface DecodedItem<TMessage> {\n message: TMessage;\n headers: Record<string, string>;\n /** Ably serial from the first Ably message for this domain message. */\n serial: string;\n}\n\n// ---------------------------------------------------------------------------\n// Decode all collected messages from scratch (chronological order)\n// ---------------------------------------------------------------------------\n\n/**\n * Re-decode all collected raw messages into completed domain messages.\n * @param state - The shared history traversal state.\n * @returns Completed messages in newest-first order.\n */\nconst decodeAll = <TEvent, TMessage>(state: HistoryState<TEvent, TMessage>): DecodedItem<TMessage>[] => {\n // Reverse to chronological (oldest first)\n const chronological = [...state.rawMessages].toReversed();\n\n // Fresh decoder and per-turn accumulators for each full re-decode.\n const decoder = state.codec.createDecoder();\n const turns = new Map<\n string,\n {\n accumulator: MessageAccumulator<TEvent, TMessage>;\n firstSeen: number;\n /** Headers from the first Ably message per x-ably-msg-id within this turn. */\n msgHeaders: Map<string, Record<string, string>>;\n /** Ably serial from the first Ably message per x-ably-msg-id within this turn. */\n msgSerials: Map<string, string>;\n }\n >();\n const defaultAccumulator = state.codec.createAccumulator();\n let orderCounter = 0;\n\n // Headers for discrete messages (writeMessages output), keyed by codec message key.\n const discreteHeaders = new Map<string, Record<string, string>>();\n // Serials for discrete messages, keyed by codec message key.\n const discreteSerials = new Map<string, string>();\n\n for (const msg of chronological) {\n const outputs: DecoderOutput<TEvent, TMessage>[] = decoder.decode(msg);\n const headers = getHeaders(msg);\n const turnId = headers[HEADER_TURN_ID];\n const msgId = headers[HEADER_MSG_ID];\n const serial = msg.serial;\n\n if (turnId) {\n let turn = turns.get(turnId);\n if (!turn) {\n turn = {\n accumulator: state.codec.createAccumulator(),\n firstSeen: orderCounter++,\n msgHeaders: new Map(),\n msgSerials: new Map(),\n };\n turns.set(turnId, turn);\n }\n // Capture headers per msg-id within this turn. Update on later\n // messages too (e.g. closing append overrides status from\n // \"streaming\" to \"finished\"/\"aborted\"). Only merge when the\n // incoming message has non-empty headers.\n if (msgId) {\n const existing = turn.msgHeaders.get(msgId);\n if (!existing) {\n turn.msgHeaders.set(msgId, { ...headers });\n if (serial) turn.msgSerials.set(msgId, serial);\n } else if (Object.keys(headers).length > 0) {\n Object.assign(existing, headers);\n }\n }\n turn.accumulator.processOutputs(outputs);\n } else {\n defaultAccumulator.processOutputs(outputs);\n }\n\n // Capture headers and serial for discrete messages by codec key.\n for (const output of outputs) {\n if (output.kind === 'message') {\n const key = state.getMessageKey(output.message);\n const existingDiscrete = discreteHeaders.get(key);\n if (!existingDiscrete) {\n discreteHeaders.set(key, { ...headers });\n if (serial) discreteSerials.set(key, serial);\n } else if (Object.keys(headers).length > 0) {\n Object.assign(existingDiscrete, headers);\n }\n }\n }\n }\n\n // Collect completed messages in chronological order (oldest first) by turn.\n const completed: DecodedItem<TMessage>[] = [];\n\n for (const msg of defaultAccumulator.completedMessages) {\n const key = state.getMessageKey(msg);\n completed.push({\n message: msg,\n headers: discreteHeaders.get(key) ?? {},\n serial: discreteSerials.get(key) ?? '',\n });\n }\n\n const sorted = [...turns.values()].toSorted((a, b) => a.firstSeen - b.firstSeen);\n for (const turn of sorted) {\n // Assign headers and serials to each completed message in this turn.\n // Discrete messages were already captured by codec key. Accumulated\n // messages need to be matched to the turn's per-msg-id headers.\n const claimedMsgIds = new Set<string>();\n\n // First pass: resolve discrete messages and mark their msg-ids as claimed\n const turnKeyHeaders = new Map<string, Record<string, string>>();\n const turnKeySerials = new Map<string, string>();\n for (const msg of turn.accumulator.completedMessages) {\n const key = state.getMessageKey(msg);\n const discrete = discreteHeaders.get(key);\n if (discrete) {\n turnKeyHeaders.set(key, discrete);\n const dSerial = discreteSerials.get(key);\n if (dSerial) turnKeySerials.set(key, dSerial);\n const mid = discrete[HEADER_MSG_ID];\n if (mid) claimedMsgIds.add(mid);\n }\n }\n\n // Second pass: assign unclaimed msg-id entries to remaining messages\n const unclaimedEntries = [...turn.msgHeaders.entries()].filter(([mid]) => !claimedMsgIds.has(mid));\n let unclaimedIdx = 0;\n\n for (const msg of turn.accumulator.completedMessages) {\n const key = state.getMessageKey(msg);\n const unclaimed = unclaimedEntries[unclaimedIdx];\n if (!turnKeyHeaders.has(key) && unclaimed) {\n const [mid, hdrs] = unclaimed;\n turnKeyHeaders.set(key, hdrs);\n const mSerial = turn.msgSerials.get(mid);\n if (mSerial) turnKeySerials.set(key, mSerial);\n unclaimedIdx++;\n }\n }\n\n for (const msg of turn.accumulator.completedMessages) {\n const key = state.getMessageKey(msg);\n completed.push({\n message: msg,\n headers: turnKeyHeaders.get(key) ?? {},\n serial: turnKeySerials.get(key) ?? '',\n });\n }\n }\n\n // Reverse to newest-first. The consumer slices from the front for the\n // most recent page, and progressively deeper for older pages.\n return completed.toReversed();\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 * @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 <TEvent, TMessage>(\n state: HistoryState<TEvent, TMessage>,\n ablyPage: Ably.PaginatedResult<Ably.InboundMessage>,\n limit: number,\n): Promise<void> => {\n state.rawMessages.push(...ablyPage.items);\n state.lastAblyPage = ablyPage;\n\n let decodedCount = decodeAll(state).length;\n while (decodedCount < state.returnedCount + limit && ablyPage.hasNext()) {\n state.logger.debug('decodeHistory.fetchUntilLimit(); fetching next page', {\n collected: state.rawMessages.length,\n decoded: decodedCount,\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 decodedCount = decodeAll(state).length;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Build PaginatedMessages result from current state\n// ---------------------------------------------------------------------------\n\n/**\n * Build a PaginatedMessages page from the current decode state.\n * @param state - The shared history traversal state.\n * @param limit - Max messages per page.\n * @returns A page of decoded messages with a `next()` cursor.\n */\nconst buildResult = <TEvent, TMessage>(\n state: HistoryState<TEvent, TMessage>,\n limit: number,\n): PaginatedMessages<TMessage> => {\n // allCompleted is newest-first. Slice from returnedCount for this page,\n // then reverse to chronological for display.\n const allCompleted = decodeAll(state);\n\n const pageSlice = allCompleted.slice(state.returnedCount, state.returnedCount + limit);\n const chronSlice = [...pageSlice].toReversed();\n state.returnedCount += pageSlice.length;\n\n const moreCompleted = allCompleted.length > state.returnedCount;\n const moreAblyPages = state.lastAblyPage?.hasNext() ?? false;\n\n // Raw Ably messages for this page in chronological order.\n const newRawCount = state.rawMessages.length - state.returnedRawCount;\n const rawSlice = newRawCount > 0 ? state.rawMessages.slice(state.returnedRawCount).toReversed() : [];\n state.returnedRawCount = state.rawMessages.length;\n\n return {\n items: chronSlice.map((d) => d.message),\n itemHeaders: chronSlice.map((d) => d.headers),\n itemSerials: chronSlice.map((d) => d.serial),\n rawMessages: rawSlice,\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 decoded 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 codec - The codec for decoding wire messages into domain messages.\n * @param options - Pagination options.\n * @param logger - Logger for diagnostic output.\n * @returns The first page of decoded history messages.\n */\n// Spec: AIT-CT11, AIT-CT11b\nexport const decodeHistory = async <TEvent, TMessage>(\n channel: Ably.RealtimeChannel,\n codec: Codec<TEvent, TMessage>,\n options: LoadHistoryOptions | undefined,\n logger: Logger,\n): Promise<PaginatedMessages<TMessage>> => {\n const limit = options?.limit ?? 100;\n const state: HistoryState<TEvent, TMessage> = {\n codec,\n rawMessages: [],\n returnedCount: 0,\n returnedRawCount: 0,\n lastAblyPage: undefined,\n getMessageKey: codec.getMessageKey.bind(codec),\n logger,\n };\n\n logger.trace('decodeHistory();', { 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 * Transport header builder.\n *\n * Single source of truth for which `x-ably-*` headers every transport\n * message carries. Used by the server transport (addMessages, streamResponse)\n * and will be used by the client transport (optimistic message stamping).\n */\n\nimport {\n HEADER_FORK_OF,\n HEADER_MSG_ID,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_TURN_CLIENT_ID,\n HEADER_TURN_ID,\n} from '../../constants.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.turnId - Turn correlation ID.\n * @param opts.msgId - Message identity.\n * @param opts.turnClientId - ClientId of the turn initiator.\n * @param opts.parent - Preceding message's msg-id (for branching). Null means root.\n * @param opts.forkOf - Forked message's msg-id (for edit/regen).\n * @returns A headers record with the `x-ably-*` transport headers set.\n */\nexport const buildTransportHeaders = (opts: {\n role: string;\n turnId: string;\n msgId: string;\n turnClientId?: string;\n parent?: string | null;\n forkOf?: string;\n}): Record<string, string> => {\n const h: Record<string, string> = {\n [HEADER_ROLE]: opts.role,\n [HEADER_TURN_ID]: opts.turnId,\n [HEADER_MSG_ID]: opts.msgId,\n };\n if (opts.turnClientId !== undefined) h[HEADER_TURN_CLIENT_ID] = opts.turnClientId;\n if (opts.parent) h[HEADER_PARENT] = opts.parent;\n if (opts.forkOf) h[HEADER_FORK_OF] = opts.forkOf;\n return h;\n};\n","/**\n * Client-side stream routing.\n *\n * Maintains a map of turnId to ReadableStreamController. Routes decoded events\n * to the correct stream. Closes streams on terminal events or explicit close.\n */\n\nimport * as Ably from 'ably';\n\nimport { ErrorCode } from '../../errors.js';\nimport type { Logger } from '../../logger.js';\nimport type { TurnEntry } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\n/** Routes decoded events to the correct turn's ReadableStream. */\nexport interface StreamRouter<TEvent> {\n /** Register a new stream for a turnId. Returns the ReadableStream the consumer reads from. */\n createStream(turnId: string): ReadableStream<TEvent>;\n /** Close the stream for a turnId. Returns true if a stream was closed. */\n closeStream(turnId: string): boolean;\n /** Enqueue an event to the correct stream. Returns true if routed successfully. */\n route(turnId: string, event: TEvent): boolean;\n /** Whether a specific turnId has an active stream. */\n has(turnId: string): boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CT14\nclass DefaultStreamRouter<TEvent> implements StreamRouter<TEvent> {\n private readonly _turns = new Map<string, TurnEntry<TEvent>>();\n private readonly _isTerminal: (event: TEvent) => boolean;\n private readonly _logger: Logger;\n\n constructor(isTerminal: (event: TEvent) => boolean, logger: Logger) {\n this._isTerminal = isTerminal;\n this._logger = logger;\n }\n\n createStream(turnId: string): ReadableStream<TEvent> {\n this._logger.trace('StreamRouter.createStream();', { turnId });\n\n // Build stream+controller together. ReadableStream's start() runs synchronously\n // per spec, so the controller is captured before the constructor returns.\n const entry: { controller?: ReadableStreamDefaultController<TEvent> } = {};\n const stream = new ReadableStream<TEvent>({\n start(controller) {\n entry.controller = controller;\n },\n });\n if (!entry.controller) {\n throw new Ably.ErrorInfo(\n 'unable to create stream; ReadableStream start() was not called synchronously',\n ErrorCode.TransportSubscriptionError,\n 500,\n );\n }\n this._turns.set(turnId, { controller: entry.controller, turnId });\n return stream;\n }\n\n // Spec: AIT-CT14b\n closeStream(turnId: string): boolean {\n const turn = this._turns.get(turnId);\n if (!turn) return false;\n\n this._logger.debug('StreamRouter.closeStream(); closing stream', { turnId });\n try {\n turn.controller.close();\n } catch {\n /* already closed */\n }\n this._turns.delete(turnId);\n return true;\n }\n\n // Spec: AIT-CT14a\n route(turnId: string, event: TEvent): boolean {\n const turn = this._turns.get(turnId);\n if (!turn) return false;\n\n try {\n turn.controller.enqueue(event);\n } catch {\n this._turns.delete(turnId);\n return false;\n }\n\n if (this._isTerminal(event)) {\n this.closeStream(turnId);\n }\n return true;\n }\n\n has(turnId: string): boolean {\n return this._turns.has(turnId);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a StreamRouter that routes decoded events to per-turn ReadableStreams.\n * @param isTerminal - Predicate that returns true for events that close the stream.\n * @param logger - Logger for diagnostic output.\n * @returns A new {@link StreamRouter} instance.\n */\nexport const createStreamRouter = <TEvent>(\n isTerminal: (event: TEvent) => boolean,\n logger: Logger,\n): StreamRouter<TEvent> => new DefaultStreamRouter(isTerminal, logger);\n","/**\n * Core client-side transport, parameterized by codec.\n *\n * Composes StreamRouter and ConversationTree to handle the full client-side\n * lifecycle. Subscribes to the Ably channel on construction. The same\n * subscription, decoder, and channel are reused across turns.\n *\n * The client never publishes user messages directly. Instead, it sends them\n * to the server via HTTP POST. The server publishes user messages and turn\n * lifecycle events (turn-start, turn-end) on behalf of the client.\n */\n\nimport * as Ably from 'ably';\n\nimport {\n EVENT_CANCEL,\n EVENT_TURN_END,\n EVENT_TURN_START,\n HEADER_CANCEL_ALL,\n HEADER_CANCEL_CLIENT_ID,\n HEADER_CANCEL_OWN,\n HEADER_CANCEL_TURN_ID,\n HEADER_MSG_ID,\n HEADER_PARENT,\n HEADER_ROLE,\n HEADER_TURN_CLIENT_ID,\n HEADER_TURN_ID,\n HEADER_TURN_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 { getHeaders } from '../../utils.js';\nimport type { DecoderOutput, MessageAccumulator, StreamDecoder } from '../codec/types.js';\nimport { createConversationTree } from './conversation-tree.js';\nimport { decodeHistory } from './decode-history.js';\nimport { buildTransportHeaders } from './headers.js';\nimport type { StreamRouter } from './stream-router.js';\nimport { createStreamRouter } from './stream-router.js';\nimport type {\n ActiveTurn,\n CancelFilter,\n ClientTransport,\n ClientTransportOptions,\n CloseOptions,\n ConversationTree,\n LoadHistoryOptions,\n MessageWithHeaders,\n PaginatedMessages,\n SendOptions,\n TurnEndReason,\n TurnLifecycleEvent,\n} from './types.js';\n\n/**\n * Returned from `on()` when the transport 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// Event map for the transport's typed EventEmitter\n// ---------------------------------------------------------------------------\n\ninterface ClientTransportEventsMap {\n message: undefined;\n turn: TurnLifecycleEvent;\n error: Ably.ErrorInfo;\n 'ably-message': undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Per-turn observer state — consolidated to avoid parallel-map bookkeeping\n// ---------------------------------------------------------------------------\n\ninterface TurnObserverState<TEvent, TMessage> {\n headers: Record<string, string>;\n serial: string | undefined;\n accumulator: MessageAccumulator<TEvent, TMessage>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CT1\nclass DefaultClientTransport<TEvent, TMessage> implements ClientTransport<TEvent, TMessage> {\n private readonly _channel: Ably.RealtimeChannel;\n private readonly _codec: ClientTransportOptions<TEvent, TMessage>['codec'];\n private readonly _clientId: string | undefined;\n private readonly _api: string;\n private readonly _credentials: RequestCredentials | undefined;\n private readonly _headersFn: (() => Record<string, string>) | undefined;\n private readonly _bodyFn: (() => Record<string, unknown>) | undefined;\n private readonly _fetchFn: typeof globalThis.fetch;\n private readonly _logger: Logger;\n\n // Typed event emitter for all transport events\n private readonly _emitter: EventEmitter<ClientTransportEventsMap>;\n\n // Relay detection — tracks msg-ids of optimistic inserts for reconciliation\n private readonly _ownMsgIds = new Set<string>();\n private readonly _ownTurnIds = new Set<string>();\n\n // Track clientId per turn for getActiveTurnIds()\n private readonly _turnClientIds = new Map<string, string>();\n // Track msgIds per turn for cleanup on turn-end\n private readonly _turnMsgIds = new Map<string, Set<string>>();\n\n // Per-turn observer state: headers, serial, and accumulator in one map.\n // A single .delete(turnId) cleans up all three.\n private readonly _turnObservers = new Map<string, TurnObserverState<TEvent, TMessage>>();\n\n // Raw Ably message log\n private readonly _ablyMessages: Ably.InboundMessage[] = [];\n\n // History pagination: withheld messages hidden from getMessages()\n private readonly _withheldKeys = new Set<string>();\n\n // Sub-components\n private readonly _tree: ConversationTree<TMessage>;\n private readonly _router: StreamRouter<TEvent>;\n private readonly _decoder: StreamDecoder<TEvent, TMessage>;\n\n // Channel subscription — subscribe() returns a Promise that resolves when the channel attaches\n private readonly _attachPromise: Promise<unknown>;\n private readonly _onMessage: (msg: Ably.InboundMessage) => void;\n\n private _closed = false;\n\n constructor(options: ClientTransportOptions<TEvent, TMessage>) {\n this._channel = options.channel;\n this._codec = options.codec;\n this._clientId = options.clientId;\n this._api = options.api ?? '/api/chat';\n this._credentials = options.credentials;\n this._headersFn =\n typeof options.headers === 'function'\n ? options.headers\n : options.headers\n ? () => options.headers as Record<string, string>\n : undefined;\n this._bodyFn =\n typeof options.body === 'function'\n ? options.body\n : options.body\n ? () => options.body as Record<string, unknown>\n : undefined;\n this._fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n this._logger = (options.logger ?? makeLogger({ logLevel: LogLevel.Silent })).withContext({\n component: 'ClientTransport',\n });\n\n this._emitter = new EventEmitter<ClientTransportEventsMap>(this._logger);\n\n // Compose sub-components\n this._tree = createConversationTree<TMessage>(this._codec.getMessageKey.bind(this._codec), this._logger);\n this._router = createStreamRouter<TEvent>(this._codec.isTerminal.bind(this._codec), this._logger);\n this._decoder = this._codec.createDecoder();\n\n // Seed tree with initial messages\n if (options.messages) {\n let prevMsgId: string | undefined;\n for (const msg of options.messages) {\n const msgId = this._codec.getMessageKey(msg);\n const seedHeaders: Record<string, string> = {};\n if (prevMsgId) seedHeaders[HEADER_PARENT] = prevMsgId;\n this._tree.upsert(msgId, msg, seedHeaders);\n prevMsgId = msgId;\n }\n this._emitter.emit('message');\n }\n\n // Spec: AIT-CT2\n // Subscribe before attach (RTL7g)\n this._onMessage = (ablyMessage: Ably.InboundMessage) => {\n this._handleMessage(ablyMessage);\n };\n this._attachPromise = this._channel.subscribe(this._onMessage);\n }\n\n // ---------------------------------------------------------------------------\n // Message subscription handler\n // ---------------------------------------------------------------------------\n\n private _handleMessage(ablyMessage: Ably.InboundMessage): void {\n if (this._closed) return;\n\n this._ablyMessages.push(ablyMessage);\n this._emitter.emit('ably-message');\n\n try {\n // Spec: AIT-CT16a\n // --- Turn lifecycle events from the server ---\n if (ablyMessage.name === EVENT_TURN_START) {\n const headers = getHeaders(ablyMessage);\n const turnId = headers[HEADER_TURN_ID];\n const turnCid = headers[HEADER_TURN_CLIENT_ID] ?? '';\n if (turnId) {\n this._turnClientIds.set(turnId, turnCid);\n this._emitter.emit('turn', { type: EVENT_TURN_START, turnId, clientId: turnCid });\n }\n return;\n }\n\n if (ablyMessage.name === EVENT_TURN_END) {\n const headers = getHeaders(ablyMessage);\n const turnId = headers[HEADER_TURN_ID];\n const turnCid = headers[HEADER_TURN_CLIENT_ID] ?? '';\n // CAST: server always writes a valid TurnEndReason; default to 'complete' for robustness\n const reason = (headers[HEADER_TURN_REASON] ?? 'complete') as TurnEndReason;\n if (turnId) {\n this._router.closeStream(turnId);\n this._turnObservers.delete(turnId);\n this._turnClientIds.delete(turnId);\n // Clean up per-turn relay-detection state\n const msgIds = this._turnMsgIds.get(turnId);\n if (msgIds) {\n for (const mid of msgIds) this._ownMsgIds.delete(mid);\n this._turnMsgIds.delete(turnId);\n }\n this._ownTurnIds.delete(turnId);\n this._emitter.emit('turn', { type: EVENT_TURN_END, turnId, clientId: turnCid, reason });\n }\n return;\n }\n\n // --- Codec-decoded messages ---\n const outputs = this._decoder.decode(ablyMessage);\n const headers = getHeaders(ablyMessage);\n const serial = ablyMessage.serial;\n\n // Always update observer headers, even when the decoder produces no outputs.\n // This ensures header transitions (e.g. x-ably-status: streaming → aborted)\n // are captured for events that the decoder suppresses (AIT-CD8: aborted\n // stream appends emit no events but still carry the updated status header).\n const turnId = headers[HEADER_TURN_ID];\n if (turnId) {\n this._updateTurnObserverHeaders(turnId, headers, serial);\n }\n\n for (const output of outputs) {\n if (output.kind === 'message') {\n this._handleMessageOutput(output.message, headers, serial, ablyMessage.action);\n } else {\n this._handleEventOutput(output, headers);\n }\n }\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.TransportSubscriptionError,\n 500,\n cause,\n ),\n );\n }\n }\n\n /**\n * Handle a decoded domain message (user message create or relayed own message).\n * @param message - The decoded domain message.\n * @param headers - Ably headers from the wire message.\n * @param serial - Ably serial for tree ordering.\n * @param action - Ably message action (e.g. 'message.create').\n */\n private _handleMessageOutput(\n message: TMessage,\n headers: Record<string, string>,\n serial: string | undefined,\n action: string | undefined,\n ): void {\n // Spec: AIT-CT15\n const msgId = headers[HEADER_MSG_ID];\n if (msgId && this._ownMsgIds.has(msgId)) {\n // Relayed own message — reconcile optimistic entry with server-assigned fields\n this._upsertAndNotify(message, headers, serial);\n return;\n }\n\n if (action === 'message.create') {\n this._upsertAndNotify(message, headers, serial);\n }\n }\n\n /**\n * Handle a decoded streaming event: route to own-turn stream or accumulate for observer.\n * @param output - The decoded event output from the codec.\n * @param headers - Ably headers from the wire message.\n */\n private _handleEventOutput(output: DecoderOutput<TEvent, TMessage>, headers: Record<string, string>): void {\n if (output.kind !== 'event') return;\n const event = output.event;\n const turnId = headers[HEADER_TURN_ID];\n if (!turnId) return;\n\n // Observer headers are already updated in _handleMessage (before outputs\n // are iterated) so that header transitions are captured even when the\n // decoder produces no outputs (e.g. aborted stream appends per AIT-CD8).\n\n // Active own turn — route to the ReadableStream\n if (this._router.route(turnId, event)) {\n this._accumulateAndEmit(turnId, output);\n if (this._codec.isTerminal(event)) this._turnObservers.delete(turnId);\n return;\n }\n\n // Completed own turn — late arrival, skip\n if (this._ownTurnIds.has(turnId) && !this._turnObservers.has(turnId)) return;\n\n // Spec: AIT-CT16\n // Observer turn — accumulate and emit\n this._accumulateAndEmit(turnId, output);\n if (this._codec.isTerminal(event)) this._turnObservers.delete(turnId);\n }\n\n // ---------------------------------------------------------------------------\n // Tree mutation + notification helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Upsert a message into the tree and notify subscribers.\n * @param message - The domain message to insert or update.\n * @param headers - Ably headers for the message.\n * @param serial - Ably serial for tree ordering.\n */\n private _upsertAndNotify(message: TMessage, headers: Record<string, string>, serial?: string): void {\n const key = this._codec.getMessageKey(message);\n const msgId = headers[HEADER_MSG_ID] ?? key;\n this._tree.upsert(msgId, message, headers, serial);\n this._emitter.emit('message');\n }\n\n // ---------------------------------------------------------------------------\n // Observer accumulation\n // ---------------------------------------------------------------------------\n\n /**\n * Ensure a TurnObserverState exists for turnId, updating headers and serial as new events arrive.\n * @param turnId - The turn to track.\n * @param headers - Headers from the current event.\n * @param serial - Ably serial from the current event.\n */\n private _updateTurnObserverHeaders(\n turnId: string,\n headers: Record<string, string>,\n serial: string | undefined,\n ): void {\n const existing = this._turnObservers.get(turnId);\n if (existing) {\n if (Object.keys(headers).length > 0) {\n Object.assign(existing.headers, headers);\n }\n // Always advance the serial so the tree node sorts after all\n // earlier messages in the turn (e.g. user-message relays that\n // arrive before the assistant response).\n if (serial !== undefined) {\n existing.serial = serial;\n }\n } else {\n this._turnObservers.set(turnId, {\n headers: { ...headers },\n serial,\n accumulator: this._codec.createAccumulator(),\n });\n }\n }\n\n /**\n * Process a streaming event through the turn's accumulator and emit the latest message.\n * @param turnId - The turn this event belongs to.\n * @param output - The decoded event output to accumulate.\n */\n private _accumulateAndEmit(turnId: string, output: DecoderOutput<TEvent, TMessage>): void {\n const observer = this._turnObservers.get(turnId);\n if (!observer) return;\n\n observer.accumulator.processOutputs([output]);\n\n const messages = observer.accumulator.messages;\n if (messages.length === 0) return;\n\n let message: TMessage | undefined;\n try {\n message = structuredClone(messages.at(-1));\n } catch {\n // CAST: structuredClone can fail if the message contains non-cloneable\n // values (e.g. functions). Fall back to the reference — the tree upsert\n // below copies headers independently, so shared message state is the\n // only risk. Accumulator messages are replaced on each event, so\n // mutation between events is not a practical concern.\n message = messages.at(-1);\n }\n\n if (message) {\n this._tree.upsert(\n observer.headers[HEADER_MSG_ID] ?? this._codec.getMessageKey(message),\n message,\n { ...observer.headers },\n observer.serial,\n );\n this._emitter.emit('message');\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cancel helpers\n // ---------------------------------------------------------------------------\n\n private async _publishCancel(filter: CancelFilter): Promise<void> {\n this._logger.trace('ClientTransport._publishCancel();', { filter });\n\n const headers: Record<string, string> = {};\n if (filter.turnId) {\n headers[HEADER_CANCEL_TURN_ID] = filter.turnId;\n } else if (filter.own) {\n headers[HEADER_CANCEL_OWN] = 'true';\n } else if (filter.clientId) {\n headers[HEADER_CANCEL_CLIENT_ID] = filter.clientId;\n } else if (filter.all) {\n headers[HEADER_CANCEL_ALL] = 'true';\n }\n\n await this._channel.publish({\n name: EVENT_CANCEL,\n extras: { headers },\n });\n }\n\n private _closeMatchingTurnStreams(filter: CancelFilter): void {\n // Only close the router streams here — do NOT clear _turnObservers.\n // The observer must remain alive so that late server events (e.g. abort,\n // x-ably-status: aborted) arriving before turn-end are still accumulated\n // into the message store. The turn-end handler cleans up observers.\n if (filter.all) {\n for (const turnId of this._ownTurnIds) {\n this._router.closeStream(turnId);\n }\n } else if (filter.own) {\n for (const tid of this._ownTurnIds) {\n this._router.closeStream(tid);\n }\n } else if (filter.clientId) {\n for (const [tid, cid] of this._turnClientIds) {\n if (cid === filter.clientId) {\n this._router.closeStream(tid);\n }\n }\n } else if (filter.turnId) {\n this._router.closeStream(filter.turnId);\n }\n }\n\n private _getMatchingTurnIds(filter: CancelFilter): Set<string> {\n const matched = new Set<string>();\n if (filter.all) {\n for (const turnId of this._turnClientIds.keys()) matched.add(turnId);\n } else if (filter.own) {\n for (const [turnId, cid] of this._turnClientIds) {\n if (cid === this._clientId) matched.add(turnId);\n }\n } else if (filter.clientId) {\n for (const [turnId, cid] of this._turnClientIds) {\n if (cid === filter.clientId) matched.add(turnId);\n }\n } else if (filter.turnId && this._turnClientIds.has(filter.turnId)) {\n matched.add(filter.turnId);\n }\n return matched;\n }\n\n // ---------------------------------------------------------------------------\n // Input message helpers\n // ---------------------------------------------------------------------------\n\n private _getMessagesWithHeaders(): MessageWithHeaders<TMessage>[] {\n return this._tree.flatten().map((m) => ({\n message: m,\n headers: this.getMessageHeaders(m),\n }));\n }\n\n /**\n * Compute truncated history: everything before the target message.\n * Used by regenerate so the LLM doesn't see the response being replaced.\n * @param messageId - The msg-id to truncate history before.\n * @returns Input messages preceding the target.\n */\n private _getHistoryBefore(messageId: string): MessageWithHeaders<TMessage>[] {\n const all = this._getMessagesWithHeaders();\n const idx = all.findIndex((inp) => inp.headers?.[HEADER_MSG_ID] === messageId);\n return idx === -1 ? all : all.slice(0, idx);\n }\n\n // ---------------------------------------------------------------------------\n // History pagination helpers\n // ---------------------------------------------------------------------------\n\n private _processHistoryPage(page: PaginatedMessages<TMessage>): void {\n for (const [i, message] of page.items.entries()) {\n const headers = page.itemHeaders?.[i] ?? {};\n const serial = page.itemSerials?.[i];\n const key = this._codec.getMessageKey(message);\n const msgId = headers[HEADER_MSG_ID] ?? key;\n this._tree.upsert(msgId, message, headers, serial);\n }\n this._emitter.emit('message');\n\n // Prepend raw Ably messages (older messages go at the beginning)\n if (page.rawMessages && page.rawMessages.length > 0) {\n this._ablyMessages.unshift(...page.rawMessages);\n this._emitter.emit('ably-message');\n }\n }\n\n private async _loadUntilVisible(\n firstPage: PaginatedMessages<TMessage>,\n target: number,\n beforeKeys: Set<string>,\n ): Promise<{ newVisible: TMessage[]; lastPage: PaginatedMessages<TMessage> }> {\n this._processHistoryPage(firstPage);\n let page = firstPage;\n\n const newVisibleCount = (): number => {\n let count = 0;\n for (const m of this._tree.flatten()) {\n if (!beforeKeys.has(this._codec.getMessageKey(m))) count++;\n }\n return count;\n };\n\n while (newVisibleCount() < target && page.hasNext()) {\n const nextPage = await page.next();\n if (!nextPage) break;\n this._processHistoryPage(nextPage);\n page = nextPage;\n }\n\n const newVisible = this._tree.flatten().filter((m) => !beforeKeys.has(this._codec.getMessageKey(m)));\n return { newVisible, lastPage: page };\n }\n\n private _releaseWithheld(messages: TMessage[]): void {\n for (const m of messages) {\n this._withheldKeys.delete(this._codec.getMessageKey(m));\n }\n if (messages.length > 0) {\n this._emitter.emit('message');\n }\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n // Spec: AIT-CT3, AIT-CT4\n async send(input: TMessage | TMessage[], sendOptions?: SendOptions): Promise<ActiveTurn<TEvent>> {\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to send; transport is closed', ErrorCode.TransportClosed, 400);\n }\n await this._attachPromise;\n // CAST: re-check after await — close() may have been called while waiting for attach.\n // TypeScript's control flow narrows _closed to false after the first check, but the\n // await yields and close() can mutate _closed concurrently.\n if (this._closed as boolean) {\n throw new Ably.ErrorInfo('unable to send; transport is closed', ErrorCode.TransportClosed, 400);\n }\n\n this._logger.trace('ClientTransport.send();');\n\n const msgs = Array.isArray(input) ? input : [input];\n const turnId = crypto.randomUUID();\n this._ownTurnIds.add(turnId);\n\n const msgIds = new Set<string>();\n const postMessages: { message: TMessage; headers: Record<string, string> }[] = [];\n\n // Capture history BEFORE optimistic inserts. The optimistic messages are\n // sent in the `messages` field — including them in `history` too would\n // cause the server to see them twice.\n const preInsertHistory = this._getMessagesWithHeaders();\n\n // Spec: AIT-CT3d\n // Auto-compute parent from the current thread if not explicitly provided\n let autoParent: string | undefined;\n if (sendOptions?.parent === undefined && !sendOptions?.forkOf) {\n const flat = this._tree.flatten();\n if (flat.length > 0) {\n const lastMsg = flat.at(-1);\n if (lastMsg) {\n const lastKey = this._codec.getMessageKey(lastMsg);\n const lastNode = this._tree.getNodeByKey(lastKey);\n autoParent = lastNode?.msgId ?? lastKey;\n }\n }\n }\n\n // Capture the first parent for the POST body before the loop advances it.\n const postParent = sendOptions?.parent === undefined ? autoParent : sendOptions.parent;\n\n for (const message of msgs) {\n const msgId = crypto.randomUUID();\n this._ownMsgIds.add(msgId);\n msgIds.add(msgId);\n\n const resolvedParent = sendOptions?.parent === undefined ? autoParent : (sendOptions.parent ?? undefined);\n\n const optimisticHeaders = buildTransportHeaders({\n role: 'user',\n turnId,\n msgId,\n turnClientId: this._clientId,\n parent: resolvedParent,\n forkOf: sendOptions?.forkOf,\n });\n // Spec: AIT-CT3c\n // Optimistically insert each user message into the tree\n this._upsertAndNotify(message, optimisticHeaders);\n\n // Include per-message parent so the server chains messages correctly.\n const postHeaders: Record<string, string> = { [HEADER_MSG_ID]: msgId, [HEADER_ROLE]: 'user' };\n if (resolvedParent) postHeaders[HEADER_PARENT] = resolvedParent;\n postMessages.push({ message, headers: postHeaders });\n\n // Spec: AIT-CT3e\n // Chain: each subsequent message in the batch parents off the previous\n // one, forming a linear conversation thread rather than siblings.\n if (sendOptions?.parent === undefined && !sendOptions?.forkOf) {\n autoParent = msgId;\n }\n }\n\n this._turnMsgIds.set(turnId, msgIds);\n\n // Create ReadableStream via router\n const stream = this._router.createStream(turnId);\n\n // Resolve headers and body\n const resolvedHeaders = this._headersFn?.() ?? {};\n const resolvedBody = this._bodyFn?.() ?? {};\n\n const postBody: Record<string, unknown> = {\n ...resolvedBody,\n history: preInsertHistory,\n ...sendOptions?.body,\n turnId,\n clientId: this._clientId,\n messages: postMessages,\n ...(sendOptions?.forkOf !== undefined && { forkOf: sendOptions.forkOf }),\n ...(postParent !== undefined && { parent: postParent }),\n };\n\n const postHeaders: Record<string, string> = {\n ...resolvedHeaders,\n ...sendOptions?.headers,\n };\n\n // Spec: AIT-CT3a, AIT-CT3b\n // Fire-and-forget: POST must not block the stream return to the caller.\n // .catch() is intentional — async/await would delay stream availability.\n this._fetchFn(this._api, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...postHeaders,\n },\n body: JSON.stringify(postBody),\n ...(this._credentials ? { credentials: this._credentials } : {}),\n })\n .then((response) => {\n if (!response.ok) {\n this._emitter.emit(\n 'error',\n new Ably.ErrorInfo(\n `unable to send; HTTP POST to ${this._api} returned ${String(response.status)} ${response.statusText}`,\n ErrorCode.TransportSendFailed,\n response.status,\n ),\n );\n this._router.closeStream(turnId);\n }\n })\n .catch((error: unknown) => {\n const cause = error instanceof Ably.ErrorInfo ? error : undefined;\n this._emitter.emit(\n 'error',\n new Ably.ErrorInfo(\n `unable to send; HTTP POST to ${this._api} failed: ${error instanceof Error ? error.message : String(error)}`,\n ErrorCode.TransportSendFailed,\n 500,\n cause,\n ),\n );\n this._router.closeStream(turnId);\n });\n\n return {\n stream,\n turnId,\n cancel: async () => this.cancel({ turnId }),\n };\n }\n\n // Spec: AIT-CT5\n async regenerate(messageId: string, sendOptions?: SendOptions): Promise<ActiveTurn<TEvent>> {\n this._logger.trace('ClientTransport.regenerate();', { messageId });\n\n const node = this._tree.getNode(messageId);\n const parentId = node?.parentId;\n\n return this.send([], {\n ...sendOptions,\n body: {\n history: this._getHistoryBefore(messageId),\n ...sendOptions?.body,\n },\n forkOf: messageId,\n parent: parentId,\n });\n }\n\n // Spec: AIT-CT6\n async edit(\n messageId: string,\n newMessages: TMessage | TMessage[],\n sendOptions?: SendOptions,\n ): Promise<ActiveTurn<TEvent>> {\n this._logger.trace('ClientTransport.edit();', { messageId });\n\n const node = this._tree.getNode(messageId);\n const parentId = node?.parentId;\n\n return this.send(newMessages, {\n ...sendOptions,\n body: {\n history: this._getHistoryBefore(messageId),\n ...sendOptions?.body,\n },\n forkOf: messageId,\n parent: parentId,\n });\n }\n\n // Spec: AIT-CT7, AIT-CT7a\n async cancel(filter?: CancelFilter): Promise<void> {\n if (this._closed) return;\n const resolved = filter ?? { own: true };\n this._logger.debug('ClientTransport.cancel();', { filter: resolved });\n await this._publishCancel(resolved);\n this._closeMatchingTurnStreams(resolved);\n }\n\n // Spec: AIT-CT18\n async waitForTurn(filter?: CancelFilter): Promise<void> {\n if (this._closed) return;\n const resolved = filter ?? { own: true };\n const remaining = this._getMatchingTurnIds(resolved);\n if (remaining.size === 0) return;\n\n this._logger.debug('ClientTransport.waitForTurn();', { turnIds: [...remaining] });\n\n return new Promise<void>((resolve) => {\n const handler = (event: TurnLifecycleEvent): void => {\n if (event.type !== EVENT_TURN_END) return;\n remaining.delete(event.turnId);\n if (remaining.size === 0) {\n this._emitter.off('turn', handler);\n resolve();\n }\n };\n this._emitter.on('turn', handler);\n });\n }\n\n // Spec: AIT-CT8, AIT-CT8a, AIT-CT8b, AIT-CT8c, AIT-CT8d\n on(event: 'message' | 'ably-message', handler: () => void): () => void;\n on(event: 'turn', handler: (event: TurnLifecycleEvent) => void): () => void;\n on(event: 'error', handler: (error: Ably.ErrorInfo) => void): () => void;\n on(\n eventName: 'message' | 'turn' | 'error' | 'ably-message',\n handler: (() => void) | ((event: TurnLifecycleEvent) => void) | ((error: Ably.ErrorInfo) => void),\n ): () => void {\n if (this._closed) return noopUnsubscribe;\n // CAST: the overload signatures enforce correct handler types per event name.\n // The implementation must cast to satisfy the EventEmitter's generic callback type.\n const cb = handler as (arg: ClientTransportEventsMap[keyof ClientTransportEventsMap]) => void;\n this._emitter.on(eventName, cb);\n return () => {\n this._emitter.off(eventName, cb);\n };\n }\n\n // Spec: AIT-CT10\n getTree(): ConversationTree<TMessage> {\n return this._tree;\n }\n\n // Spec: AIT-CT17\n getActiveTurnIds(): Map<string, Set<string>> {\n const result = new Map<string, Set<string>>();\n for (const [turnId, cid] of this._turnClientIds) {\n let set = result.get(cid);\n if (!set) {\n set = new Set();\n result.set(cid, set);\n }\n set.add(turnId);\n }\n return result;\n }\n\n getMessageHeaders(message: TMessage): Record<string, string> | undefined {\n const key = this._codec.getMessageKey(message);\n return this._tree.getNodeByKey(key)?.headers;\n }\n\n // Spec: AIT-CT9\n getMessages(): TMessage[] {\n if (this._withheldKeys.size === 0) return this._tree.flatten();\n return this._tree.flatten().filter((m) => !this._withheldKeys.has(this._codec.getMessageKey(m)));\n }\n\n getMessagesWithHeaders(): MessageWithHeaders<TMessage>[] {\n return this._getMessagesWithHeaders();\n }\n\n getAblyMessages(): Ably.InboundMessage[] {\n return [...this._ablyMessages];\n }\n\n // Spec: AIT-CT11, AIT-CT11a, AIT-CT11b, AIT-CT11c\n async history(opts?: LoadHistoryOptions): Promise<PaginatedMessages<TMessage>> {\n if (this._closed) {\n throw new Ably.ErrorInfo('unable to load history; transport is closed', ErrorCode.TransportClosed, 400);\n }\n this._logger.trace('ClientTransport.history();', { limit: opts?.limit });\n const limit = opts?.limit ?? 100;\n\n // Snapshot before loading — everything already in the tree stays visible\n const beforeKeys = new Set(this._tree.flatten().map((m) => this._codec.getMessageKey(m)));\n\n let lastPage = await decodeHistory(this._channel, this._codec, opts, this._logger);\n\n const initial = await this._loadUntilVisible(lastPage, limit, beforeKeys);\n lastPage = initial.lastPage;\n\n // newVisible is chronological (oldest-first from flatten).\n // For \"load older\" pagination: release the NEWEST `limit` now,\n // withhold the older ones for subsequent next() calls.\n const newVisible = initial.newVisible;\n\n // Withhold ALL new visible messages first, then release the newest batch\n for (const m of newVisible) {\n this._withheldKeys.add(this._codec.getMessageKey(m));\n }\n\n const released = newVisible.slice(-limit);\n // Mutable buffer of older messages, drained newest-first by successive next() calls\n const withheldBuffer = newVisible.slice(0, -limit);\n this._releaseWithheld(released);\n\n const buildPage = (items: TMessage[]): PaginatedMessages<TMessage> => ({\n items,\n hasNext: () => withheldBuffer.length > 0 || lastPage.hasNext(),\n next: async () => {\n // Drain withheld buffer first (older messages, released newest-first)\n if (withheldBuffer.length > 0) {\n // Remove and return the newest `limit` items from the buffer\n const batch = withheldBuffer.splice(-limit, limit);\n this._releaseWithheld(batch);\n return buildPage(batch);\n }\n\n // Buffer exhausted — load more pages from decodeHistory\n if (!lastPage.hasNext()) return;\n\n const nextInternal = await lastPage.next();\n if (!nextInternal) return;\n\n // Everything currently in the tree is \"already known\"\n const alreadyKnown = new Set(beforeKeys);\n for (const m of this._tree.flatten()) {\n alreadyKnown.add(this._codec.getMessageKey(m));\n }\n\n const loaded = await this._loadUntilVisible(nextInternal, limit, alreadyKnown);\n lastPage = loaded.lastPage;\n\n const moreVisible = loaded.newVisible;\n for (const m of moreVisible) {\n this._withheldKeys.add(this._codec.getMessageKey(m));\n }\n // Remove and return the newest `limit` items; rest stays in buffer\n const moreBatch = moreVisible.splice(-limit, limit);\n withheldBuffer.push(...moreVisible);\n this._releaseWithheld(moreBatch);\n\n if (moreBatch.length === 0) return;\n return buildPage(moreBatch);\n },\n });\n\n return buildPage(released);\n }\n\n // Spec: AIT-CT12, AIT-CT12a, AIT-CT12b\n async close(options?: CloseOptions): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n this._logger.info('ClientTransport.close();');\n\n // Best-effort cancel publish before tearing down local state\n if (options?.cancel) {\n try {\n await this._publishCancel(options.cancel);\n } catch {\n // Swallow: cancel is best-effort during teardown\n }\n this._closeMatchingTurnStreams(options.cancel);\n }\n\n this._channel.unsubscribe(this._onMessage);\n\n // Close any remaining active streams\n for (const turnId of this._ownTurnIds) {\n this._router.closeStream(turnId);\n }\n\n this._turnObservers.clear();\n this._emitter.off();\n this._ownTurnIds.clear();\n this._ownMsgIds.clear();\n this._turnMsgIds.clear();\n this._turnClientIds.clear();\n this._withheldKeys.clear();\n this._ablyMessages.length = 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a client-side transport that manages conversation state over an Ably channel.\n *\n * Subscribes to the channel immediately (before attach per RTL7g). The caller should\n * ensure the channel is attached or will be attached shortly after creation.\n * @param options - Configuration for the client transport.\n * @returns A new {@link ClientTransport} instance.\n */\nexport const createClientTransport = <TEvent, TMessage>(\n options: ClientTransportOptions<TEvent, TMessage>,\n): ClientTransport<TEvent, TMessage> => new DefaultClientTransport(options);\n","/**\n * Vercel AI SDK Message Accumulator\n *\n * Builds and maintains a UIMessage[] list from decoder outputs.\n * Implements MessageAccumulator<UIMessageChunk, UIMessage>.\n *\n * The accumulator consumes DecoderOutput[] from the decoder and groups\n * streaming events into UIMessage objects using lifecycle boundaries\n * (start/finish). Complete messages (from writeMessages) are inserted\n * directly.\n *\n * Multiple messages can be in-progress concurrently — each is identified\n * by the `messageId` field on DecoderOutput (read from x-ably-msg-id).\n */\n\nimport type * as AI from 'ai';\n\nimport type { DecoderOutput, MessageAccumulator } from '../../core/codec/types.js';\nimport { stripUndefined } from '../../utils.js';\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\n/** Status of a streamed message (text, reasoning, or tool-input). */\ntype StreamStatus = 'streaming' | 'finished' | 'aborted';\n\n/**\n * Tracks an in-progress tool part's position and accumulated streaming input.\n * Text and reasoning parts don't need this — we write directly to the part.\n * Tool parts need the 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/** 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/** Bundled per-message state for an in-progress message. */\ninterface ActiveMessageState {\n message: AI.UIMessage;\n textStreams: DeltaStreamTracker;\n reasoningStreams: DeltaStreamTracker;\n toolTrackers: Record<string, ToolPartTracker>;\n streamStatus: Map<string, StreamStatus>;\n}\n\n// ---------------------------------------------------------------------------\n// Tool base helper\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 */\nconst 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// DeltaStreamTracker — manages text or reasoning stream accumulation\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks in-progress text or reasoning streams within a single message.\n * Owns the mapping from stream ID to part index, enforcing the pairing\n * of part type and index map by construction.\n */\nclass DeltaStreamTracker {\n private readonly _partType: 'text' | 'reasoning';\n private _activeIndex = new Map<string, number>();\n\n constructor(partType: 'text' | 'reasoning') {\n this._partType = partType;\n }\n\n start(id: string, msg: AI.UIMessage, streamStatus: Map<string, StreamStatus>): void {\n this._activeIndex.set(id, msg.parts.length);\n msg.parts.push({ type: this._partType, text: '' });\n streamStatus.set(id, 'streaming');\n }\n\n delta(id: string, msg: AI.UIMessage, text: string): void {\n const idx = this._activeIndex.get(id);\n if (idx === undefined) return;\n const part = msg.parts[idx];\n if (part?.type === this._partType) {\n part.text += text;\n }\n }\n\n end(id: string, streamStatus: Map<string, StreamStatus>): void {\n streamStatus.set(id, 'finished');\n this._activeIndex.delete(id);\n }\n\n reset(): void {\n this._activeIndex = new Map();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\nclass DefaultUIMessageAccumulator implements MessageAccumulator<AI.UIMessageChunk, AI.UIMessage> {\n private readonly _messageList: AI.UIMessage[] = [];\n private readonly _activeMessages = new Map<string, ActiveMessageState>();\n\n get messages(): AI.UIMessage[] {\n return this._messageList;\n }\n\n get completedMessages(): AI.UIMessage[] {\n const activeSet = new Set<AI.UIMessage>();\n for (const state of this._activeMessages.values()) {\n activeSet.add(state.message);\n }\n return this._messageList.filter((msg) => !activeSet.has(msg));\n }\n\n get hasActiveStream(): boolean {\n for (const state of this._activeMessages.values()) {\n for (const status of state.streamStatus.values()) {\n if (status === 'streaming') return true;\n }\n }\n return false;\n }\n\n processOutputs(outputs: DecoderOutput<AI.UIMessageChunk, AI.UIMessage>[]): void {\n for (const output of outputs) {\n if (output.kind === 'message') {\n this._messageList.push(output.message);\n } else if (output.messageId !== undefined) {\n this._processEvent(output.event, output.messageId);\n }\n }\n }\n\n updateMessage(message: AI.UIMessage): void {\n const idx = this._messageList.findIndex((m) => m.id === message.id);\n if (idx !== -1) {\n this._messageList[idx] = message;\n }\n }\n\n // -------------------------------------------------------------------------\n // Shared helpers\n // -------------------------------------------------------------------------\n\n private _ensureActiveMessage(messageId: string): ActiveMessageState {\n const existing = this._activeMessages.get(messageId);\n if (existing) return existing;\n\n const state: ActiveMessageState = {\n message: { id: messageId, role: 'assistant', parts: [] },\n textStreams: new DeltaStreamTracker('text'),\n reasoningStreams: new DeltaStreamTracker('reasoning'),\n toolTrackers: {},\n streamStatus: new Map(),\n };\n this._activeMessages.set(messageId, state);\n this._messageList.push(state.message);\n return state;\n }\n\n /**\n * Look up a tracked tool part by toolCallId within a message state.\n * @param toolCallId - The tool call identifier to look up.\n * @param state - The active message state to search in.\n * @returns The tracker and current part, or undefined if not found.\n */\n private _getToolPart(\n toolCallId: string,\n state: ActiveMessageState,\n ): { tracker: ToolPartTracker; part: AI.DynamicToolUIPart } | undefined {\n const tracker = state.toolTrackers[toolCallId];\n if (!tracker) return undefined;\n\n const existing = state.message.parts[tracker.partIndex];\n if (existing?.type !== 'dynamic-tool') return undefined;\n\n return { tracker, part: existing };\n }\n\n // -------------------------------------------------------------------------\n // Event dispatch\n // -------------------------------------------------------------------------\n\n private _processEvent(chunk: AI.UIMessageChunk, messageId: string): void {\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 this._processLifecycle(chunk, messageId);\n break;\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 this._processTextOrReasoning(chunk, messageId);\n break;\n }\n\n case 'tool-input-start':\n case 'tool-input-delta':\n case 'tool-input-available':\n case 'tool-input-error': {\n this._processToolInput(chunk, messageId);\n break;\n }\n\n case 'tool-output-available':\n case 'tool-output-error':\n case 'tool-output-denied':\n case 'tool-approval-request': {\n this._processToolOutput(chunk, messageId);\n break;\n }\n\n case 'file':\n case 'source-url':\n case 'source-document': {\n this._processContentPart(chunk, messageId);\n break;\n }\n\n default: {\n if (chunk.type.startsWith('data-')) {\n if (chunk.transient) break;\n\n const state = this._ensureActiveMessage(messageId);\n\n // CAST: chunk.type is `data-${string}` which satisfies DataUIPart,\n // but TypeScript cannot verify the template literal matches a\n // specific 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 = state.message.parts.findIndex((p) => p.type === chunk.type && 'id' in p && p.id === chunk.id);\n if (idx !== -1) {\n state.message.parts[idx] = dataPart;\n break;\n }\n }\n\n state.message.parts.push(dataPart);\n }\n break;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle events\n // -------------------------------------------------------------------------\n\n private _processLifecycle(\n chunk: Extract<\n AI.UIMessageChunk,\n { type: 'start' | 'start-step' | 'finish-step' | 'finish' | 'abort' | 'error' | 'message-metadata' }\n >,\n messageId: string,\n ): void {\n switch (chunk.type) {\n case 'start': {\n const state = this._ensureActiveMessage(messageId);\n if (chunk.messageId) state.message.id = chunk.messageId;\n if (chunk.messageMetadata !== undefined) {\n state.message.metadata = chunk.messageMetadata;\n }\n break;\n }\n\n case 'start-step': {\n const state = this._ensureActiveMessage(messageId);\n state.message.parts.push({ type: 'step-start' });\n break;\n }\n\n case 'finish-step': {\n const state = this._activeMessages.get(messageId);\n if (state) {\n state.textStreams.reset();\n state.reasoningStreams.reset();\n }\n break;\n }\n\n case 'finish': {\n const state = this._activeMessages.get(messageId);\n if (state && chunk.messageMetadata !== undefined) {\n state.message.metadata = chunk.messageMetadata;\n }\n this._activeMessages.delete(messageId);\n break;\n }\n\n case 'abort': {\n const state = this._activeMessages.get(messageId);\n if (state) {\n for (const [id, status] of state.streamStatus) {\n if (status === 'streaming') {\n state.streamStatus.set(id, 'aborted');\n }\n }\n }\n this._activeMessages.delete(messageId);\n break;\n }\n\n case 'error': {\n break;\n }\n\n case 'message-metadata': {\n const state = this._activeMessages.get(messageId);\n if (state && chunk.messageMetadata !== undefined) {\n state.message.metadata = chunk.messageMetadata;\n }\n break;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Text and reasoning streaming\n // -------------------------------------------------------------------------\n\n private _processTextOrReasoning(\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 ): void {\n const state = this._ensureActiveMessage(messageId);\n\n switch (chunk.type) {\n case 'text-start': {\n state.textStreams.start(chunk.id, state.message, state.streamStatus);\n break;\n }\n case 'text-delta': {\n state.textStreams.delta(chunk.id, state.message, chunk.delta);\n break;\n }\n case 'text-end': {\n state.textStreams.end(chunk.id, state.streamStatus);\n break;\n }\n case 'reasoning-start': {\n state.reasoningStreams.start(chunk.id, state.message, state.streamStatus);\n break;\n }\n case 'reasoning-delta': {\n state.reasoningStreams.delta(chunk.id, state.message, chunk.delta);\n break;\n }\n case 'reasoning-end': {\n state.reasoningStreams.end(chunk.id, state.streamStatus);\n break;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Tool input streaming\n // -------------------------------------------------------------------------\n\n private _processToolInput(\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 ): void {\n switch (chunk.type) {\n case 'tool-input-start': {\n const state = this._ensureActiveMessage(messageId);\n const partIndex = state.message.parts.length;\n state.message.parts.push({ ...toolBase(chunk), state: 'input-streaming', input: undefined });\n state.toolTrackers[chunk.toolCallId] = { partIndex, inputText: '' };\n state.streamStatus.set(chunk.toolCallId, 'streaming');\n break;\n }\n\n case 'tool-input-delta': {\n const state = this._ensureActiveMessage(messageId);\n const tracker = state.toolTrackers[chunk.toolCallId];\n if (!tracker) break;\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 = this._getToolPart(chunk.toolCallId, state);\n if (!found) break;\n state.message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'input-streaming',\n input: parsedInput,\n };\n break;\n }\n\n case 'tool-input-available': {\n const state = this._ensureActiveMessage(messageId);\n const found = this._getToolPart(chunk.toolCallId, state);\n if (!found) break;\n state.message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'input-available',\n input: chunk.input,\n };\n state.streamStatus.set(chunk.toolCallId, 'finished');\n break;\n }\n\n case 'tool-input-error': {\n const state = this._ensureActiveMessage(messageId);\n const found = this._getToolPart(chunk.toolCallId, state);\n if (found) {\n state.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 = state.message.parts.length;\n state.message.parts.push({\n ...toolBase(chunk),\n state: 'output-error',\n input: chunk.input,\n errorText: chunk.errorText,\n });\n state.toolTrackers[chunk.toolCallId] = { partIndex, inputText: '' };\n }\n state.streamStatus.set(chunk.toolCallId, 'finished');\n break;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Tool output transitions\n // -------------------------------------------------------------------------\n\n private _processToolOutput(\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 ): void {\n const state = this._ensureActiveMessage(messageId);\n const found = this._getToolPart(chunk.toolCallId, state);\n if (!found) return;\n\n switch (chunk.type) {\n case 'tool-output-available': {\n state.message.parts[found.tracker.partIndex] = stripUndefined({\n ...toolBase(found.part),\n state: 'output-available' as const,\n input: found.part.input,\n output: chunk.output,\n preliminary: chunk.preliminary,\n });\n break;\n }\n\n case 'tool-output-error': {\n state.message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'output-error',\n input: found.part.input,\n errorText: chunk.errorText,\n };\n break;\n }\n\n case 'tool-output-denied': {\n state.message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'output-denied',\n input: found.part.input,\n approval: { id: '', approved: false },\n };\n break;\n }\n\n case 'tool-approval-request': {\n state.message.parts[found.tracker.partIndex] = {\n ...toolBase(found.part),\n state: 'approval-requested',\n input: found.part.input,\n approval: { id: chunk.approvalId },\n };\n break;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Content parts\n // -------------------------------------------------------------------------\n\n private _processContentPart(\n chunk: Extract<AI.UIMessageChunk, { type: 'file' | 'source-url' | 'source-document' }>,\n messageId: string,\n ): void {\n const state = this._ensureActiveMessage(messageId);\n\n switch (chunk.type) {\n case 'file': {\n state.message.parts.push({ type: 'file', mediaType: chunk.mediaType, url: chunk.url });\n break;\n }\n\n case 'source-url': {\n state.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 break;\n }\n\n case 'source-document': {\n state.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 break;\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Vercel AI SDK accumulator that builds UIMessage[] from decoder outputs.\n * @returns A {@link MessageAccumulator} for UIMessageChunk/UIMessage.\n */\nexport const createAccumulator = (): MessageAccumulator<AI.UIMessageChunk, AI.UIMessage> =>\n new DefaultUIMessageAccumulator();\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.\n */\n\nimport type * as Ably from 'ably';\n\nimport { HEADER_MSG_ID, HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID } from '../../constants.js';\nimport type { Logger } from '../../logger.js';\nimport { getHeaders } from '../../utils.js';\nimport type { DecoderOutput, MessagePayload, StreamTrackerState } from './types.js';\n\n/**\n * Wrap a domain event as a single-element decoder output array.\n * @param event - The domain event to wrap.\n * @returns A single-element array containing the event as a decoder output.\n */\nexport const eventOutput = <TEvent, TMessage>(event: TEvent): DecoderOutput<TEvent, TMessage>[] => [\n { kind: 'event', event },\n];\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, TMessage> {\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): DecoderOutput<TEvent, TMessage>[];\n\n /** Build domain events for a text delta received on a stream. */\n buildDeltaEvents(tracker: StreamTrackerState, delta: string): DecoderOutput<TEvent, TMessage>[];\n\n /**\n * Build domain events emitted when a stream finishes (x-ably-status:finished).\n * Not called for aborted streams. The closing headers may differ from\n * tracker.headers if the closing append carried updated headers.\n */\n buildEndEvents(\n tracker: StreamTrackerState,\n closingHeaders: Record<string, string>,\n ): DecoderOutput<TEvent, TMessage>[];\n\n /**\n * Decode a discrete message (message.create where x-ably-stream is \"false\",\n * or a non-streamable first-contact update). Handles user messages, lifecycle\n * events, tool lifecycle, data-*, etc.\n */\n decodeDiscrete(input: MessagePayload): DecoderOutput<TEvent, TMessage>[];\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\n/** The decoder core returned by {@link createDecoderCore}. */\nexport interface DecoderCore<TEvent, TMessage> {\n /** Decode a single Ably message into zero or more domain outputs. */\n decode(message: Ably.InboundMessage): DecoderOutput<TEvent, TMessage>[];\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\n// Spec: AIT-CD7\nclass DefaultDecoderCore<TEvent, TMessage> implements DecoderCore<TEvent, TMessage> {\n private readonly _hooks: DecoderCoreHooks<TEvent, TMessage>;\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, TMessage>, 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): DecoderOutput<TEvent, TMessage>[] {\n const action = message.action;\n\n this._logger?.trace('DefaultDecoderCore.decode();', { action, serial: message.serial, name: message.name });\n\n let outputs: DecoderOutput<TEvent, TMessage>[];\n\n switch (action) {\n // Spec: AIT-CD7a\n case 'message.create': {\n const payload = this._toPayload(message);\n\n outputs =\n payload.headers?.[HEADER_STREAM] === 'true'\n ? this._decodeStreamedCreate(payload, message.serial)\n : this._hooks.decodeDiscrete(payload);\n break;\n }\n\n case 'message.append': {\n outputs = this._decodeAppend(message);\n break;\n }\n\n case 'message.update': {\n outputs = this._decodeUpdate(message);\n break;\n }\n\n case 'message.delete': {\n outputs = this._decodeDelete(message);\n break;\n }\n\n default: {\n return [];\n }\n }\n\n // Tag all event outputs with the message ID from x-ably-msg-id for accumulator correlation.\n const messageId = getHeaders(message)[HEADER_MSG_ID];\n if (messageId) {\n for (const output of outputs) {\n if (output.kind === 'event') {\n output.messageId = messageId;\n }\n }\n }\n\n return outputs;\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 headers: getHeaders(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(\n payload: MessagePayload,\n serial: string | undefined,\n ): DecoderOutput<TEvent, TMessage>[] {\n if (!serial) return [];\n\n const streamId = payload.headers?.[HEADER_STREAM_ID] ?? '';\n const h = payload.headers ?? {};\n\n const tracker: StreamTrackerState = {\n name: payload.name,\n streamId,\n accumulated: '',\n headers: { ...h },\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): DecoderOutput<TEvent, TMessage>[] {\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 h = getHeaders(message);\n const delta = typeof message.data === 'string' ? message.data : '';\n const status = h[HEADER_STATUS];\n const outputs: DecoderOutput<TEvent, TMessage>[] = [];\n\n if (delta.length > 0) {\n tracker.accumulated += delta;\n outputs.push(...this._hooks.buildDeltaEvents(tracker, delta));\n }\n\n if (status === 'finished' && !tracker.closed) {\n tracker.closed = true;\n outputs.push(...this._hooks.buildEndEvents(tracker, h));\n this._logger?.debug('DefaultDecoderCore._decodeAppend(); stream finished', { streamId: tracker.streamId });\n } else if (status === 'aborted' && !tracker.closed) {\n tracker.closed = true;\n this._logger?.debug('DefaultDecoderCore._decodeAppend(); stream aborted', { 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): DecoderOutput<TEvent, TMessage>[] {\n const serial = message.serial;\n if (!serial) return [];\n\n const payload = this._toPayload(message);\n const h = payload.headers ?? {};\n const isStreamed = h[HEADER_STREAM] === 'true';\n const status = h[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: DecoderOutput<TEvent, TMessage>[] = [];\n\n if (delta.length > 0) {\n tracker.accumulated = data;\n outputs.push(...this._hooks.buildDeltaEvents(tracker, delta));\n }\n\n if (status === 'finished' && !tracker.closed) {\n tracker.closed = true;\n outputs.push(...this._hooks.buildEndEvents(tracker, h));\n } else if (status === 'aborted' && !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.headers = { ...h };\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 ): DecoderOutput<TEvent, TMessage>[] {\n // Non-streamed messages are discrete\n if (!isStreamed) {\n return this._hooks.decodeDiscrete(payload);\n }\n\n const streamId = payload.headers?.[HEADER_STREAM_ID] ?? '';\n const h = payload.headers ?? {};\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 headers: { ...h },\n closed: status === 'finished' || status === 'aborted',\n };\n this._serialState.set(serial, newTracker);\n\n // Emit start + delta (if any) + end (if finished)\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 === 'finished') {\n outputs.push(...this._hooks.buildEndEvents(newTracker, h));\n }\n\n return outputs;\n }\n\n // -------------------------------------------------------------------------\n // Private: delete handling\n // -------------------------------------------------------------------------\n\n // Spec: AIT-CD10\n private _decodeDelete(message: Ably.InboundMessage): DecoderOutput<TEvent, TMessage>[] {\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, TMessage>(\n hooks: DecoderCoreHooks<TEvent, TMessage>,\n options: DecoderCoreOptions = {},\n): DecoderCore<TEvent, TMessage> => new DefaultDecoderCore(hooks, options);\n","/**\n * Generic lifecycle tracker for codec decoders.\n *\n * Manages per-scope (typically per-turn) 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 turn 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 * Returns synthetic events for any phases not yet marked as emitted,\n * then marks them. Returns an empty array if all phases are current.\n * @param scopeId - The scope to check (e.g. turn 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. turn 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. turn 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 turn completion\n * (finish, abort) 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 DecoderOutput<UIMessageChunk, UIMessage>[].\n *\n * Delegates action dispatch and serial tracking to the decoder core.\n * This file contains only the Vercel-specific event building, discrete\n * event decoding, and synthetic event emission.\n *\n * Domain-specific headers use the `x-domain-` prefix. Transport-level\n * headers use the `x-ably-` prefix.\n */\n\nimport type * as Ably from 'ably';\nimport type * as AI from 'ai';\n\nimport { HEADER_ROLE, HEADER_TURN_ID } from '../../constants.js';\nimport type { DecoderCore, DecoderCoreHooks, DecoderCoreOptions } from '../../core/codec/decoder.js';\nimport { createDecoderCore, eventOutput } from '../../core/codec/decoder.js';\nimport type { LifecycleTracker } from '../../core/codec/lifecycle-tracker.js';\nimport { createLifecycleTracker } from '../../core/codec/lifecycle-tracker.js';\nimport type { DecoderOutput, MessagePayload, StreamDecoder, StreamTrackerState } from '../../core/codec/types.js';\nimport { type DomainHeaderReader, headerReader as rawHeaderReader, stripUndefined } from '../../utils.js';\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 tool-input-error data payload. */\ninterface ToolInputErrorWireData {\n errorText?: string;\n input?: unknown;\n}\n\n/** Wire format for tool-output-available data payload. */\ninterface ToolOutputAvailableWireData {\n output?: unknown;\n}\n\n/** Wire format for tool-output-error data payload. */\ninterface ToolOutputErrorWireData {\n errorText?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Shared output type alias\n// ---------------------------------------------------------------------------\n\ntype Out = DecoderOutput<AI.UIMessageChunk, AI.UIMessage>;\n\n/**\n * Bind eventOutput to the Vercel domain types.\n * @param chunk - The UIMessageChunk to wrap.\n * @returns A single-element decoder output array.\n */\nconst event = (chunk: AI.UIMessageChunk): Out[] => eventOutput<AI.UIMessageChunk, AI.UIMessage>(chunk);\n\n// ---------------------------------------------------------------------------\n// JSON boundary helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a finish reason string against the FinishReason union.\n * @param value - The finish reason string from the wire, or undefined.\n * @param fallback - Default finish reason if the value is not recognized.\n * @returns The validated FinishReason.\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\n/**\n * Type predicate for data-* message names.\n * @param name - The message name to check.\n * @returns True if the name starts with \"data-\".\n */\nconst isDataEventName = (name: string): name is `data-${string}` => name.startsWith('data-');\n\n/**\n * Parse a string as JSON, returning the raw string if parsing fails\n * or undefined if empty.\n * @param value - The string to attempt JSON parsing on.\n * @returns The parsed value, the raw string on parse failure, or undefined if empty.\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\n// ---------------------------------------------------------------------------\n\nconst buildStartChunk = (tracker: StreamTrackerState): AI.UIMessageChunk => {\n const r = headerReader(tracker.headers);\n switch (tracker.name) {\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 (tracker.name) {\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 (tracker.name) {\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.headers).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)\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 * Run the lifecycle tracker and wrap results as DecoderOutput events.\n * @param lifecycle - The lifecycle tracker instance.\n * @param turnId - The turn scope ID.\n * @param context - Context passed through to phase build functions.\n * @returns Decoder outputs for any synthesized lifecycle events.\n */\nconst ensurePhases = (\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n turnId: string,\n context: Record<string, string | undefined>,\n): Out[] => lifecycle.ensurePhases(turnId, context).map((e) => ({ kind: 'event', event: e }));\n\n// ---------------------------------------------------------------------------\n// Discrete event decoders (one function per event type)\n// ---------------------------------------------------------------------------\n\nconst decodeStart = (r: VercelHeaderReader, turnId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): Out[] => {\n lifecycle.markEmitted(turnId, 'start');\n return event(\n stripUndefined({\n type: 'start' as const,\n messageId: r.str('messageId'),\n messageMetadata: r.json('messageMetadata'),\n }),\n );\n};\n\nconst decodeStartStep = (turnId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): Out[] => {\n lifecycle.markEmitted(turnId, 'start-step');\n return event({ type: 'start-step' });\n};\n\nconst decodeFinishStep = (turnId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): Out[] => {\n lifecycle.resetPhase(turnId, 'start-step');\n return event({ type: 'finish-step' });\n};\n\nconst decodeFinish = (r: VercelHeaderReader, turnId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): Out[] => {\n lifecycle.clearScope(turnId);\n return event(\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 = (data: unknown): Out[] => {\n const errorText = typeof data === 'string' ? data : '';\n return event({ type: 'error', errorText });\n};\n\nconst decodeAbort = (data: unknown, turnId: string, lifecycle: LifecycleTracker<AI.UIMessageChunk>): Out[] => {\n lifecycle.clearScope(turnId);\n const reason = typeof data === 'string' && data ? data : undefined;\n return event(stripUndefined({ type: 'abort' as const, reason }));\n};\n\nconst decodeMessageMetadata = (r: VercelHeaderReader): Out[] =>\n event({ type: 'message-metadata', messageMetadata: r.json('messageMetadata') });\n\nconst decodeFile = (r: VercelHeaderReader, data: unknown): Out[] =>\n event(\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): Out[] =>\n event(\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): Out[] =>\n event(\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): Out[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ToolInputErrorWireData | undefined;\n return event(\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 decodeToolOutputAvailable = (r: VercelHeaderReader, data: unknown): Out[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ToolOutputAvailableWireData | undefined;\n return event(\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 decodeToolOutputError = (r: VercelHeaderReader, data: unknown): Out[] => {\n // CAST: Trust boundary — encoder produced the expected object shape.\n const parsed = data as ToolOutputErrorWireData | undefined;\n return event(\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): Out[] =>\n event({\n type: 'tool-approval-request',\n toolCallId: r.strOr('toolCallId', ''),\n approvalId: r.strOr('approvalId', ''),\n });\n\nconst decodeToolOutputDenied = (r: VercelHeaderReader): Out[] =>\n event({ type: 'tool-output-denied', toolCallId: r.strOr('toolCallId', '') });\n\nconst decodeDataEvent = (name: `data-${string}`, r: VercelHeaderReader, data: unknown): Out[] =>\n event(\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\n// ---------------------------------------------------------------------------\n\nconst decodeNonStreamingToolInput = (\n r: VercelHeaderReader,\n data: unknown,\n turnId: string,\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): Out[] => {\n const outputs = ensurePhases(lifecycle, turnId, { messageId: r.str('messageId') });\n\n outputs.push(\n {\n kind: 'event',\n event: 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 },\n {\n kind: 'event',\n event: 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 return outputs;\n};\n\n// ---------------------------------------------------------------------------\n// Discrete event dispatch\n// ---------------------------------------------------------------------------\n\n/**\n * Reconstruct a UIMessage from a discrete message part published by writeMessages.\n * The encoder splits each UIMessage into per-part Ably messages with a shared\n * x-domain-messageId. This function rebuilds a single-part UIMessage from one\n * such Ably message. The transport's tree upsert merges parts that share the\n * same x-ably-msg-id, so multi-part messages accumulate correctly over\n * successive decoder calls.\n * @param input - The discrete message payload to decode.\n * @returns A single-element array with the reconstructed UIMessage, or empty if unrecognized.\n */\nconst decodeDiscreteMessage = (input: MessagePayload): Out[] => {\n const h = input.headers ?? {};\n const r = headerReader(h);\n const role = (h[HEADER_ROLE] ?? 'user') as AI.UIMessage['role'];\n const messageId = r.str('messageId') ?? '';\n\n let part: AI.UIMessage['parts'][number] | undefined;\n\n switch (input.name) {\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(input.name)) {\n // CAST: data-* part type matches the DataUIPart shape.\n part = stripUndefined({ type: input.name, id: r.str('id'), data: input.data }) as AI.UIMessage['parts'][number];\n }\n break;\n }\n }\n\n if (!part) return [];\n\n const message: AI.UIMessage = { id: messageId, role, parts: [part] };\n return [{ kind: 'message', message }];\n};\n\n/**\n * Whether a message name represents a discrete message part (written by writeMessages)\n * rather than a streaming lifecycle event. Discrete message parts carry x-ably-role\n * and encode a single UIMessage part each.\n * @param name - The Ably message name to check.\n * @param headers - The Ably message headers to inspect for role presence.\n * @returns True if this is a discrete message part, false if it's a lifecycle event.\n */\nconst isDiscreteMessagePart = (name: string, headers: Record<string, string>): boolean =>\n (name === 'text' || name === 'file' || isDataEventName(name)) && HEADER_ROLE in headers;\n\nconst decodeDiscretePayload = (input: MessagePayload, lifecycle: LifecycleTracker<AI.UIMessageChunk>): Out[] => {\n const h = input.headers ?? {};\n const r = headerReader(h);\n const turnId = h[HEADER_TURN_ID] ?? '';\n\n // Discrete message parts from writeMessages (user messages, history entries).\n // Distinguished from lifecycle events by the presence of x-ably-role.\n if (isDiscreteMessagePart(input.name, h)) {\n return decodeDiscreteMessage(input);\n }\n\n if (input.name === 'tool-input') {\n return decodeNonStreamingToolInput(r, input.data, turnId, lifecycle);\n }\n\n switch (input.name) {\n case 'start': {\n return decodeStart(r, turnId, lifecycle);\n }\n case 'start-step': {\n return decodeStartStep(turnId, lifecycle);\n }\n case 'finish-step': {\n return decodeFinishStep(turnId, lifecycle);\n }\n case 'finish': {\n return decodeFinish(r, turnId, lifecycle);\n }\n case 'error': {\n return decodeError(input.data);\n }\n case 'abort': {\n return decodeAbort(input.data, turnId, lifecycle);\n }\n case 'message-metadata': {\n return decodeMessageMetadata(r);\n }\n case 'file': {\n return decodeFile(r, input.data);\n }\n case 'source-url': {\n return decodeSourceUrl(r, input.data);\n }\n case 'source-document': {\n return decodeSourceDocument(r);\n }\n case 'tool-input-error': {\n return decodeToolInputError(r, input.data);\n }\n case 'tool-output-available': {\n return decodeToolOutputAvailable(r, input.data);\n }\n case 'tool-output-error': {\n return decodeToolOutputError(r, input.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(input.name) ? decodeDataEvent(input.name, r, input.data) : [];\n }\n }\n};\n\n// ---------------------------------------------------------------------------\n// Decoder core hooks\n// ---------------------------------------------------------------------------\n\nconst createHooks = (\n lifecycle: LifecycleTracker<AI.UIMessageChunk>,\n): DecoderCoreHooks<AI.UIMessageChunk, AI.UIMessage> => ({\n buildStartEvents: (tracker: StreamTrackerState): Out[] => {\n const turnId = tracker.headers[HEADER_TURN_ID] ?? '';\n const messageId = headerReader(tracker.headers).str('messageId');\n const outputs = ensurePhases(lifecycle, turnId, { messageId });\n outputs.push({ kind: 'event', event: buildStartChunk(tracker) });\n return outputs;\n },\n\n buildDeltaEvents: (tracker: StreamTrackerState, delta: string): Out[] => event(buildDeltaChunk(tracker, delta)),\n\n buildEndEvents: (tracker: StreamTrackerState, closingHeaders: Record<string, string>): Out[] =>\n event(buildEndChunk(tracker, closingHeaders)),\n\n decodeDiscrete: (payload: MessagePayload): Out[] => decodeDiscretePayload(payload, lifecycle),\n});\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\nclass DefaultUIMessageDecoder implements StreamDecoder<AI.UIMessageChunk, AI.UIMessage> {\n private readonly _core: DecoderCore<AI.UIMessageChunk, AI.UIMessage>;\n\n constructor(options: DecoderCoreOptions = {}) {\n this._core = createDecoderCore<AI.UIMessageChunk, AI.UIMessage>(\n createHooks(createVercelLifecycleTracker()),\n options,\n );\n }\n\n decode(message: Ably.InboundMessage): Out[] {\n return this._core.decode(message);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Vercel AI SDK decoder that maps Ably messages to UIMessageChunk\n * events and UIMessage objects via the decoder core.\n * @param options - Decoder configuration (callbacks, logger).\n * @returns A {@link StreamDecoder} for UIMessageChunk/UIMessage.\n */\nexport const createDecoder = (options: DecoderCoreOptions = {}): StreamDecoder<AI.UIMessageChunk, AI.UIMessage> =>\n new DefaultUIMessageDecoder(options);\n","/**\n * Encoder core — message append lifecycle machinery.\n *\n * Provides Ably primitives (publish, append, close, abort, 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 { HEADER_MSG_ID, HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID } 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 persistentHeaders: Record<string, string>;\n aborted: boolean;\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 x-ably-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} or {@link close}.\n */\n appendStream(streamId: string, data: string): void;\n\n /**\n * Close a streamed message with x-ably-status:finished. Flushes all pending\n * appends for recovery before returning. Repeats persistent and payload headers.\n */\n closeStream(streamId: string, payload: StreamPayload): Promise<void>;\n\n /**\n * Abort a single in-progress stream (x-ably-status:aborted) and flush all\n * pending appends for recovery before returning.\n */\n abortStream(streamId: string, opts?: WriteOptions): Promise<void>;\n\n /**\n * Abort all in-progress streams (x-ably-status:aborted) and flush all\n * pending appends for recovery before returning.\n */\n abortAllStreams(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));\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 allHeaders = this._buildHeaders(payload.headers ?? {}, opts);\n allHeaders[HEADER_STREAM] = 'true';\n allHeaders[HEADER_STATUS] = 'streaming';\n allHeaders[HEADER_STREAM_ID] = streamId;\n\n const clientId = this._resolveClientId(opts);\n const msg: Ably.Message = {\n name: payload.name,\n data: payload.data,\n extras: { headers: allHeaders },\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 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 persistentHeaders: allHeaders,\n aborted: 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 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: { headers: { ...tracker.persistentHeaders } },\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 allHeaders = this._buildClosingHeaders(tracker, payload.headers ?? {});\n allHeaders[HEADER_STATUS] = 'finished';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: payload.data,\n extras: { headers: allHeaders },\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 abortStream(streamId: string, opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.abortStream();', { streamId });\n\n const tracker = this._trackers.get(streamId);\n if (!tracker) {\n throw new Ably.ErrorInfo(\n `unable to abort stream; no active stream for streamId '${streamId}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n\n tracker.aborted = true;\n\n const allHeaders = this._buildClosingHeaders(tracker, {}, opts);\n allHeaders[HEADER_STATUS] = 'aborted';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: '',\n extras: { headers: allHeaders },\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.abortStream(); stream aborted', { streamId });\n }\n\n // Spec: AIT-CD5a\n async abortAllStreams(opts?: WriteOptions): Promise<void> {\n this._assertNotClosed();\n this._logger?.trace('DefaultEncoderCore.abortAllStreams();', { streamCount: this._trackers.size });\n\n for (const tracker of this._trackers.values()) {\n tracker.aborted = true;\n\n const allHeaders = this._buildClosingHeaders(tracker, {}, opts);\n allHeaders[HEADER_STATUS] = 'aborted';\n\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: '',\n extras: { headers: allHeaders },\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.aborted ? 'aborted' : 'finished';\n const msg: Ably.Message = {\n serial: tracker.serial,\n data: tracker.accumulated,\n extras: { headers: { ...tracker.persistentHeaders, [HEADER_STATUS]: recoveryStatus } },\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 private _buildHeaders(codecHeaders: Record<string, string>, opts?: WriteOptions): Record<string, string> {\n const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);\n const merged = { ...callerHeaders, ...codecHeaders };\n if (opts?.messageId !== undefined) {\n merged[HEADER_MSG_ID] = opts.messageId;\n }\n return merged;\n }\n\n private _buildDiscreteMessage(payload: MessagePayload, opts?: WriteOptions): Ably.Message {\n const headers = this._buildHeaders(payload.headers ?? {}, opts);\n headers[HEADER_STREAM] = 'false';\n const clientId = this._resolveClientId(opts);\n\n const msg: Ably.Message = {\n name: payload.name,\n data: payload.data,\n extras: {\n headers,\n ...(payload.ephemeral ? { ephemeral: true } : {}),\n },\n ...(clientId ? { clientId } : {}),\n };\n\n this._invokeOnMessage(msg);\n return msg;\n }\n\n /**\n * Build headers for a closing append. Closing appends must repeat ALL\n * 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 codecHeaders - Codec-layer headers to merge.\n * @param opts - Optional per-write overrides.\n * @returns Merged headers for the closing append.\n */\n private _buildClosingHeaders(\n tracker: StreamState,\n codecHeaders: Record<string, string>,\n opts?: WriteOptions,\n ): Record<string, string> {\n const h = { ...tracker.persistentHeaders };\n const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);\n Object.assign(h, callerHeaders);\n Object.assign(h, codecHeaders);\n return h;\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 * Maps UIMessageChunk events and complete UIMessage objects to Ably channel\n * operations (publish, appendMessage, updateMessage).\n *\n * Delegates the message append lifecycle (publish, append, close, abort,\n * flush/recover) to the encoder core. This file contains only the\n * Vercel-specific event-to-operation mapping.\n *\n * Domain-specific headers use the `x-domain-` prefix to distinguish them\n * from transport-level `x-ably-` headers.\n *\n * ## Core operations and domain headers\n *\n * Each UIMessageChunk maps to exactly one encoder core operation. Domain\n * headers are passed to every operation that accepts them — the core handles\n * merging, persistence, and deduplication:\n *\n * - **`startStream`**: Opens a message stream. Domain headers become\n * \"persistent headers\" — the core repeats them on every subsequent append.\n * - **`appendStream`**: Appends a text delta. Data only, no headers parameter.\n * The core automatically carries persistent headers from start.\n * - **`closeStream`**: Closes the stream. Pass all domain headers from the\n * chunk — the core merges them on top of persistent headers, so changed\n * values (e.g. updated providerMetadata) are picked up and unchanged\n * values are harmlessly deduplicated.\n * - **`publishDiscrete`**: Publishes a standalone message. All domain headers\n * for the chunk are passed directly.\n */\n\nimport * as Ably from 'ably';\nimport type * as AI from 'ai';\nimport { isDataUIPart } from 'ai';\n\nimport { HEADER_STATUS } from '../../constants.js';\nimport type { EncoderCore, EncoderCoreOptions } from '../../core/codec/encoder.js';\nimport { createEncoderCore } from '../../core/codec/encoder.js';\nimport type { ChannelWriter, MessagePayload, StreamEncoder, WriteOptions } from '../../core/codec/types.js';\nimport { ErrorCode, errorInfoIs } from '../../errors.js';\nimport { headerWriter } from '../../utils.js';\n\n// ---------------------------------------------------------------------------\n// Default implementation\n// ---------------------------------------------------------------------------\n\nclass DefaultUIMessageEncoder implements StreamEncoder<AI.UIMessageChunk, AI.UIMessage> {\n private readonly _core: EncoderCore;\n private _aborted = false;\n\n constructor(writer: ChannelWriter, options: EncoderCoreOptions = {}) {\n this._core = createEncoderCore(writer, options);\n }\n\n async appendEvent(chunk: AI.UIMessageChunk, perWrite?: WriteOptions): Promise<void> {\n switch (chunk.type) {\n // -- Stream start: open a message stream with persistent headers -------\n\n case 'text-start': {\n const h = headerWriter().str('id', chunk.id).json('providerMetadata', chunk.providerMetadata).build();\n await this._core.startStream(chunk.id, { name: 'text', data: '', headers: h }, perWrite);\n break;\n }\n\n case 'reasoning-start': {\n const h = headerWriter().str('id', chunk.id).json('providerMetadata', chunk.providerMetadata).build();\n await this._core.startStream(chunk.id, { name: 'reasoning', data: '', headers: h }, perWrite);\n break;\n }\n\n case 'tool-input-start': {\n const h = headerWriter()\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: 'tool-input', data: '', headers: h }, perWrite);\n break;\n }\n\n // -- Stream append: data only, core carries persistent headers --------\n\n case 'text-delta': {\n this._core.appendStream(chunk.id, chunk.delta);\n break;\n }\n\n case 'reasoning-delta': {\n this._core.appendStream(chunk.id, chunk.delta);\n break;\n }\n\n case 'tool-input-delta': {\n this._core.appendStream(chunk.toolCallId, chunk.inputTextDelta);\n break;\n }\n\n // -- Stream close: pass all chunk headers, core merges with persistent\n\n case 'text-end': {\n const h = headerWriter().str('id', chunk.id).json('providerMetadata', chunk.providerMetadata).build();\n await this._core.closeStream(chunk.id, { name: 'text', data: '', headers: h });\n break;\n }\n\n case 'reasoning-end': {\n const h = headerWriter().str('id', chunk.id).json('providerMetadata', chunk.providerMetadata).build();\n await this._core.closeStream(chunk.id, { name: 'reasoning', data: '', headers: h });\n break;\n }\n\n case 'tool-input-available': {\n // If a stream tracker exists, this tool call was streamed — close it.\n // Otherwise it's a non-streaming tool call — publish discrete.\n try {\n const h = headerWriter()\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: 'tool-input', data: '', headers: h });\n } catch (error: unknown) {\n // Only fall through to discrete for \"no active stream\" — rethrow real failures\n if (!(error instanceof Ably.ErrorInfo && errorInfoIs(error, ErrorCode.InvalidArgument))) {\n throw error;\n }\n const h = headerWriter()\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: 'tool-input', data: chunk.input, headers: h });\n }\n break;\n }\n\n // -- Discrete: lifecycle events ---------------------------------------\n\n case 'start': {\n const h = headerWriter()\n .str('messageId', chunk.messageId)\n .json('messageMetadata', chunk.messageMetadata)\n .build();\n await this._core.publishDiscrete({ name: 'start', data: '', headers: h }, perWrite);\n break;\n }\n\n case 'start-step': {\n await this._core.publishDiscrete({ name: 'start-step', data: '' }, perWrite);\n break;\n }\n\n case 'finish-step': {\n await this._core.publishDiscrete({ name: 'finish-step', data: '' }, perWrite);\n break;\n }\n\n case 'finish': {\n const h = headerWriter()\n .str('finishReason', chunk.finishReason)\n .json('messageMetadata', chunk.messageMetadata)\n .build();\n await this._core.publishDiscrete({ name: 'finish', data: '', headers: h }, perWrite);\n break;\n }\n\n case 'error': {\n await this._core.publishDiscrete({ name: 'error', data: chunk.errorText }, perWrite);\n break;\n }\n\n case 'abort': {\n this._aborted = true;\n await this._core.abortAllStreams(perWrite);\n await this._core.publishDiscrete(\n { name: 'abort', data: chunk.reason ?? '', headers: { [HEADER_STATUS]: 'aborted' } },\n perWrite,\n );\n break;\n }\n\n // -- Discrete: tool lifecycle events ----------------------------------\n\n case 'tool-input-error': {\n const h = headerWriter()\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: 'tool-input-error',\n data: { errorText: chunk.errorText, input: chunk.input },\n headers: h,\n });\n break;\n }\n\n case 'tool-output-available': {\n const h = headerWriter()\n .str('toolCallId', chunk.toolCallId)\n .bool('dynamic', chunk.dynamic)\n .bool('providerExecuted', chunk.providerExecuted)\n .bool('preliminary', chunk.preliminary)\n .build();\n await this._core.publishDiscrete({\n name: 'tool-output-available',\n data: { output: chunk.output },\n headers: h,\n });\n break;\n }\n\n case 'tool-output-error': {\n const h = headerWriter()\n .str('toolCallId', chunk.toolCallId)\n .bool('dynamic', chunk.dynamic)\n .bool('providerExecuted', chunk.providerExecuted)\n .build();\n await this._core.publishDiscrete({\n name: 'tool-output-error',\n data: { errorText: chunk.errorText },\n headers: h,\n });\n break;\n }\n\n case 'tool-approval-request': {\n const h = headerWriter().str('toolCallId', chunk.toolCallId).str('approvalId', chunk.approvalId).build();\n await this._core.publishDiscrete({ name: 'tool-approval-request', data: '', headers: h }, perWrite);\n break;\n }\n\n case 'tool-output-denied': {\n const h = headerWriter().str('toolCallId', chunk.toolCallId).build();\n await this._core.publishDiscrete({ name: 'tool-output-denied', data: '', headers: h }, perWrite);\n break;\n }\n\n // -- Discrete: content parts ------------------------------------------\n\n case 'file': {\n const h = headerWriter()\n .str('mediaType', chunk.mediaType)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete({ name: 'file', data: chunk.url, headers: h }, perWrite);\n break;\n }\n\n case 'source-url': {\n const h = headerWriter()\n .str('sourceId', chunk.sourceId)\n .str('title', chunk.title)\n .json('providerMetadata', chunk.providerMetadata)\n .build();\n await this._core.publishDiscrete({ name: 'source-url', data: chunk.url, headers: h }, perWrite);\n break;\n }\n\n case 'source-document': {\n const h = headerWriter()\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: 'source-document', data: '', headers: h }, perWrite);\n break;\n }\n\n case 'message-metadata': {\n const h = headerWriter().json('messageMetadata', chunk.messageMetadata).build();\n await this._core.publishDiscrete({ name: 'message-metadata', data: '', headers: h }, perWrite);\n break;\n }\n\n // -- Discrete: data-* custom chunks -----------------------------------\n\n default: {\n if (chunk.type.startsWith('data-')) {\n const h = headerWriter().str('id', chunk.id).bool('transient', chunk.transient).build();\n const ephemeral = chunk.transient === true;\n await this._core.publishDiscrete({ name: chunk.type, data: chunk.data, headers: h, ephemeral }, perWrite);\n }\n break;\n }\n }\n }\n\n async writeEvent(chunk: AI.UIMessageChunk, perWrite?: WriteOptions): Promise<Ably.PublishResult> {\n if (!chunk.type.startsWith('data-')) {\n throw new Ably.ErrorInfo(\n `unable to write event; only data-* chunk types are supported, got '${chunk.type}'`,\n ErrorCode.InvalidArgument,\n 400,\n );\n }\n const h = headerWriter()\n .str('id', 'id' in chunk ? chunk.id : undefined)\n .bool('transient', 'transient' in chunk ? chunk.transient : undefined)\n .build();\n const ephemeral = 'transient' in chunk && chunk.transient === true;\n return this._core.publishDiscrete(\n { name: chunk.type, data: 'data' in chunk ? chunk.data : undefined, headers: h, ephemeral },\n perWrite,\n );\n }\n\n async writeMessages(messages: AI.UIMessage[], perWrite?: WriteOptions): Promise<Ably.PublishResult> {\n const payloads = messages.flatMap((msg) => encodeMessagePayloads(msg));\n return this._core.publishDiscreteBatch(payloads, perWrite);\n }\n\n async abort(reason?: string): Promise<void> {\n if (this._aborted) return;\n this._aborted = true;\n await this._core.abortAllStreams();\n await this._core.publishDiscrete({\n name: 'abort',\n data: reason ?? '',\n headers: { [HEADER_STATUS]: 'aborted' },\n });\n }\n\n async close(): Promise<void> {\n await this._core.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message payload encoding (stateless helper)\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({ name: 'text', data: part.text, headers: headerWriter().str('messageId', messageId).build() });\n break;\n }\n case 'file': {\n payloads.push({\n name: 'file',\n data: part.url,\n headers: headerWriter().str('messageId', messageId).str('mediaType', part.mediaType).build(),\n });\n break;\n }\n default: {\n if (isDataUIPart(part)) {\n payloads.push({\n name: part.type,\n data: part.data,\n headers: headerWriter().str('messageId', messageId).str('id', part.id).build(),\n });\n }\n break;\n }\n }\n }\n\n if (payloads.length === 0) {\n payloads.push({ name: 'text', data: '', headers: headerWriter().str('messageId', messageId).build() });\n }\n\n return payloads;\n};\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Vercel AI SDK encoder that maps UIMessageChunk events to Ably\n * 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 A {@link StreamEncoder} for UIMessageChunk/UIMessage.\n */\nexport const createEncoder = (\n writer: ChannelWriter,\n options: EncoderCoreOptions = {},\n): StreamEncoder<AI.UIMessageChunk, AI.UIMessage> => new DefaultUIMessageEncoder(writer, options);\n","/**\n * Vercel AI SDK codec — maps UIMessageChunk events and UIMessage objects\n * to/from native Ably message primitives (publish, append, update, delete).\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 accumulator = UIMessageCodec.createAccumulator();\n * ```\n */\n\nimport type * as AI from 'ai';\n\nimport type { Codec } from '../../core/codec/types.js';\nimport { createAccumulator } from './accumulator.js';\nimport { createDecoder } from './decoder.js';\nimport { createEncoder } from './encoder.js';\n\n/**\n * Vercel AI SDK codec implementing `Codec<UIMessageChunk, UIMessage>`.\n *\n * Provides factory methods for creating encoders, decoders, and accumulators\n * that map between Vercel's UIMessageChunk/UIMessage types and Ably's native\n * message primitives.\n */\nexport const UIMessageCodec: Codec<AI.UIMessageChunk, AI.UIMessage> = {\n createEncoder,\n createDecoder,\n createAccumulator,\n\n getMessageKey: (message: AI.UIMessage): string => message.id,\n\n isTerminal: (event: AI.UIMessageChunk): boolean =>\n event.type === 'finish' || event.type === 'error' || event.type === 'abort',\n};\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 { createClientTransport } from '@ably/ai-transport/vercel';\n *\n * const transport = createClientTransport({ channel });\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 { createClientTransport as createCoreClientTransport } from '../../core/transport/client-transport.js';\nimport { createServerTransport as createCoreServerTransport } from '../../core/transport/server-transport.js';\nimport type {\n ClientTransport,\n ClientTransportOptions,\n ServerTransport,\n ServerTransportOptions,\n} from '../../core/transport/types.js';\nimport { UIMessageCodec } from '../codec/index.js';\n\n/** Options for creating a Vercel client transport. Same as core options but without the codec field. */\nexport type VercelClientTransportOptions = Omit<ClientTransportOptions<AI.UIMessageChunk, AI.UIMessage>, 'codec'>;\n\n/** Options for creating a Vercel server transport. Same as core options but without the codec field. */\nexport type VercelServerTransportOptions = Omit<ServerTransportOptions<AI.UIMessageChunk, AI.UIMessage>, 'codec'>;\n\n/**\n * Create a client-side transport pre-configured with the Vercel AI SDK codec.\n *\n * Equivalent to calling the core `createClientTransport` with `codec: UIMessageCodec`.\n * @param options - Configuration for the client transport (codec is provided automatically).\n * @returns A new {@link ClientTransport} for Vercel AI SDK UIMessage/UIMessageChunk types.\n */\nexport const createClientTransport = (\n options: VercelClientTransportOptions,\n): ClientTransport<AI.UIMessageChunk, AI.UIMessage> => createCoreClientTransport({ ...options, codec: UIMessageCodec });\n\n/**\n * Create a server-side transport pre-configured with the Vercel AI SDK codec.\n *\n * Equivalent to calling the core `createServerTransport` with `codec: UIMessageCodec`.\n * @param options - Configuration for the server transport (codec is provided automatically).\n * @returns A new {@link ServerTransport} for Vercel AI SDK UIMessage/UIMessageChunk types.\n */\nexport const createServerTransport = (\n options: VercelServerTransportOptions,\n): ServerTransport<AI.UIMessageChunk, AI.UIMessage> => createCoreServerTransport({ ...options, codec: UIMessageCodec });\n","/**\n * useChatTransport: wraps a core ClientTransport into the ChatTransport\n * shape that Vercel's useChat expects.\n *\n * Accepts either an existing ClientTransport or options to create one:\n * - From an existing ClientTransport — wraps it directly\n * - From options — creates a ClientTransport with UIMessageCodec and wraps it\n *\n * Both forms accept an optional second argument for ChatTransportOptions\n * (e.g. prepareSendMessagesRequest for the persistence pattern).\n *\n * The hook does NOT auto-close the transport on unmount. Channel lifecycle is\n * managed by the Ably provider (useChannel). Auto-closing would break React\n * Strict Mode. Call chatTransport.close() explicitly if needed.\n */\n\nimport type * as AI from 'ai';\nimport { useRef } from 'react';\n\nimport type { ClientTransport } from '../../core/transport/types.js';\nimport type { ChatTransport, ChatTransportOptions } from '../transport/chat-transport.js';\nimport { createChatTransport } from '../transport/chat-transport.js';\nimport type { VercelClientTransportOptions } from '../transport/index.js';\nimport { createClientTransport as createCoreClientTransport } from '../transport/index.js';\n\n/**\n * Type guard: distinguish an existing ClientTransport from options.\n * @param x - Either a transport instance or options object.\n * @returns True if the argument is a ClientTransport instance.\n */\nconst isClientTransport = (\n x: ClientTransport<AI.UIMessageChunk, AI.UIMessage> | VercelClientTransportOptions,\n): x is ClientTransport<AI.UIMessageChunk, AI.UIMessage> => 'send' in x && typeof x.send === 'function';\n\n/**\n * Create and memoize a {@link ChatTransport} for Vercel's useChat hook.\n *\n * Pass an existing `ClientTransport` to wrap it, or pass\n * `VercelClientTransportOptions` to create one internally with UIMessageCodec.\n * @param transportOrOptions - An existing ClientTransport, or options to create one.\n * @param chatOptions - Optional hooks for customizing request construction.\n * @returns A {@link ChatTransport} compatible with Vercel's useChat hook.\n */\nexport const useChatTransport = (\n transportOrOptions: ClientTransport<AI.UIMessageChunk, AI.UIMessage> | VercelClientTransportOptions,\n chatOptions?: ChatTransportOptions,\n): ChatTransport => {\n const chatTransportRef = useRef<ChatTransport | null>(null);\n\n if (chatTransportRef.current === null) {\n if (isClientTransport(transportOrOptions)) {\n chatTransportRef.current = createChatTransport(transportOrOptions, chatOptions);\n } else {\n const transport = createCoreClientTransport(transportOrOptions);\n chatTransportRef.current = createChatTransport(transport, chatOptions);\n }\n }\n\n return chatTransportRef.current;\n};\n","/**\n * useMessageSync: wires transport message lifecycle events into useChat's setMessages.\n *\n * Subscribes to the transport's 'message' event and replaces messages state\n * with the transport's authoritative message list. Events fire immediately\n * on every store update (including during active streaming), so this hook\n * keeps React state in sync in real time.\n *\n * Returns the unsubscribe function in the useEffect cleanup so handlers\n * are removed on unmount or when dependencies change.\n */\n\nimport type * as AI from 'ai';\nimport { useEffect } from 'react';\n\nimport type { ClientTransport } from '../../core/transport/types.js';\n\n/**\n * Wire transport message updates into useChat's `setMessages` updater.\n * @param transport - The client transport to observe, or null/undefined if not yet available.\n * @param setMessages - The `setMessages` updater function from useChat.\n */\nexport const useMessageSync = (\n transport: ClientTransport<unknown, AI.UIMessage> | null | undefined,\n setMessages: (updater: (prev: AI.UIMessage[]) => AI.UIMessage[]) => void,\n): void => {\n useEffect(() => {\n if (!transport) return;\n const unsubscribe = transport.on('message', () => {\n setMessages(() => transport.getMessages());\n });\n return unsubscribe;\n }, [transport, setMessages]);\n};\n"],"mappings":"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"}
|