/**
 * CredentialLocationLedger — the bookkeeping core of live credential re-pointing.
 *
 * Spec: docs/specs/live-credential-repointing-rebalancer.md §2.2.
 *
 * A machine-local durable ledger (`<stateDir>/credential-locations.json`) that records,
 * for each config-home SLOT, which pool account's credential currently lives there. It is
 * the single source of truth for "which account is in which home" once live re-pointing is
 * enabled — every consumer that today treats a pool account's enrollment `configHome` as its
 * live location instead resolves through `slotOf(accountId)` / `tenantOf(slot)` here.
 *
 * This module is Step 2 of Increment A: the bookkeeping + durability + seeding/recovery core.
 * It performs NO keychain writes itself — the staged swap executor (§2.3, Step 5) and the
 * write funnel (§2.2, Step 4) are separate. The identity oracle it seeds/recovers from is the
 * `IdentityOracle` interface (Step 3 implements it against the Anthropic OAuth profile endpoint,
 * `/api/oauth/profile`, routed through the existing intelligence-provider chokepoint).
 *
 * Durability posture (the §2.2 contract — NOT SubscriptionPool's posture):
 *   - missing file        → NEVER-SEEDED (mode 'ok', empty assignments). Reads return null →
 *                           consumers fall back to today's enrollment-home behavior. This is
 *                           normal, not a degradation.
 *   - corrupt/unparseable → UNKNOWN MODE (fail-closed for moves: every mutation refuses;
 *                           fail-open-LOUD for reads: reads return null AND one HIGH attention
 *                           item names the degradation). Recovery is a fresh `seedFromOracle()`.
 *                           This is deliberately NOT a silent fresh-start (No Silent Degradation):
 *                           a quiet reset here could place a session onto the wrong account.
 */
/** The on-disk schema version this module writes. Bumped only on a breaking shape change. */
export declare const CREDENTIAL_LEDGER_SCHEMA_VERSION = 1;
/** Which account's credential currently lives in a given config-home slot. */
export interface CredentialAssignment {
    /** Config-home path acting as the slot, e.g. `~/.claude` or an enrollment home. */
    slot: string;
    /** Pool account id whose credential currently sits in this slot. */
    accountId: string;
    /** ISO — when this assignment was last recorded (a swap/seed into this slot). */
    since: string;
    /** ISO — last identity-oracle confirmation of this slot's tenant; null = never verified. */
    lastVerifiedAt: string | null;
    /** Excluded from balancing — oracle was unavailable, or a divergence was detected. */
    quarantined: boolean;
}
/** One journal entry — in-flight swap phases + the last N completed (pruned at commit). */
export interface CredentialLedgerJournalEntry {
    /** Monotonic sequence within the ledger (== the version at write time). */
    seq: number;
    /** The operation this entry belongs to. */
    op: 'seed' | 'swap' | 'set-default' | 'quarantine' | 'unquarantine' | 'restore';
    /** The phase of that operation (swap is multi-phase per §2.3; others are single-shot). */
    phase: 'begin' | 'staged' | 'exchanged' | 'verified' | 'done' | 'aborted';
    /** Slots this entry touched (ordered). */
    slots: string[];
    /** Free-text detail (never a credential — names/ids/reasons only). */
    detail?: string;
    /** ISO timestamp. */
    at: string;
}
export interface CredentialLocationLedgerStore {
    version: number;
    assignments: CredentialAssignment[];
    journal: CredentialLedgerJournalEntry[];
}
/**
 * Result of probing a slot's CURRENT credential blob for its owning account.
 * Implemented in Step 3 against `GET /api/oauth/profile` (the Anthropic OAuth profile endpoint).
 *
 * CONTRACT (§2.11): `email` set === identity-confirmed; `unavailable:true` for EVERY other
 * outcome (timeout/401/403/429/5xx/missing-or-empty-or-nonstring email/unparseable). The
 * oracle NEVER reports a "mismatch" — an unverifiable slot is quarantine-never-repair, never
 * a guess.
 */
export interface IdentityOracleResult {
    email?: string;
    unavailable?: boolean;
    reason?: string;
}
export interface IdentityOracle {
    /** Resolve which account email the credential blob currently in `slot` belongs to. */
    resolveSlotTenant(slot: string): Promise<IdentityOracleResult>;
}
/** Narrow view of the subscription pool the ledger needs (satisfied by SubscriptionPool). */
export interface LedgerPoolAccount {
    id: string;
    email?: string;
    configHome: string;
    framework?: string;
}
export interface LedgerPoolView {
    list(): LedgerPoolAccount[];
}
/** Attention-item shape (mirrors AgentWorktreeDetector.AttentionItemInput). */
export interface CredentialLedgerAttentionInput {
    id: string;
    title: string;
    summary: string;
    description?: string;
    category: string;
    priority: 'URGENT' | 'HIGH' | 'NORMAL' | 'LOW';
    sourceContext?: string;
}
export interface CredentialLocationLedgerDeps {
    /** Agent stateDir (e.g. `.instar`). The ledger lives at `<stateDir>/credential-locations.json`. */
    stateDir: string;
    /** The subscription pool — used to map a probed email → accountId during seed/recovery. */
    pool: LedgerPoolView;
    /** Identity oracle for seeding/recovery (Step 3 implementation). */
    oracle: IdentityOracle;
    /** Emit a HIGH attention item (typically telegramAdapter.createAttentionItem). */
    emitAttention?: (item: CredentialLedgerAttentionInput) => void | Promise<void>;
    /** Injectable clock for deterministic tests. */
    now?: () => string;
}
/** Thrown when a mutation is attempted while the ledger is in UNKNOWN mode (fail-closed). */
export declare class CredentialLedgerUnknownModeError extends Error {
    constructor(message: string);
}
/** Outcome of a seed/recovery probe for one slot (returned for caller observability + tests). */
export interface SeedSlotOutcome {
    slot: string;
    result: 'assigned' | 'unavailable' | 'ambiguous' | 'unknown-email';
    accountId?: string;
    email?: string;
    reason?: string;
}
/** Outcome of a periodic NON-DESTRUCTIVE identity re-verification (auditIdentities) for one slot. */
export interface IdentityAuditOutcome {
    slot: string;
    /**
     * - `refreshed` — healthy slot re-confirmed; lastVerifiedAt stamped fresh (the core fix).
     * - `recovered` — a quarantined/tenant-less slot now resolves cleanly; assignment restored + unquarantined.
     * - `diverged-quarantined` — a healthy slot's credential now belongs to a DIFFERENT account (confirmed login change) → quarantined.
     * - `unverifiable-quarantined` — a healthy slot's email became ambiguous/unknown → quarantined.
     * - `unavailable-held` — oracle unavailable/threw; a healthy slot is HELD (never quarantined on a transient probe failure).
     * - `still-quarantined` — an already-quarantined slot still can't be cleanly resolved.
     */
    result: 'refreshed' | 'recovered' | 'diverged-quarantined' | 'unverifiable-quarantined' | 'unavailable-held' | 'still-quarantined';
    accountId?: string;
    email?: string;
    reason?: string;
}
/** Aggregate report of one auditIdentities pass (returned + retained for the status surface). */
export interface IdentityAuditReport {
    at: string;
    outcomes: IdentityAuditOutcome[];
    refreshed: number;
    recovered: number;
    quarantined: number;
    /** Held (transient) + still-quarantined — slots the pass could not freshly confirm. */
    unresolved: number;
}
export declare class CredentialLocationLedger {
    private readonly storePath;
    private readonly pool;
    private readonly oracle;
    private readonly emitAttention?;
    private readonly now;
    private store;
    /** UNKNOWN mode — set when the on-disk ledger was present but corrupt. Fail-closed for moves. */
    private unknownMode;
    /** Dedupe the UNKNOWN-mode attention item so a re-read storm raises it once. */
    private unknownAttentionRaised;
    /** Last NON-DESTRUCTIVE identity audit pass (auditIdentities) — surfaced in status. */
    private lastAuditReport;
    /** In-memory index: accountId → slot (rebuilt on every mutation/load). */
    private slotByAccount;
    /** In-memory index: slot → accountId. */
    private accountBySlot;
    constructor(deps: CredentialLocationLedgerDeps);
    private load;
    private save;
    private reindex;
    /** Current slot holding `accountId`'s credential, or null (UNKNOWN mode / never-seeded). */
    slotOf(accountId: string): string | null;
    /** Account currently tenanting `slot`, or null (UNKNOWN mode / never-seeded). */
    tenantOf(slot: string): string | null;
    isUnknownMode(): boolean;
    /** True when the ledger has at least one assignment (has been seeded). */
    isSeeded(): boolean;
    getAssignments(): readonly CredentialAssignment[];
    getAssignment(slot: string): CredentialAssignment | null;
    getJournal(): readonly CredentialLedgerJournalEntry[];
    get version(): number;
    private assertMutable;
    /** Record (or re-point) `slot`'s tenant to `accountId`. Single-writer; bumps version. */
    recordAssignment(slot: string, accountId: string, opts?: {
        verifiedAt?: string | null;
        op?: CredentialLedgerJournalEntry['op'];
    }): CredentialAssignment;
    /** Mark a slot quarantined (excluded from balancing) — oracle unavailable / divergence. */
    quarantineSlot(slot: string, reason: string): void;
    /** Lift a quarantine after a clean re-probe. */
    unquarantineSlot(slot: string): void;
    /** Stamp a slot's lastVerifiedAt after an identity-oracle confirmation. */
    markVerified(slot: string, at?: string): void;
    /** Append a journal entry for an in-flight swap phase (used by the executor in Step 5). */
    appendJournal(entry: Omit<CredentialLedgerJournalEntry, 'seq' | 'at'> & {
        at?: string;
    }): CredentialLedgerJournalEntry;
    private bumpAndJournal;
    /** Keep all non-terminal (in-flight) entries + the last MAX_COMPLETED_JOURNAL terminal ones. */
    private pruneJournal;
    /**
     * Derive each slot's tenant by probing its CURRENT credential via the oracle, then mapping
     * email → accountId through the pool. Clears UNKNOWN mode on a clean pass. Per §2.2:
     *   - oracle unavailable for a slot → quarantine that slot (never guess), continue.
     *   - email maps to exactly one claude-code account → record the assignment (verified now).
     *   - ambiguous (≥2 accounts share the email) or unknown email → REFUSE auto-assign for that
     *     slot + raise an attention item; never guess.
     * Slots = the distinct enrollment `configHome`s of claude-code pool accounts.
     */
    seedFromOracle(): Promise<SeedSlotOutcome[]>;
    /**
     * Periodic, NON-DESTRUCTIVE identity re-verification (the §2.4 scheduled identity audit).
     *
     * Unlike `seedFromOracle()` — which WIPES + rebuilds the whole ledger — this re-probes each
     * EXISTING tracked slot and updates ONLY its verification/quarantine state. It is the missing
     * piece that keeps a healthy slot's `lastVerifiedAt` FRESH: without a periodic re-stamp the
     * rebalancer's `targetVerifiedRecent` gate (default 6h) decays every slot to "not recently
     * verified", leaving every objective (wall-rescue AND use-it-or-lose-it drain) with zero
     * eligible targets — a permanently-inert optimizer. It ALSO lets a slot that has since become
     * resolvable EXIT quarantine.
     *
     * Safety direction (§2.11 never-guess / quarantine-never-repair):
     *   - oracle unavailable/throws → HOLD: NEVER quarantine a currently-healthy slot on a
     *     transient probe failure (that would itself induce the inert-rebalancer failure this
     *     method exists to prevent). An already-quarantined slot stays quarantined.
     *   - email → exactly one pool account:
     *       · matches the slot's current healthy tenant → markVerified (refresh — the core fix).
     *       · slot is quarantined / tenant-less → RECOVER: record the assignment verified-now and
     *         lift the quarantine.
     *       · matches a DIFFERENT account than the recorded healthy tenant → a CONFIRMED login
     *         divergence (the oracle never guesses a mismatch — an `email` is identity-confirmed)
     *         → quarantine (safe direction).
     *   - ambiguous / unknown-email on a currently-healthy slot → quarantine (its login became
     *     unrecognizable); an already-quarantined slot stays quarantined.
     *
     * This NEVER triggers a credential swap — it only refreshes the ledger verification state the
     * rebalancer reads on its next pass. No-op in UNKNOWN mode (recovery is seedFromOracle's job).
     */
    auditIdentities(): Promise<IdentityAuditReport>;
    /** The last NON-DESTRUCTIVE identity audit pass, or null if none has run. */
    getLastAuditReport(): IdentityAuditReport | null;
    private raiseUnknownModeAttention;
    private raiseSeedRefusalAttention;
}
/**
 * The boot-seed decision (B3a). Pure + isolated so the wiring decision is unit-testable without
 * booting server.ts. Returns true ONLY when the re-pointing feature is enabled AND the ledger is
 * not already seeded — `isSeeded()` is false for BOTH never-seeded and UNKNOWN (corrupt) mode, so
 * a true result also covers the named recovery path, and a seeded ledger is always skipped (so the
 * boot-seed is idempotent across restarts). The actual seed (`seedFromOracle()`) is non-destructive
 * and already unit-tested; this guard is the new logic the wiring introduced.
 */
export declare function shouldBootSeedCredentialLedger(enabled: boolean, isSeeded: boolean): boolean;
/**
 * The periodic identity-audit gating decision (B3c). Pure + isolated so the scheduled-audit wiring
 * is unit-testable without booting server.ts. Returns true ONLY when ALL hold:
 *   - `enabled` — the re-pointing dev-gate is on (strict no-op / no oracle probe on the dark fleet);
 *   - `isSeeded` — there is a ledger to re-verify (boot-seed + recovery is seedFromOracle's job);
 *   - NOT `unknownMode` — a corrupt ledger refuses mutations (auditIdentities is a no-op there anyway);
 *   - NOT `inFlight` — the prior pass has not finished (reentrancy guard; a slow oracle never overlaps).
 * The audit (`auditIdentities()`) is non-destructive (verification state only; zero credential moves)
 * and unit-tested; this guard is the new logic the scheduled wiring introduced.
 */
export declare function shouldRunIdentityAudit(enabled: boolean, isSeeded: boolean, unknownMode: boolean, inFlight: boolean): boolean;
//# sourceMappingURL=CredentialLocationLedger.d.ts.map