/** * Durable conductor inbox. * * Written BEFORE the delta cursor commits (the trigger loop today commits the * cursor and then invokes its callback, so a callback failure loses the batch * forever - operator-trigger-loop.ts, "feed AFTER commit"). With this table * the order inverts: enqueue -> commit. A crash between the two redelivers on * the next drain, and per-event dedupe turns redelivery into a no-op. * * Claims are leases, not transfers: an acked row is done; an unacked claim * older than the lease returns to pending via replayStale(). * * `eventIds` and `lines` are INDEPENDENT fields, never zipped positionally: * eventIds is the identity/cause set (the whole batch), lines is the bounded * display excerpt - the same split the reconcile callback documents. Review * caught the original positional pairing scrambling content for any batch * larger than the display cap. */ import type { SQLiteDatabase } from '../sqlite.js'; export interface InboxBatch { channelKey: string; /** Identity + cause: EVERY event in the batch. */ eventIds: string[]; /** Bounded human-readable excerpt - display only, not paired with eventIds. */ lines: string[]; } export interface InboxRow extends InboxBatch { id: number; status: 'pending' | 'claimed' | 'acked' | 'dead'; attempts: number; } export declare class ConductorInbox { private readonly db; private readonly stmtInsertEvent; private readonly stmtInsertBatch; private readonly stmtClaimSelect; private readonly stmtClaimUpdate; private readonly stmtAck; private readonly stmtRetry; private readonly stmtRetryStatus; private readonly stmtPruneAcked; private readonly stmtPrunePending; private readonly stmtPruneEvents; private readonly stmtReplay; private readonly stmtDepth; constructor(db: SQLiteDatabase); private now; /** * Dedupe is PER EVENT, not per batch shape. A batch-boundary key fails on * partial redelivery: [e1] enqueues, the cursor commit fails elsewhere, the * next drain delivers [e1,e2] under a different boundary - and e1 runs * twice. Seen event ids live in their own table; a batch with no unseen * events is dropped, a batch with any unseen event is stored whole (its * display lines may briefly re-show an already-seen event; identity never * lies). */ enqueue(batch: InboxBatch): number | null; claimNext(): InboxRow | null; ack(id: number): void; /** * Return a claim to pending, or park it dead after MAX_ATTEMPTS. Returns * the resulting status so the caller can be LOUD about a dead batch - a * permanent loss must never be silent. */ retry(id: number, error: string): 'pending' | 'dead' | 'noop'; replayStale(olderThanMs: number, now?: number): number; depth(): { pending: number; claimed: number; dead: number; }; } //# sourceMappingURL=conductor-inbox.d.ts.map