/** * Options for {@link ChunkedMessageStream}. */ export interface ChunkedMessageStreamOptions { /** Per-message character limit (e.g. ~4000 for Slack, ~28k for Teams). */ maxLength: number; /** Posts a new platform message for chunk `index` with initial `chunk` text. */ post: (chunk: string, index: number) => Promise; /** Updates the already-posted message for chunk `index` with `chunk` text. */ update: (chunk: string, index: number) => Promise; /** Throttle floor for each underlying stream's update, in ms (default 800). */ throttleMs?: number; /** Forwarded to each per-chunk stream — sink for a failed chunk update. */ onApplyError?: (err: unknown) => void; } /** * Chat platforms cap message length. For longer agent responses we spread the * text over several platform messages — but the *chunk boundaries* must be * stable once committed (we can't reflow a sentence into a previous chunk * after that chunk has already been posted). * * ChunkedMessageStream owns N underlying {@link SerializedMessageStream}s (one * per platform message). It accepts the full accumulated text on `write()`, * decides where the chunk boundaries are, mints new messages as needed, and * dispatches the right slice to each chunk's stream. Each chunk is passed * through {@link autoCloseMarkdown} before it hits the platform, so a split * inside a code fence closes the fence at the boundary and reopens it (same * info string) at the start of the next chunk. * * Boundaries are chosen greedily: once an active chunk exceeds `maxLength`, * freeze the boundary at the last newline (or last space) within the limit; * remaining text becomes the next chunk. Boundaries don't move once frozen, * so an already-posted chunk's text never shrinks. */ export declare class ChunkedMessageStream { private buffer; /** Sorted positions where a chunk ends (= where the next chunk begins). */ private boundaries; private streams; /** Serialises new-chunk creation so async post calls don't race. */ private setupPromise; private finished; private readonly maxLength; private readonly throttleMs; private readonly post; private readonly update; private readonly onApplyError?; constructor(opts: ChunkedMessageStreamOptions); /** * Feed the full accumulated text (not a delta). Resolves once any newly * required messages have been posted and slices dispatched to their streams * (per-chunk updates may still be throttled until {@link finalize}). */ write(fullText: string): Promise; /** * Mark the stream done: drain pending setup, run a final dispatch, then * force-flush every chunk so all messages reflect the final buffer. */ finalize(): Promise; /** Returns the number of platform messages this stream has posted so far. */ get chunkCount(): number; /** * Walk forward from the last frozen boundary, freezing new ones whenever * the active chunk's length exceeds `maxLength`. Once frozen, a boundary * doesn't move. * * Special case (block-keeps-whole): if the chosen boundary lands INSIDE * an open fenced code block, we try to move the boundary BACK to the * position right before the fence opener, so the *whole* block lives in * the next message rather than being split. The previous chunk gets * shortened (update will edit it down). Fallback when the block is too * large to fit in one chunk: keep the inside-fence boundary; the * dispatcher will prepend the fence opener to the next chunk (re-opener * path). */ private refreezeBoundaries; private ensureStreamsAndDispatch; } //# sourceMappingURL=chunked-message-stream.d.ts.map