/** A store's retention capability declaration. Absent ⇒ read fail-closed as `"none"`. */
export type RetentionDeclaration = "managed" | "none";
/** The declaration face a storage implementation carries (structural — every store interface's
 *  optional `retention` field conforms). `"managed"` is a PROMISE to honor
 *  {@link ManagedRetentionCapability}; `"none"` is the honest default for stores that cannot delete
 *  on schedule (all bundled in-memory/TTL stores declare it). */
export interface RetentionDeclaring {
    readonly retention?: RetentionDeclaration;
}
/**
 * The deployment's managed-retention policy ({@link import("./types.js").RunnerDeps.retentionPolicy}).
 * Core consumes it ONLY for the startup capability validation (and exposes it to the deployment's
 * scheduler); the engine never deletes data on the task path. Administrative locking rides the
 * locked-config plane (key `"retentionPolicy"`), not a field here — one lock mechanism, not two.
 */
export interface RetentionPolicy {
    /** Retention horizon in days. Fail-loud validated: non-finite or negative refuses the deployment
     *  (`config.retention_policy`); `0` is absurd-but-evaluable (delete-eligible immediately) and legal. */
    maxAgeDays: number;
}
/**
 * What a `retention: "managed"` store PROMISES (contract TYPE — core ships no implementation; the
 * server half consumes this. Reference implementation of the cascade: the server's session-purge
 * order contract). Every method is expected to be:
 *  - **tenant-domain scoped**: work is enumerated and executed per retention domain (tenant/scope),
 *    never as one global sweep;
 *  - **idempotent**: a retried invocation after a crash re-converges (already-deleted rows are
 *    counted, not errors);
 *  - **tombstoned**: deletions leave tombstones so replicas/backups converge instead of resurrecting;
 *  - **audited**: every destructive call is recorded append-only. The IDENTITY of that record —
 *    which occurrence, which of the three operations, at what wall clock — belongs to the side that
 *    WRITES the row, not to the receipt: an implementation records it in the same transaction as the
 *    deletion (recording after the delete would leave an unwitnessed delete on a crash), and it is
 *    the only side that knows its own op, sweep round, and policy generation. The receipt below is
 *    therefore the CALL's three counts, deliberately not an audit row.
 */
export interface ManagedRetentionCapability {
    /** Enumerate the retention domains (tenant/scope keys) this store holds data for. */
    listRetentionDomains(): Promise<readonly string[]>;
    /** CAS-fenced checkpoint expiry for one domain: `pending → expired` strictly by deadline, exactly
     *  one winner per row across replicas, AND release the associated session pins (the reap/unpin
     *  split is the documented gap this contract closes). */
    expireCheckpoints(input: {
        domain: string;
        cutoffMs: number;
    }): Promise<RetentionReceipt>;
    /** Lineage-aware transcript/session deletion: a session tree is deleted only when no live
     *  reference (fork child, background child, workflow run, pending checkpoint) still points into
     *  it — references are re-checked inside the deletion transaction, never assumed from a prior
     *  scan; {@link RETENTION_CASCADE_EXEMPT} is never deleted with one. */
    deleteExpiredSessions(input: {
        domain: string;
        cutoffMs: number;
    }): Promise<RetentionReceipt>;
    /** Delete offloaded tool results whose owning sessions are gone (or past the horizon), skipping
     *  refs still reachable from live transcripts. */
    deleteOrphanToolResults(input: {
        domain: string;
        cutoffMs: number;
    }): Promise<RetentionReceipt>;
}
/**
 * The three counts one destructive retention call produced. NOT an audit row and deliberately
 * identity-free (see the capability's `audited` clause): a scheduler that needs to tell two
 * same-shaped calls apart reads its store's audit records, which the implementation writes with its
 * own occurrence id, operation, and timestamp inside the deleting transaction. Adding those here
 * would ask a return value to know its own call context.
 */
/** @contract retention.cascade_domains — a managed-retention cascade deletes checkpoint rows,
 *  session trees (transcripts and lineage) and orphan tool results; nothing else is session data. */
export declare const RETENTION_CASCADE_DOMAINS: readonly ["checkpoints", "session-trees", "orphan-tool-results"];
/** @contract retention.governance_records_outlive_the_purge — the pollution marker dir, the capture
 *  opt-out record dir and the lineage ledger are governance FACTS about content that exists, not
 *  session data: deleting them makes a one-way privacy record revocable. */
export declare const RETENTION_CASCADE_EXEMPT: readonly ["session-pollution", "session-capture-optout", "lineage.json"];
export interface RetentionReceipt {
    domain: string;
    /** Rows destroyed by THIS call (idempotent retries count already-gone rows as 0, not errors). */
    deleted: number;
    /** Rows intentionally skipped (live lineage references, legal-hold marks — hold semantics are the
     *  scheduler's: when a hold mark exists for a domain, the scheduler skips the domain entirely and
     *  records the skip; this field carries the per-row remainder). */
    skipped: number;
    /** Tombstones written for the deleted rows. */
    tombstones: number;
}
/**
 * Startup validation (and per-prepare backstop): with the retention policy LOCKED and configured,
 * every wired store must declare `retention: "managed"` — a locked policy over a store that cannot
 * delete is refused (`config.retention_capability`), never silently accepted. Also fail-loud
 * validates the policy value itself (`config.retention_policy`). Exported for deployments to call at
 * their own startup; the prepare preflight re-runs it on every leg regardless.
 */
export declare function assertRetentionCapability(input: {
    policy: RetentionPolicy | undefined;
    /** Whether locked-config locks the `retentionPolicy` key (locked-config.ts `LockedKey`). */
    locked: boolean;
    /** The wired stores to check, each with its deployment-facing name for the refusal message.
     *  Accepts any store object (interfaces that predate the declaration conform structurally with
     *  no declaration ⇒ read fail-closed as `"none"`); the declaration read is the one narrow. */
    stores: ReadonlyArray<{
        name: string;
        store: object | undefined;
    }>;
}): void;
