/** * Stuck-warning key-injection guard. * * Extracted from worker.ts's `tui_keys` message handler so the fail-closed * boundary (lifetime pre-check, fresh capture, backend/lifetime re-check, * page-type re-match, write success/failure → delivered/expired) can be unit * tested in isolation. The production handler delegates to this function. * * Fail-closed contract: * - expected lifetime missing / unequal → no keys written, send expired * - fresh capture returns null or throws → no keys written, send expired * - backend/lifetime changed after capture, OR page type no longer matches * → no keys written, send expired * - write success → send delivered; write returns false or throws → send failed * * backend/lifetime are read via getters (not value snapshots) so the guard can * re-check LIVE state after `await capture()` — a backend replacement or CLI * restart that happens mid-capture must still be detected. */ import type { SessionBackend } from '../adapters/backend/types.js'; export interface StuckKeyGuardMessage { stuckNonce: number; stuckPageType: string; stuckCliLifetime?: number; keys: string[]; isFinal: boolean; } export interface StuckKeyGuardResult { /** What was sent back to the daemon, if anything. */ sent: 'delivered' | 'expired' | 'failed' | 'none'; /** Whether keys were actually written to the backend. */ wroteKeys: boolean; } /** * Decide whether to re-arm the stuck detector after a tui_keys click. * * The detector re-arms ONLY when the card-handler explicitly flagged this as a * stuck-warning Enter action (advances to the next review layer) AND keys were * actually written. An expired click (CLI recovered, page changed — wroteKeys * is false) must NOT re-arm: the detector should stay disarmed until the next * real stall, otherwise a recovered CLI could immediately re-trigger a warning. */ export declare function shouldRearmStuckDetector(rearmStuckDetector: boolean, wroteKeys: boolean): boolean; export interface StuckKeyGuardDeps { /** Live read of the current backend (may change between calls / awaits). */ getBackend: () => SessionBackend | null; /** Live read of the current CLI lifetime nonce (incremented on every backend replacement). */ getCurrentLifetime: () => number; renderCols: number; renderRows: number; turnId: string | undefined; dispatchAttempt: number | undefined; /** Fresh PTY capture. Returns null on no-screenshot; may throw. */ capture: (backend: SessionBackend, cols: number, rows: number, opts: { filter: boolean; }) => Promise<{ content: string | null; } | null>; /** Classifier: returns the page type label or undefined if no match. */ match: (snap: string) => string | undefined; /** Writes keys to the backend. Returns true on success, false on failure. */ writeKeys: (keys: string[], isFinal: boolean) => Promise; /** Sends a message back to the daemon. */ sendExpired: (nonce: number, turnId: string | undefined, dispatchAttempt: number | undefined) => void; sendDelivered: (nonce: number, turnId: string | undefined, dispatchAttempt: number | undefined) => void; sendFailed: (nonce: number, turnId: string | undefined, dispatchAttempt: number | undefined) => void; log: (msg: string) => void; } /** * Process a stuck-warning card's tui_keys click with full fail-closed guards. * * Returns a result describing what happened (for testing); production callers * use `result.wroteKeys` to decide whether to re-arm the stuck detector. */ export declare function processStuckWarningTuiKeys(msg: StuckKeyGuardMessage, deps: StuckKeyGuardDeps): Promise; //# sourceMappingURL=stuck-key-guard.d.ts.map