/** * Placeholder texts posted for streaming messages: the first chunk shows * "_thinking…_" and later (continuation) chunks show "_…(continued)_" until * real content lands. Exported as a shared const so the producer here and the * history filter in adapter.ts (which excludes the bot's own placeholders from * `read_thread`) never drift apart. The `…` is a real U+2026 ellipsis. */ export declare const STREAM_PLACEHOLDERS: readonly ["_thinking…_", "_…(continued)_"]; /** * Discord's HARD per-message content limit is 2000 chars. For longer agent * responses we spread the text over several Discord 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 * to Discord). * * ChunkedMessageStream owns N underlying `MessageStream`s (one per Discord * message). It accepts the full accumulated text on `append()`, decides * where the chunk boundaries are, mints new Discord messages as needed, * and dispatches the right slice to each chunk's stream. * * Boundaries are chosen greedily: once an active chunk exceeds the soft * limit, 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 interface ChunkedMessageStreamConfig { /** * Soft per-message char limit used to choose chunk boundaries. Defaults to * 1900 — deliberately below Discord's 2000 hard limit so the per-chunk * `transform` has headroom to grow the text without overflowing. The * transform appends a fence/marker closer (autoCloseOpenMarkdown) and, for * continuation chunks, prepends a re-opener (e.g. "```longlangname\n"); a * chunk sliced to exactly 2000 raw chars would exceed 2000 once transformed * and Discord would reject the edit with BASE_TYPE_MAX_LENGTH. */ limit?: number; /** Throttle floor for each underlying stream's message edit. */ minIntervalMs?: number; /** Posts a new Discord message with placeholder text; resolves with its id. */ postPlaceholder: (text: string) => Promise; /** Updates the Discord message at `id` with `text`. */ updateAt: (id: string, text: string) => Promise; /** * Optional transformer for the text *just before* it hits the edit call — * e.g. markdown translation. Applied per-chunk. * Defaults to `discordMarkdown`. */ transform?: (text: string) => string; } 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 postPlaceholder calls don't race. */ private setupPromise; /** * Records the first setup-chain failure (e.g. a rejecting `postPlaceholder`). * The chain itself is kept rejection-free via a `.catch` so no interim * unhandled promise rejection is emitted; the stored error is rethrown * deterministically at the next `append`/`finish`. */ private setupError; private finished; private readonly limit; private readonly minIntervalMs; private readonly postPlaceholder; private readonly updateAt; private readonly transform; constructor(config: ChunkedMessageStreamConfig); append(fullText: string): void; finish(): Promise; /** Wraps the recorded setup failure with context for surfacing to callers. */ private asSetupError; /** Returns the number of Discord 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 the soft limit. 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 Discord message rather than being split. The previous chunk * gets shortened (message edit will update 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