/**
 * ContextWedgeSentinel — detects transcript-level fast-fail wedges (the
 * thinking-block 400 and the Usage-Policy rejection loop) and recovers via a
 * FRESH respawn.
 *
 * Signature family 1 — thinking-block 400 (diagnosed 2026-05-28): a tool call
 * is cancelled inside a parallel tool batch while extended thinking is on.
 * Claude Code cancels every sibling call, and that cancellation corrupts the
 * thinking block on the latest assistant turn. The Anthropic API then rejects
 * EVERY resume of that session with `400 ... thinking blocks in the latest
 * assistant message cannot be modified`. The session fast-fails instantly on
 * every inbound message ("Cooked for 0s"); it is permanently dead yet still
 * emitting output, so neither the silence sentinel (output never goes quiet)
 * nor the socket sentinel (no disconnect string) catches it.
 *
 * Signature family 2 — AUP-rejection loop (diagnosed 2026-06-05, EXO 3.0
 * incident): the transcript accumulates content that trips the API's Usage
 * Policy classifier (e.g. literal red-team / prompt-injection test payloads
 * generated during security-harness work). Because every turn re-sends the
 * full conversation, EVERY response attempt is rejected with `API Error:
 * Claude Code is unable to respond to this request, which appears to violate
 * our Usage Policy` — same permanent-death shape, same recovery. Unlike the
 * thinking-block 400, a SINGLE policy rejection can be a benign one-off (one
 * bad request, next message fine), so this family additionally requires the
 * signature to appear MORE THAN ONCE in the capture (the loop always repeats;
 * a one-off never does) before it counts as a wedge.
 *
 * Critical difference from the other silently-stopped sentinels: a send-keys
 * nudge CANNOT recover this — re-engaging just re-sends the corrupted turn and
 * hits the same 400. The only recovery is a clean respawn that does NOT
 * `--resume` the corrupted transcript. That is the injected `recoverFn`
 * (wired to SessionRefresh fresh-mode, which clears the topic's resume UUID
 * before respawning).
 *
 * Because recovery is destructive (kills + respawns a session), detection is
 * gated by a CONFIRM WINDOW: the signature must still be the non-progressing
 * session tail after `confirmWindowMs` before a wedge is confirmed. This is the
 * defense against false-positives — most importantly a session merely
 * *discussing* this very error (its tail keeps changing as it works, so the
 * signature does not persist as the tail). And the destructive recovery itself
 * is opt-in (autoRecovery, default off) and rides the Graduated Feature Rollout
 * track; in the default detect-only mode the sentinel only audits + escalates.
 *
 * Spec: docs/specs/context-wedge-sentinel.md (companion to silently-stopped-trio.md)
 *
 * Signal-vs-authority: the regex + confirm-window are detectors. The recoverFn
 * is a bounded recovery primitive (SessionRefresh rate-guards respawns). User-
 * facing escalation routes through the existing MessagingToneGate via the
 * SentinelNotifier path. No new blocking authority.
 */
import { EventEmitter } from 'node:events';
export type ContextWedgeStatus = 'detected' | 'confirming' | 'recovered' | 'escalated';
/** Outcome of a recovery attempt, returned by the injected recoverFn. */
export type WedgeRecoveryOutcome = 'respawned' | 'dry-run' | 'detect-only' | 'failed';
export interface ContextWedgeState {
    sessionName: string;
    detectedAt: number;
    status: ContextWedgeStatus;
    /** Which signature family triggered the detection. */
    kind: WedgeKind;
}
export interface ContextWedgeSentinelDeps {
    /** Peek at the session's most recent tmux output (may be empty). */
    getRecentOutput: (sessionName: string) => string;
    /**
     * Attempt recovery of a confirmed wedge. The wiring decides policy
     * (detect-only / dry-run / live respawn) and reports back via the outcome.
     * Never throws on the expected failure modes; rejection is treated as 'failed'.
     */
    recoverFn: (sessionName: string) => Promise<WedgeRecoveryOutcome>;
    /** Route a user-facing escalation; server.ts owns topic routing + tone gate. */
    notifyFn: (sessionName: string, text: string) => Promise<void>;
    /** List the session names to scan each tick. Without it the sentinel is
     *  event-driven only (caller invokes scanSession/report). */
    listSessionNames?: () => string[];
    /** Override Date.now (tests). */
    now?: () => number;
    /** Override timer setters (tests). */
    setTimer?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
    clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;
}
export interface ContextWedgeSentinelConfig {
    enabled?: boolean;
    /** How often the self-driving loop scans every session (ms). Default 20s. */
    tickIntervalMs?: number;
    /** How long the signature must persist as the non-progressing tail before the
     *  wedge is confirmed (ms). Default 45s. */
    confirmWindowMs?: number;
}
/**
 * Patterns for the thinking-block 400. Intentionally anchored on the unusual,
 * API-specific phrase — natural prose almost never contains it, and the
 * confirm-window + opt-in recovery cover the one case that does (a session
 * discussing this very bug).
 */
export declare const CONTEXT_WEDGE_PATTERNS: readonly RegExp[];
/**
 * Patterns for the AUP-rejection loop. Anchored on the API-specific rejection
 * phrase, which natural prose almost never contains verbatim.
 */
export declare const AUP_WEDGE_PATTERNS: readonly RegExp[];
/** Which signature family a wedge detection matched. */
export type WedgeKind = 'thinking-block-400' | 'aup-rejection';
/** Detector — true if `text` contains the thinking-block-400 signature. */
export declare function detectContextWedge(text: string): boolean;
/** Detector — true if `text` contains the AUP-rejection signature. */
export declare function detectAupRejection(text: string): boolean;
/**
 * Tail classifier — which wedge family (if any) is the live tail of the
 * capture? A genuinely wedged session shows the error as its tail; a session
 * that merely mentioned the error earlier and then kept working has scrolled
 * it out. The AUP family additionally requires the signature on >1 line of the
 * full capture — the loop always repeats, a benign one-off rejection doesn't.
 */
export declare function classifyWedgeTail(text: string, tailLines?: number): WedgeKind | null;
/**
 * Tail detector — is any wedge signature the live tail? (Back-compat wrapper
 * around classifyWedgeTail; existing callers and tests use the boolean form.)
 */
export declare function signatureIsTail(text: string, tailLines?: number): boolean;
export declare class ContextWedgeSentinel extends EventEmitter {
    private readonly deps;
    private readonly cfg;
    private readonly states;
    private readonly timers;
    private tickHandle;
    /** Liveness of the tick loop (GUARD-POSTURE-ENDPOINT-SPEC §2.2): 0 = never ticked. */
    private lastTickAt;
    constructor(deps: ContextWedgeSentinelDeps, cfg?: ContextWedgeSentinelConfig);
    /** Begin the self-driving scan loop. No-op without listSessionNames. */
    start(): void;
    stop(): void;
    /** One scan pass over every tracked session. */
    tick(): void;
    /** Scan one session for the wedge signature. */
    scanSession(sessionName: string): void;
    /** Public entry: report a detected (not yet confirmed) wedge. Idempotent. */
    report(sessionName: string, kind?: WedgeKind): void;
    isRecoveryActive(sessionName: string): boolean;
    listActive(): ContextWedgeState[];
    clear(sessionName: string): void;
    shutdown(): void;
    private confirm;
    private escalate;
    private notify;
    private setTimer;
    private clearTimer;
    /** Sync in-memory runtime read for the GuardRegistry (GET /guards).
     *  MUST stay a cheap property read — no I/O, no session listing. */
    guardStatus(): {
        enabled: boolean;
        lastTickAt: number;
    };
}
//# sourceMappingURL=ContextWedgeSentinel.d.ts.map