import type Database from 'better-sqlite3'; export type OutboxStatus = 'pending' | 'sending' | 'delivered' | 'giving_up'; export type OutboxKind = 'text' | 'card' | 'typing'; export type OutboxPriority = 'high' | 'normal'; export interface OutboxRow { id: number; job_id: number | null; thread_key: string; platform: string; channel_id: string; thread_id: string; payload: string; kind: OutboxKind; priority: OutboxPriority; status: OutboxStatus; attempts: number; last_attempt_at: string | null; last_error: string | null; delivered_at: string | null; next_attempt_at: string | null; created_at: string; dedup_key: string | null; } export interface EnqueueOpts { threadKey: string; platform: string; channelId: string; threadId: string; /** For text/card: serialized payload (text body or JSON of card). For typing: unused, pass ''. */ payload: string; kind: OutboxKind; priority?: OutboxPriority; jobId?: number | null; /** Optional idempotency key. When set, a second enqueue with the same key * is a no-op and returns the existing row id, so a retried/replayed caller * (e.g. a reminder resurrected from the 'firing' state after a crash) can't * enqueue a duplicate message. */ dedupKey?: string | null; } export declare const MAX_ATTEMPTS: 6; /** Stop the retention sweep (graceful shutdown / tests). */ export declare function stopOutboxRetentionSweep(): void; /** Close the underlying SQLite handle. Called by cli.ts on graceful shutdown. */ export declare function closeOutboxDb(): void; /** Insert a new pending row. Returns the row id, or -1 if the DB is broken * (caller should fall back to direct send). * * R13 B5 — outbound text payloads are scrubbed for secret-shaped * substrings before insert. Defense-in-depth tail behind B1's hard * denylist: if an agent dumps a token / private key / .env block * via a path the denylist missed, the scrubber catches it on the * way out. The scrubbed payload is what hits SQLite + the wire; * the original never persists. */ export declare function enqueue(opts: EnqueueOpts): number; /** Pick up to `limit` pending rows whose next_attempt_at is now-or-past, * highest priority first. Worker calls this on each tick. */ export declare function pickPending(limit?: number): OutboxRow[]; /** Atomically claim a 'pending' row for delivery, transitioning it to the * transient 'sending' lease. Returns true iff THIS caller won the claim * (changes === 1). The worker must only call doSend() after a winning * claim, so a re-entrant tick can never double-send the same row. * * Note: attempts is intentionally NOT incremented here — markDelivered / * markFailed own the attempts accounting, so direct (non-worker) callers * and the existing tests keep their semantics. */ export declare function claimForSend(id: number): boolean; /** Requeue rows orphaned in the transient 'sending' lease by a crash (the * process died between claimForSend and markDelivered/markFailed). Called * once at worker startup. Returns the number of rows requeued. * * This preserves at-least-once delivery: a row that was actually sent but * crashed before the delivered-ack is re-sent once on restart (a duplicate * is the safe failure mode here vs. a silently dropped message). The win * over the previous design is that, absent a crash, a failed delivered-ack * leaves the row parked in 'sending' (not 'pending'), so the worker no * longer re-sends it every tick. */ export declare function requeueStuckSending(): number; /** Mark a row delivered. Accepts a row that is either still 'pending' (the * high-priority sync path enqueues then delivers directly) or held in the * 'sending' lease (the worker path, post claimForSend). */ export declare function markDelivered(id: number): void; /** Record a failed attempt. If attempts have exhausted the backoff schedule * the row transitions to 'giving_up' (and no longer drains). Otherwise it * stays 'pending' with next_attempt_at = now + backoff. */ export declare function markFailed(id: number, error: string): { gaveUp: boolean; attempts: number; }; /** Get one row by id. */ export declare function getOutbox(id: number): OutboxRow | null; /** List rows in 'giving_up' or 'pending' state for /outbox command. */ export declare function listOutbox(opts?: { status?: OutboxStatus; limit?: number; threadKey?: string; }): OutboxRow[]; /** Aggregate counts for /outbox status. */ export declare function getOutboxStats(): Record; /** Resurrect a 'giving_up' row back to 'pending' so the worker tries again. * Resets attempts to 0 so the full backoff ladder applies again. */ export declare function retryGivingUp(id: number): boolean; /** Prune delivered + giving_up rows older than the retention window. */ export declare function pruneOldOutbox(d?: Database.Database): number; /** Test hook: wipe everything. Only safe in unit tests. */ export declare function _resetOutboxForTests(): void; //# sourceMappingURL=outbox.d.ts.map