/** * AI streaming response reader. * * Reads chunks from a `Response`'s body, batches store updates to one * per animation frame, strips trailing incomplete HTML tags so partial * flash, and parses widget blocks at completion. * * Pure with respect to the DOM — only writes into the store. The bubble * keeps the same id from frame one: the server pre-creates the row and * returns the persisted id via the `X-Vtilt-Message-Id` header, so the * later Ably echo / DB-update merges into the same row with no remount. * On older servers that don't send the header we fall back to the legacy * `temp-ai-*` id and upsertMessage's id-swap behaviour. */ import type { ChatStore } from "../store/chat-store"; /** Header the server uses to publish the persisted AI message id up-front. */ export declare const AI_MESSAGE_ID_HEADER = "X-Vtilt-Message-Id"; /** Header the server uses to publish the persisted AI message timestamp. */ export declare const AI_MESSAGE_CREATED_AT_HEADER = "X-Vtilt-Message-Created-At"; /** Widget block marker regex: `` */ export declare const WIDGET_BLOCK_RE: RegExp; /** Parse `` markers out of streamed content. */ export declare function parseWidgetBlocks(text: string): Array<{ type: string; params: Record; }>; /** * Align the optimistic user row's `created_at` with the server timestamp from * stream response headers, then re-sort. * * The optimistic bubble is stamped with the *client* clock. The AI placeholder * uses the *server* clock from `X-Vtilt-Message-Created-At`. When the client * clock is ahead of the server, chronological insert places the AI bubble * *above* the user question until a full reload. Applying the user ack * timestamp before the AI insert keeps local order identical to Postgres. */ export declare function applyUserMessageCreatedAtFromHeaders(store: ChatStore, response: Response): void; interface StreamArgs { store: ChatStore; response: Response; channelId: string; tempId?: string; /** When aborted, the reader stops and removes its partial bubble. */ signal?: AbortSignal; /** Extra guard for superseded streams (new user send while still reading). */ isActive?: () => boolean; onStreamStart?: (messageId: string) => void; onComplete?: (messageId: string) => void; } /** * Read an AI streaming response into the store. The bubble id is decided * once at frame one (persisted id from `X-Vtilt-Message-Id` if the server * provided it, otherwise a `temp-ai-*` fallback) and never changes, so * the same `MessageBubble` instance receives content updates without * re-keying. */ export declare function handleStreamingResponse({ store, response, channelId, tempId, signal, isActive, onStreamStart, onComplete, }: StreamArgs): Promise; /** Remove a single message by id (used to clean up failed temp messages). */ export declare function removeMessageById(store: ChatStore, id: string): void; export {};