/**
 * Restart-safe resume of interrupted runs (DURABLE.4).
 *
 * Daemon boot fires `resumeOnStartup()` AFTER lock acquire + BEFORE cron
 * registration. Scans the checkpoint store for runs that have a
 * manifest, no terminal marker, and a last-completed step within the
 * resume window. For each, resolves the rule's current `ProcessStep[]`
 * (via caller-supplied `RuleResolver`), hydrates `as:` bindings, and
 * re-invokes the caller-supplied `RunEvaluator` from the next step.
 *
 * Locks (DURABLE.4 §learn / §risk):
 *   - Window default 60s; `OPENSQUID_RESUME_WINDOW_MS` env override.
 *     Explicit `opensquid checkpoints resume <run_id>` bypasses window.
 *   - Pack-version mismatch → skip + audit `pack_version_mismatch`.
 *     No silent migration (rules may have changed shape).
 *   - Audit posture: ONE summary entry per startup call; per-run
 *     entries only for resumed + reason-worthy skips. Stale-window
 *     runs are filtered at SQL level, never enumerated.
 *   - Concurrent-execution race avoided by daemon's proper-lockfile
 *     singleton (SCHED.1); Resumer holds no lock of its own.
 *
 * Terminal-marker choice: Option 2 (explicit `terminal_markers` table)
 * — cheaper than reloading every pack at scan time to compute
 * `lastCompletedStep === totalSteps - 1`.
 *
 * Imports from: ./checkpoint_store.js, ../types.js.
 * Imported by: OpenSquidDaemon (DURABLE.4 wire), checkpoints CLI.
 */
import type { CheckpointStore, RunManifest } from './checkpoint_store.js';
import type { ProcessStep } from '../types.js';
/** 60s — long enough for fast restart, short enough to avoid ghost revives. */
export declare const DEFAULT_RESUME_WINDOW_MS = 60000;
/** Resolver: manifest → current `ProcessStep[]` + live `packVersion`, or `null`
 *  when the pack/skill/rule no longer exists. Drives `pack_missing` skip. */
export interface ResolvedRule {
    process: ProcessStep[];
    packVersion: string;
}
export type RuleResolver = (manifest: RunManifest) => Promise<ResolvedRule | null>;
/** Caller-supplied evaluator entry. Resumer never imports `evaluateProcess`
 *  directly — keeps the module dependency-light and stubbable in tests. */
export interface RunEvaluatorInput {
    manifest: RunManifest;
    bindings: Record<string, unknown>;
    entryStepIdx: number;
}
export type RunEvaluator = (input: RunEvaluatorInput) => Promise<void>;
/** Joined view of an interrupted run — manifest + checkpoint cursor. */
export interface InterruptedRun {
    runId: string;
    lastCompletedStep: number;
    lastCompletedAtMs: number;
    packId: string;
    packVersion: string;
    skill: string;
    ruleId: string;
    eventKind: string;
    eventPayload: unknown;
}
/** Audit entry shapes. `resume_summary` fires once per startup;
 *  `resume_run` / `resume_skipped` ride along per run. */
export type SkipReason = 'manifest_missing' | 'pack_missing' | 'rule_missing' | 'pack_version_mismatch' | 'evaluator_error';
export type AuditEntry = {
    event: 'resume_summary';
    scanned: number;
    resumed: number;
    skippedOther: number;
} | {
    event: 'resume_run';
    runId: string;
    packId: string;
    fromStepIdx: number;
} | {
    event: 'resume_skipped';
    runId: string;
    reason: SkipReason;
    detail?: string;
};
export type AuditSink = (entry: AuditEntry) => void;
export interface ResumeOpts {
    store: CheckpointStore;
    evaluator: RunEvaluator;
    resolver: RuleResolver;
    /** Audit sink; default no-op. */
    auditLog?: AuditSink;
    /** Override the default resume window. `Number.POSITIVE_INFINITY` disables. */
    resumeWindowMs?: number;
    /** Injected clock for tests; defaults to `Date.now`. */
    nowMs?: () => number;
}
export interface ResumeResult {
    resumed: boolean;
    reason?: SkipReason;
}
export interface ResumeStartupResult {
    resumed: number;
    skipped: number;
}
export declare class Resumer {
    private readonly store;
    private readonly evaluator;
    private readonly resolver;
    /** Primary sink supplied at construction. Patch C adds `additionalSinks`
     *  via `attachAuditSink` so the daemon can fan-out resumer events into
     *  its unified `AuditLog` without rebuilding the Resumer. */
    private readonly auditLog;
    private readonly additionalSinks;
    private readonly resumeWindowMs;
    private readonly nowMs;
    constructor(opts: ResumeOpts);
    /** Patch C — daemon-side wiring hook. Attaches an additional audit sink
     *  that fires AFTER the primary sink for every emitted entry. Multiple
     *  attaches accumulate; no detach (daemon lifetime owns the Resumer).
     *  Errors thrown from attached sinks are swallowed so a downstream
     *  AuditLog hiccup never blocks the primary audit path. */
    attachAuditSink(sink: AuditSink): void;
    /** Internal — fan-out wrapper used by every audit emit site. */
    private emit;
    /** Discover interrupted runs in the configured resume window. */
    scanInterrupted(): Promise<InterruptedRun[]>;
    /** Resume one interrupted run. Returns `{ resumed: false, reason }` on
     *  any skip (pack uninstalled, version diverged, evaluator threw). */
    resume(run: InterruptedRun): Promise<ResumeResult>;
    /** Daemon-startup entrypoint: scan + resume all. Invoked AFTER lock
     *  acquire + BEFORE cron registration so resumes finish before fresh
     *  fires can race. One summary audit entry; per-run only for resumed +
     *  reason-worthy skips. */
    resumeOnStartup(): Promise<ResumeStartupResult>;
    private joinManifests;
}
//# sourceMappingURL=resumer.d.ts.map