import type { ButtonCallback } from './types.js'; interface PendingRetry { jobId: number; agent: string; prompt: string; threadKey: string; platform: string; channelId: string; threadId: string; creatorId: string; /** ms-epoch when the entry expires (interrupted_at + window). */ expiresAt: number; /** v1.2 β€” id of the choice card we sent for this entry (if the adapter * supported sendChoiceCard). Used to edit the card to its terminal text * after the user picks a button or replies via text. Null when we fell * back to plain text. */ cardMessageId: string | null; } /** * v1.2 β€” executor hook injected by cli.ts at boot. When the user accepts * a retry (via text "1" OR button "πŸ” 重发"), the new inline-job row is * created here but the agent never runs unless we feed a synthetic * MessageContext through cli's handleMessage. cli.ts owns that closure * (it has the registry, sink, agent dispatcher) so we register the * callback here and invoke it after a successful retry. * * Without this hook the row stays in 'pending' forever. That bug * affected the button path only (text path was working via the * messenger's own onMessage chain). */ export interface RecoveryExecutorArgs { entry: PendingRetry; newJobId: number; } export type RecoveryExecutor = (args: RecoveryExecutorArgs) => Promise; export declare function setRecoveryExecutor(fn: RecoveryExecutor | null): void; /** Test hook β€” wipe the in-memory state. */ export declare function _resetPendingForTests(): void; /** Test hook β€” inspect current state. */ export declare function _peekPendingForTests(): Map; /** Run once on agim startup, after messengers + sink are up. */ export declare function scanInterruptedAndNotify(): Promise<{ notified: number; abandoned: number; }>; /** Periodic in-memory expiry sweep. Removes entries whose `expiresAt` has * passed without a user reply. Called from cli.ts on a setInterval. */ export declare function sweepExpiredPending(now?: number): number; /** Result of a recovery-reply attempt. */ export type RecoveryReplyOutcome = { kind: 'not-pending'; } | { kind: 'not-recovery-reply'; } | { kind: 'retried'; newJobId: number; oldJobId: number; entry: PendingRetry; } | { kind: 'cancelled'; oldJobId: number; }; /** Try to interpret a user's reply as a recovery decision. Returns an * outcome the caller can render. Side-effect: on a recognized decision, * the pending entry is removed and the DB row transitions. When the * entry was originally sent as a native choice card, this also edits * the card to its terminal state so its buttons can't be tapped after * the user already replied with text β€” otherwise a stale tap would * hit the "θ―·ζ±‚ε·²θΏ‡ζœŸ" branch and read as a glitch. */ export declare function tryHandleRecoveryReply(threadKey: string, text: string): RecoveryReplyOutcome; /** Convenience for cli.ts: was the thread expecting a recovery reply just * before we routed this message? Used to skip approval / reminder * interceptors when the user is replying to a recovery prompt. */ export declare function hasPendingRecovery(threadKey: string): boolean; /** * True if the given callback_data belongs to a recovery prompt β€” used by * approval-router's button dispatcher to route the tap to us instead of * the approval flow. */ export declare function isRecoveryButtonData(data: string): boolean; /** * Handle a button tap on a recovery card. Mirrors tryHandleRecoveryReply's * semantics: returns the outcome the caller can act on. Also edits the * card (drops the buttons) so a stale tap can't fire twice. */ export declare function handleRecoveryButton(platform: string, cb: ButtonCallback): Promise; export {}; //# sourceMappingURL=job-recovery.d.ts.map