/**
 * NativeModuleHealer — in-line self-heal for better-sqlite3 NODE_MODULE_VERSION mismatch.
 *
 * Background:
 *   ServerSupervisor.preflightSelfHeal handles the supervisor-spawn path
 *   (it rebuilds better-sqlite3 in `shadow-install` before forking the server).
 *   But CLI commands (`instar memory ...`, `instar semantic ...`) and any direct
 *   instantiation of SemanticMemory / TopicMemory / MemoryIndex bypass it.
 *
 *   When Node is upgraded after Instar was installed, the native better-sqlite3
 *   binding throws NODE_MODULE_VERSION on construction. Without an in-line heal,
 *   the only fix is for the user to run `npm rebuild better-sqlite3` manually —
 *   and 1254 reports in the field show users hit this and file bug reports
 *   instead (cluster-degradation-semanticmemory-semanticmemory-init-failed-the-m).
 *
 * Strategy:
 *   1. Wrap the better-sqlite3 constructor call in `openWithHeal`.
 *   2. On NODE_MODULE_VERSION error, locate npm + the install prefix that
 *      contains the better-sqlite3 package, run `npm rebuild better-sqlite3
 *      --prefix <install_prefix>` synchronously.
 *   3. Clear better-sqlite3 from `require.cache` so a fresh native binding
 *      is loaded on retry.
 *   4. Retry the constructor once. If it still fails, throw.
 *   5. Log heal events to `<stateDir>/native-module-heals.jsonl` for
 *      observability (consumed by health checks and DegradationReporter).
 *
 * Once-per-process guard:
 *   The rebuild is expensive (~30s) and shouldn't run more than once per
 *   process. After one attempt, subsequent failures throw immediately.
 *
 * Spec: PROP-399 (chronic, 24 cycles, 1254 field reports).
 */
export interface RemediatorInvocationContext {
    attemptId: string;
    runbookId: string;
    abortSignal: AbortSignal;
    /** `process.hrtime.bigint()` issued + expectedRuntimeMs converted to ns. */
    monotonicDeadline: bigint;
    /** §A3 capability-token HMAC — present on Tier-2 dispatched ctxs. */
    hmac?: Buffer;
    /** Wall-clock expiry, mirrors RemediationContext.expiresAt. */
    expiresAt?: number;
}
/**
 * Optional keyVault dependency for `invokeFromRemediator` §A3 / §A23
 * verification. Structural so `src/memory/*` doesn't pull in
 * `src/remediation/*` at module load.
 */
export interface InvocationContextKeyVault {
    deriveLeafKey(context: 'capability', scopeId: string): Buffer;
}
export interface RemediatorExecutionResult {
    outcome: 'success' | 'failure';
    details: Record<string, unknown>;
}
export interface HealEvent {
    /** Component that triggered the heal (e.g. 'SemanticMemory', 'TopicMemory', 'MemoryIndex'). */
    component: string;
    /** UTC ISO timestamp of the attempt. */
    timestamp: string;
    /** Whether the rebuild + retry succeeded. */
    success: boolean;
    /** Node version process is running. */
    nodeVersion: string;
    /** Detected install prefix where `npm rebuild` was run, if any. */
    installPrefix?: string;
    /** npm binary path used for the rebuild, if found. */
    npmPath?: string;
    /** Stderr tail (last 300 chars) on failure. */
    errorTail?: string;
    /** Duration of the rebuild attempt, in ms. */
    durationMs?: number;
}
/**
 * Listener fired after every persisted HealEvent (success or failure).
 * Used by the DegradationReporter bridge so heal failures surface on the
 * user-visible alert path, not just in the jsonl observability log.
 * Listener errors are swallowed — observability never breaks the heal.
 */
export type HealEventListener = (event: HealEvent) => void;
/**
 * NativeModuleHealer is a process-singleton. Stateful members track
 * whether a heal has already been attempted this process so the
 * expensive rebuild doesn't run on every open() retry.
 */
declare class NativeModuleHealerImpl {
    private healAttempted;
    private lastResult;
    private stateDir;
    private listeners;
    /** Configure where heal events are persisted. Optional. */
    configure(opts: {
        stateDir?: string | null;
    }): void;
    /**
     * Subscribe to HealEvents. Listener fires after every persisted event
     * (both success and failure — the bridge filters on success internally
     * so a future health-info consumer can also subscribe without
     * duplicating the dispatch). Returns an unsubscribe function.
     */
    onHealEvent(listener: HealEventListener): () => void;
    /** Reset for testing. */
    resetForTesting(): void;
    /**
     * §A3 verify the HMAC on a RemediatorInvocationContext. Mirrors the
     * canonical body layout in `src/remediation/RemediationContext.ts`. We
     * inline rather than import to avoid the `src/memory/*` → `src/remediation/*`
     * dependency that would break the legacy `openWithHeal` path on installs
     * without the remediation tree (e.g., partial CLI surfaces).
     */
    private verifyContextHmac;
    /**
     * Fall-back path when the surface-side HMAC verification rejects a ctx.
     * Runs the legacy in-line `openWithHeal` heal step (no opener — we just
     * want the rebuild + retry behavior). Returns an `ExecutionResult`-shaped
     * object so the caller's contract is preserved.
     */
    private fallbackToInlineHeal;
    /** Detect NODE_MODULE_VERSION errors. Tolerant of message wording variants. */
    isNodeModuleVersionError(err: unknown): boolean;
    /** Returns the last heal attempt result (or null if none). */
    getLastResult(): HealEvent | null;
    /**
     * Wrap an open() / new Database(path) call. If it throws with
     * NODE_MODULE_VERSION, run the heal once, then retry. Otherwise rethrow.
     *
     * The opener is passed in (rather than calling `new Database` directly)
     * because each caller has different construction logic — TopicMemory does
     * integrity checks, SemanticMemory does pragma setup, etc.
     *
     * The opener may be sync or async; both return paths are awaited.
     */
    openWithHeal<T>(component: string, opener: () => T | Promise<T>): Promise<T>;
    /**
     * Sync variant of openWithHeal. Use when the caller cannot be async
     * (e.g. a class constructor). The internal rebuild path is already
     * synchronous (spawnSync + fs.appendFileSync), so this is the same
     * safety as openWithHeal — it just removes the async surface for
     * sync-only call sites like TokenLedger's constructor.
     *
     * The opener must be sync. If you have an async opener, use
     * openWithHeal instead.
     */
    openWithHealSync<T>(component: string, opener: () => T): T;
    /**
     * Run `npm rebuild better-sqlite3 --prefix <install_prefix>` synchronously.
     * Returns true if the rebuild succeeded, false otherwise. Always logs
     * a HealEvent. Async wrapper over healBetterSqlite3Sync so existing
     * `await` call sites are unchanged.
     */
    healBetterSqlite3(component: string): Promise<boolean>;
    /**
     * Synchronous implementation of the better-sqlite3 rebuild. The
     * internals (spawnSync, fs.appendFileSync) were already synchronous;
     * this just exposes that fact through a sync surface for callers
     * like openWithHealSync that cannot use `await`.
     */
    healBetterSqlite3Sync(component: string): boolean;
    /**
     * Resolve the npm-installable prefix for better-sqlite3. We want the
     * directory whose `node_modules/better-sqlite3` is the one that just
     * failed to load — that's the install prefix npm needs.
     *
     * Strategy: use require.resolve('better-sqlite3'), then walk up to find
     * the parent of the `node_modules/better-sqlite3` segment.
     */
    private findBetterSqlite3InstallPrefix;
    /**
     * Find npm on disk. Mirrors ServerSupervisor.findNpmPath logic.
     */
    private findNpmPath;
    /**
     * Clear better-sqlite3 entries from Node's require cache so the fresh
     * native binding is loaded on retry.
     */
    private clearBetterSqlite3Cache;
    /**
     * Remediator entry point — F-8 / W-1 (SELF-HEALING-REMEDIATOR-V2-SPEC
     * §A28, §A45, §A55). Wraps the same rebuild logic as `openWithHeal`'s
     * heal step but accepts a `RemediatorInvocationContext` so:
     *   - the abort signal is honoured (deadline / parent cancel),
     *   - the monotonic deadline gates `npm rebuild` timeout,
     *   - the rebuilt `.node` binary's sha256 is recorded in `lastResult` so
     *     the Remediator's audit-projection can detect cross-process binary
     *     divergence (A28),
     *   - prebuilt-first heal (parity with the boot heal paths): attempt 1 is
     *     `npm install better-sqlite3@<pinned> --no-save` so prebuild-install
     *     fetches the correct-ABI prebuilt (no compiler needed — the only path
     *     that heals a node-ABI bump on a box without a C++ toolchain); attempt 2
     *     is the from-source `--ignore-scripts --build-from-source` fallback.
     *     NOTE — this deliberately RELAXES the original §A45 from-source-only
     *     stance: the scoped prebuilt fetch (single pinned package, official
     *     better-sqlite3 release — the same source a normal `npm install` uses)
     *     is accepted in exchange for healability, matching what the boot heal
     *     paths already do fleet-wide. The from-source fallback retains
     *     `--ignore-scripts` (no arbitrary postinstalls).
     *
     * Returns an `ExecutionResult`-shaped object compatible with F-8's
     * `ApprovedRunbook.surfaceCallable` contract. Errors are caught and
     * mapped to `{outcome: 'failure', details: {...}}` — the Remediator's
     * verify step (A21) decides whether the heal actually worked; this
     * method ONLY reports whether the rebuild succeeded.
     *
     * The legacy in-line `openWithHeal` entry point remains the canonical
     * safety net for direct-construction CLI paths. This method is the
     * Remediator-orchestrated parallel path; both acquire the same
     * MachineLock tuple (§A2 lock-bound co-existence), so only one rebuild
     * runs at a time on a given machine.
     */
    invokeFromRemediator(ctx: RemediatorInvocationContext, keyVault?: InvocationContextKeyVault): Promise<RemediatorExecutionResult>;
    /**
     * Remediator-side rebuild path. Mirrors `healBetterSqlite3`: prebuilt-first
     * (`npm install better-sqlite3@<pinned>` → prebuild-install fetches the
     * correct-ABI prebuilt, no compiler) with a scoped `--ignore-scripts
     * --build-from-source` fallback (A28 + A45), toolchain PATH-pinned to this
     * process's Node, and records the sha256 of the rebuilt `.node` binary in the
     * heal log for cross-process binary-divergence detection (A28).
     *
     * Returns `{outcome: 'success' | 'failure', details: {...}}` directly so
     * the caller (W-1 runbook surfaceCallable) doesn't need a second mapping
     * step. The HealEvent is still appended to the in-line log for
     * observability parity with the legacy path.
     */
    private healBetterSqlite3FromRemediator;
    /**
     * Read `package-lock.json` and return better-sqlite3's `resolved` URL +
     * `integrity` hash, if locatable. Returns `null` on any failure — this is
     * a best-effort secondary check per A45, not authoritative.
     */
    private readPackageLockIntegrity;
    /**
     * Compute sha256 of the rebuilt better-sqlite3 `.node` binary. Used for
     * A28 cross-process binary-divergence detection. Returns null on any
     * failure — the heal still succeeded; the divergence detector simply
     * loses this data point.
     */
    private computeBetterSqlite3BinarySha256;
    /**
     * Persist a heal event for observability. Best-effort; never throws.
     * Logged to <stateDir>/native-module-heals.jsonl if stateDir is configured,
     * otherwise to <os.tmpdir>/instar-native-module-heals.jsonl as a fallback.
     */
    private logHealEvent;
}
export declare const NativeModuleHealer: NativeModuleHealerImpl;
export {};
//# sourceMappingURL=NativeModuleHealer.d.ts.map