import { type ConversationRecord, type ConversationStoreFs } from "./conversation-store"; export declare const CHAT_EFFECT_JOURNAL_VERSION = 1; export declare const MAX_TERMINAL_CHAT_EFFECTS = 128; export declare function chatEffectJournalPath(agentDir: string, transport: "discord" | "slack"): string; export type ChatEffectState = "pending" | "leased" | "accepted" | "uncertain" | "terminal"; /** Provider receipts are identifiers/status only. Never put request or response bodies here. */ export interface ChatEffectReceipt { provider?: string; messageId?: string; channelId?: string; threadId?: string; timestamp?: string; status?: string; } /** * A protected, generation-bound provider-visible effect. Payload is deliberately * owned by this journal; conversation mappings may retain only `effectId`. */ export interface ChatEffect extends ConversationRecord { id: string; kind: string; transport: "discord" | "slack"; sessionId?: string; endpointGeneration: number; payload: TPayload; state: ChatEffectState; owner?: string; leaseExpiresAt?: number; epoch: number; createdAt: number; updatedAt: number; receipt?: ChatEffectReceipt; } export interface EnqueueChatEffect { id: string; kind: string; transport: "discord" | "slack"; sessionId?: string; endpointGeneration: number; payload: TPayload; receipt?: ChatEffectReceipt; } export interface ChatEffectLease { owner: string; epoch: number; } /** * One journal per transport. It uses the same 0600, fsynced atomic persistence * and cross-process exclusive locking as mappings, but stores payloads in a * separate protected file (`effects.json`). Terminal history is compacted only * after terminal state is durably recorded; nonterminal effects are never evicted. */ export declare class ChatEffectJournal { #private; constructor(input: { agentDir: string; transport: "discord" | "slack"; fs?: ConversationStoreFs; now?: () => number; pid?: number; pidAlive?: (pid: number) => boolean; pidIncarnation?: (pid: number) => string | undefined; sleep?: (ms: number) => Promise; lockTimeoutMs?: number; maxTerminalEffects?: number; }); get filePath(): string; read(id: string): Promise | undefined>; list(): Promise; replayable(transport: "discord" | "slack", endpointGeneration: number): Promise; enqueue(input: EnqueueChatEffect): Promise>; /** * Inserts an effect directly into a live lease. Recovery can never observe a * newly persisted effect as claimable before its owner has authority to act. * Existing effects are left untouched and report no acquired lease. */ enqueueAndClaim(input: EnqueueChatEffect, owner: string, leaseMs: number): Promise | undefined>; claim(id: string, owner: string, leaseMs: number): Promise | undefined>; renew(id: string, lease: ChatEffectLease, leaseMs: number): Promise | undefined>; /** Persists provider progress without releasing the owner/epoch fence. */ recordReceipt(id: string, lease: ChatEffectLease, receipt: ChatEffectReceipt): Promise | undefined>; record(id: string, lease: ChatEffectLease, state: Exclude, receipt?: ChatEffectReceipt): Promise | undefined>; /** Irreversibly rejects an effect whose mapping never accepted its authority. */ terminalize(id: string, receipt: ChatEffectReceipt, lease?: ChatEffectLease): Promise; }