import type { Destination, RichBlock, MessageRef, MessageContent, PostMessageOpts } from '../platform/types.js'; interface MessageSender { postMessage(destination: Destination, content: MessageContent, opts?: PostMessageOpts): Promise; updateMessage(ref: MessageRef, content: MessageContent): Promise; } export interface OutboundQueueOpts { walPath?: string; adapter: MessageSender; ttlMs?: number; } export interface EnqueueInput { type: 'post' | 'update'; channel: string; destination?: Destination; text: string; richBlocks?: RichBlock[]; threadId?: string; messageId?: string; } export declare class OutboundQueue { private walPath; private adapter; private ttlMs; private mutex; private pending; private sentIds; /** * IDs currently being processed by the inline send path (OutputStream / * durablePost). drain() skips these to prevent double-sends. Entries are * claimed by buildDurableHooks.beforePost/beforeUpdate and released by * afterSent (on success) or onSendFailed (on permanent failure). * On restart, inFlight is empty — unfinished entries fall back to drain(). */ private inFlight; private opCount; private drainTimer; private stopped; constructor(opts: OutboundQueueOpts); enqueue(input: EnqueueInput): Promise; /** Mark an entry as "being sent by the inline path" so drain() skips it. */ claim(id: string): void; /** Release an in-flight claim (after success or permanent failure). */ release(id: string): void; markSent(id: string, slackTs?: string): Promise; recover(): Promise; drain(): Promise; compact(): Promise; startDrainLoop(intervalMs?: number): void; stop(): Promise; flush(): Promise; getPendingCount(): number; private _coalesce; private _appendOp; /** Replay the WAL, skipping unparseable lines instead of throwing. * * An append can be torn mid-line (a full disk truncates the write; the next append then * starts at the truncated offset and glues two records into one unparseable line). A single * such line used to reject recover(), and recover() is awaited from the unguarded startup * path in entry/app.ts — so one bad byte range aborted the rest of boot (thread templates, * thread store, project store, client-manager WebSocket) and left the server unable to run * any agent. Losing an unsendable notification is strictly better than losing the server. */ private _readWAL; } export declare const outboundQueue: { instance: OutboundQueue | null; }; export declare function initOutboundQueue(adapter: MessageSender, opts?: { walPath?: string; ttlMs?: number; }): OutboundQueue; export declare function getOutboundQueue(): OutboundQueue | null; export declare function durablePost(queue: OutboundQueue, sender: MessageSender, destination: Destination, content: MessageContent, opts?: PostMessageOpts): Promise; export declare function durableUpdate(queue: OutboundQueue, sender: MessageSender, ref: MessageRef, content: MessageContent): Promise; export {};