/** One parked message. `seq` is minted by the store, monotonic per (scope, handle), stable across
 *  lease/expiry cycles (a re-leased message keeps its number — dedup/ordering key). */
export interface MailboxMessage {
    seq: number;
    /** Sender display name, when the sender had one (SendMessage `senderName` — display only). */
    from?: string;
    content: string;
    sentAt: number;
    /** design/176 — the peer-message forwarding chain (engine-minted hop tokens; typed side channel,
     *  never model-facing text). Presence — even an empty array — marks the record as written through
     *  the sema send gate; an absent field means a pre-176 or FOREIGN-engine record (a shared CC inbox
     *  has other writers): such records deliver with an empty chain and are never retro-admitted —
     *  cross-engine records sit outside the guard's promise domain by ruling. */
    hopChain?: string[];
    /** design/385 — typed peer metadata the drain point's authoritative judgments read (from-mode parity,
     *  reply routing, admission sender key, record kind, the cross-principal trust anchors). Typed side
     *  channel, never model-facing text — encoding it into `content` would let text reach authority.
     *  ABSENT = a pre-385 or foreign record (same semantics as an absent `hopChain`): the drain treats it
     *  as foreign, never retro-admits it. Validated at `append` ({@link readMailboxPeerMeta}). */
    peerMeta?: MailboxPeerMeta;
}
/** design/385 — the typed peer-metadata record on a parked message. ONE optional object, one-time
 *  contract extension; every field optional. Bundled backends persist it verbatim and hand back a
 *  detached copy. */
export interface MailboxPeerMeta {
    /** The sending session's id (reply routing). */
    fromSession?: string;
    /** The delegation-tree ROOT the sender belongs to, stamped by the park seat of a session that
     *  received the frame through its own child uplink — never by the sender. A recipient whose
     *  session IS this root reads the record as a teammate's words (same tree = same admission as the
     *  live uplink: the admission gate runs, the cross-session parity hold does not). */
    fromRoot?: string;
    /** The sender's permission-mode class at send time — the drain judges parity against the RECEIVER. */
    fromMode?: MailboxPeerFromMode;
    /** The admission gate's sender key (rate/dedup axis). */
    senderKey?: string;
    /** Record kind: a peer message runs the parity judgment; notices bypass it and land on the notice face. */
    kind?: MailboxPeerRecordKind;
    /** Cross-principal delivery only — the SENDER's scope, the typed trust anchor for attribution. */
    fromScope?: string;
    /** Cross-principal delivery only — the delivery gate's receipt id (audit back-reference). */
    gateReceiptId?: string;
    /** Notice-kind records only (design/385 §4.4 / §5.2): the typed notice the requester's drain renders
     *  — a delivery receipt's state (`kind: "delivery_notice"`) or an idle notice's kind
     *  (`kind: "idle_notice"`). The record's `content` is a human-readable mirror; THIS is the authority. */
    notice?: MailboxPeerNoticeMeta;
    /** `peer_message` only — the SENDER marked it urgent. The recipient's drain presents an urgent record
     *  ahead of the non-urgent ones of the same pass and counts it on the inbox notice; it never changes
     *  WHEN the recipient reads (the recipient's setting decides that, never the sender). */
    urgent?: boolean;
}
/** design/385 — the typed body of a notice-kind record. `state` is drawn from the receipt states on a
 *  `delivery_notice` (`held|denied|expired|delivered|refused|dropped`) and from the idle kinds on an
 *  `idle_notice` (`idle|exited|unavailable|expired`); the validator admits the union, the drain checks
 *  the pairing (a state that does not fit its kind is settled `notice_unrouted`, never rendered). */
export interface MailboxPeerNoticeMeta {
    state: string;
    /** delivery_notice: the box seq of the SENDER'S message this receipt is about. */
    refSeq?: number;
    /** delivery_notice: the recipient's display label (as its directory row spells it). */
    recipient?: string;
    /** delivery_notice `dropped`: the ingress guard's reason. */
    dropReason?: string;
    /** idle_notice `idle`/`exited`: when the target finished its turn (epoch ms). */
    finishedAt?: number;
    /** idle_notice `idle`: the target's one-line detail (already bounded by the producer). */
    detail?: string;
    /** idle_notice `expired` (the requester answering ITSELF): the label the ask carried for the target
     *  that never answered — the record's author is the requester, so the target must be named here. */
    target?: string;
}
export declare const MAILBOX_PEER_FROM_MODES: readonly ["bypass", "prompting"];
export type MailboxPeerFromMode = (typeof MAILBOX_PEER_FROM_MODES)[number];
/**
 * design/385: `peer_message` runs the parity judgment; `idle_subscription` registers the sender as an
 * idle subscriber of the box owner (§5.2, the store form of CC's `notify_when_idle` frame);
 * `idle_notice` / `delivery_notice` land on the notice face (rendered, never enveloped as a peer's words).
 *
 * @contract mailbox.engine_frame_is_not_a_peers_words — an `engine_frame` record is the ENGINE's own
 * task-notification frame parked durably (a child's terminal, a monitor batch, a workflow settle),
 * written by the park seat of the session that owns the box and only over a deployment that vouches
 * every drainer of the box reads the codec (`RunnerDeps.sessionBoxEngineFrames`). Its `content` is the
 * versioned codec text (`encodeEngineFrame`), never a message: the drain decodes it and re-injects the
 * frame through the engine's own notification lane — no parity judgment, no admission gate, no peer
 * envelope — and a codec version this engine does not read STOPS the drain at that record (the box
 * waits for an engine that reads it) rather than rendering engine speech as a peer's words. Only the
 * engine's own lanes (`background_agent` / `monitor` / `workflow`) may be parked this way; an
 * `external` event never is.
 */
export declare const MAILBOX_PEER_RECORD_KINDS: readonly ["peer_message", "idle_notice", "delivery_notice", "idle_subscription", "engine_frame"];
export type MailboxPeerRecordKind = (typeof MAILBOX_PEER_RECORD_KINDS)[number];
/** The `notice.state` vocabulary the validator admits (the union of both notice families; the drain
 *  checks the kind↔state pairing). Spelled here, beside the record kinds, so the store contract is
 *  self-contained — the lane's own closed sets re-derive from these. */
export declare const MAILBOX_PEER_NOTICE_STATES: readonly ["held", "denied", "expired", "delivered", "refused", "dropped", "idle", "exited", "unavailable"];
export type MailboxPeerNoticeState = (typeof MAILBOX_PEER_NOTICE_STATES)[number];
/** The `append` refusal code for a malformed `peerMeta` (design/385): a garbage record is refused up
 *  front with this code, never silently stripped or stored — a drain that reads a half-typed record
 *  would judge on fabricated inputs. */
export declare const MAILBOX_INVALID_PEER_META_CODE = "mailbox.invalid_peer_meta";
/** The lane-mount refusal code: the cross-session lane asked for a backend that does not declare
 *  cross-process safety ({@link mailboxCrossProcessMountVerdict}). */
export declare const MAILBOX_CROSS_PROCESS_UNSAFE_CODE = "mailbox.cross_process_unsafe";
/**
 * Validate + detach an appended `peerMeta`. `undefined` ⇒ `undefined` (absent record). Anything else
 * must be a plain object whose keys are all known, whose present values are non-empty strings, with
 * `fromMode`/`kind` drawn from their closed sets; an `undefined`-valued key counts as absent. Any other
 * shape throws {@link MailboxStoreError} with {@link MAILBOX_INVALID_PEER_META_CODE} — every bundled
 * backend calls this BEFORE touching storage, so a refused append has zero side effects. Unknown keys
 * are refused on purpose: the object is the contract's one typed slot, and a key nobody declared is
 * either a typo or a newer schema this engine cannot judge on — both are loud, not silent.
 */
export declare function readMailboxPeerMeta(raw: unknown): MailboxPeerMeta | undefined;
/** A detached copy of a persisted record's `peerMeta` (absent stays absent) — the read-side twin of
 *  {@link readMailboxPeerMeta}, so a lease consumer's mutation never reaches the stored record. */
export declare function cloneMailboxPeerMeta(meta: MailboxPeerMeta | undefined): MailboxPeerMeta | undefined;
/** The enqueue refusal code of the pre-delete clause (see {@link MailboxStore} and
 *  {@link MailboxStoreError}) — the ONE place it is spelled, so an out-of-repo store twin imports it
 *  instead of value-copying the string (same posture as `STALE_RUNNING_REAP_ATTRIBUTION`: a shared
 *  symbol makes drift impossible rather than merely caught). */
export declare const MAILBOX_TOMBSTONED_RECIPIENT_CODE = "mailbox.recipient_tombstoned";
/**
 * Typed store error so callers branch on `code` (mirrors `BackgroundAgentStoreError`).
 *
 * `recipient_tombstoned` is the ENQUEUE refusal a backend raises when the recipient it addresses is
 * in a deployment's PRE-DELETE state — the window a retention/deletion cascade opens when it has
 * decided a session (and every row hanging off it) is going away but has not finished removing the
 * rows. An `append` accepted in that window returns a seq to the sender — a durable receipt — for a
 * message the cascade then deletes before any consumer could ever lease it. The refusal is what
 * turns that into a fact the sender can act on.
 *
 * A backend may raise the same code with a plain `Error` carrying `.code`; consumers branch on the
 * string, not on this class (a cross-process/out-of-repo store cannot hand back an instance).
 */
export type MailboxStoreErrorCode = typeof MAILBOX_TOMBSTONED_RECIPIENT_CODE | typeof MAILBOX_INVALID_PEER_META_CODE | typeof MAILBOX_CROSS_PROCESS_UNSAFE_CODE;
export declare class MailboxStoreError extends Error {
    readonly code: MailboxStoreErrorCode;
    constructor(code: MailboxStoreErrorCode, message: string);
}
/** A leased batch: the messages a claim winner owns for delivery, plus the ack cursor. */
export interface MailboxLease {
    messages: MailboxMessage[];
    /** Ack everything ≤ this seq once the revived cycle has durably received the batch. */
    maxSeq: number;
}
/** REF-D18: one appended mailbox message — the SINGLE shape the interface method and both the
 *  In-Memory and File implementations reference. */
export interface MailboxAppendMessage {
    from?: string;
    content: string;
    sentAt: number;
    /** design/176 — see {@link MailboxMessage.hopChain}. */
    hopChain?: string[];
    /** design/385 — see {@link MailboxMessage.peerMeta}; validated by {@link readMailboxPeerMeta}. */
    peerMeta?: MailboxPeerMeta;
}
/**
 * The pluggable mailbox seam (design/151 §7.1). Contract notes for implementations (file/pg):
 * - `append` is durable-first (a returned seq means the message survives a crash); seq is monotonic
 *   per (scope, handle) and never reused WITHIN a box's lifetime (across lease/ack cycles the
 *   high-water mark persists). `drop` ENDS the lifetime — a later rebuild restarts at 1 (server[1523]
 *   定谳: the row died with the box, dedup keys carry the handle, and a* handles are 64-bit-random).
 *   RB-90 (2026-07-25, red probe) SPLITS `reap` back out of that sentence: the two were grouped, but
 *   only `drop` is preceded by the row's death. `reap` is an age-based sweep that runs while the
 *   recipient is alive and may hold a lease, so restarting seq there let a stale ack (up to the
 *   pre-sweep high-water) delete a brand-new message no consumer had ever been shown — the "a parked
 *   message is never lost" promise at the top of this file, broken silently. `reap` therefore CLEARS a
 *   box and KEEPS its high-water mark; both bundled impls do so.
 * - `claimLease` atomically marks every currently-visible (unleased or lease-expired) message as
 *   leased by `owner` until `now + ttlMs`, returning them in seq order. Empty mailbox → null.
 *   A second claim by a DIFFERENT owner while a live lease exists returns null (single consumer
 *   per window — the row claim-CAS already guarantees one revival winner; this is the belt); the
 *   SAME owner re-claiming RENEWS the lease and picks up any newer messages (the crash-retry form
 *   — server[1523] 定谳: both bundled impls behave so, the contract follows the implementations).
 * - `ack` deletes every message with seq ≤ upToSeq (the winner calls it at ATTACH success, never
 *   before — §7.1: ack-late is what makes a crashed revival lossless).
 * - `releaseLease` (best-effort) returns a lease early (spawn preflight failed before delivery).
 * - `reap` ages out whole mailboxes by newest-message age (explicit policy). It is the BACKSTOP, not
 *   the only reclaim path: the winning-delete arm of `reapDurableAgents` calls {@link MailboxStore.drop}
 *   for the row it deletes, in the same sweep (F-13 — the row's mailbox dies with the row). That call
 *   is ADVISORY: a drop fault never blocks the reap, and a box orphaned that way still ages out here.
 *   `drop` therefore has a real caller — an implementation that stubs it strands mailboxes until the
 *   age policy catches them.
 * - PRE-DELETE STATE (optional capability, additive contract extension): a backend that can SEE its
 *   recipient's lifecycle — one whose rows live alongside the session rows a retention/deletion
 *   cascade removes — must refuse `append` for a recipient the cascade has already tombstoned,
 *   CODED: an `Error` whose `code` is `"mailbox.recipient_tombstoned"` ({@link MailboxStoreError} is
 *   the bundled shape; consumers branch on the string). Accepting is the failure this clause names:
 *   `append` is durable-first, so the returned seq is a receipt for a message the cascade deletes
 *   before any consumer can lease it — a delivery promised to the sender and kept to nobody, which
 *   no other face reports. The refusal is per RECIPIENT and enqueue-only: a tombstone belongs to one
 *   `(scope, handle)` — the same two-part identity every other method here is keyed by, so it says
 *   nothing about a sibling handle NOR about the same handle under another scope — and it is not a
 *   wipe (whatever is already parked stays under the same lease/ack rules until the cascade removes
 *   the box). A backend with NO view of that
 *   lifecycle (the two bundled ones, the CC inbox adapter) has nothing to refuse and keeps accepting
 *   — the clause fixes the SPELLING of the refusal, so a deployment reads one code instead of a
 *   per-backend dialect. Acceptance kit: `mailboxTombstonedRecipientContract`.
 * - PEER METADATA (design/385, additive): `append` takes an optional typed `peerMeta`; a backend
 *   persists it and hands it back on `claimLease` byte-for-byte (a detached copy), absent stays absent,
 *   and a malformed value is REFUSED with `"mailbox.invalid_peer_meta"` before any side effect
 *   (`readMailboxPeerMeta` is the one validator; bundled backends call it first). T1 cases in
 *   `mailboxStoreContract` cover the pass trip, the refusal and the aliasing.
 * - CROSS-PROCESS SAFETY (design/385, optional capability declared on the store): the session-box
 *   drain shares ONE box between several OS processes. A backend that is correct under that sharing —
 *   seq minted once across processes, lease/ack never crossing, one process's housekeeping never
 *   discarding another's durable append — declares `crossProcessSafe: true` AFTER passing
 *   `mailboxCrossProcessContract` (the T2 kit that pins the whole set, not "has a lock"). A backend
 *   that does not declare it is refused by the lane at mount time with a named reason
 *   ({@link mailboxCrossProcessMountVerdict}) — never silently mounted on luck.
 */
/** #1036 — what {@link MailboxStore.ack} answers: did this call actually consume the prefix?
 *  `false` = the lease fence refused it (this caller does not hold the box), so nothing was deleted
 *  and the records are still parked for whoever does. */
export interface MailboxAckResult {
    readonly acked: boolean;
}
export interface MailboxStore {
    /** design/385 — cross-process safety declaration (see the interface notes). `true` = this backend
     *  passed `mailboxCrossProcessContract` over real OS processes; `false` = deliberately process-local
     *  (the in-memory reference); absent = undeclared (the CC inbox adapter). Read by
     *  {@link mailboxCrossProcessMountVerdict}; any other value is a malformed declaration and refuses. */
    readonly crossProcessSafe?: boolean;
    /** Durably park one message. Refuses `"mailbox.recipient_tombstoned"` when the backend can see
     *  that its recipient is in the deployment's pre-delete state (see the interface notes above). */
    append(scope: string, handle: string, msg: MailboxAppendMessage): Promise<number>;
    claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
    /**
     * Consume the leased prefix up to `upToSeq` (the records are DELETED). Fenced by the current lease:
     * a caller that does not hold it consumes nothing.
     *
     * #1036 — the fence's verdict is REPORTED (`{acked:false}`), not silent. The fence itself is
     * older (MB1): it exists so a stale owner whose lease a live rival already took over cannot delete
     * the rival's messages. What was missing is that a no-op and a success were the same `void` to the
     * caller, so a drain that had lost its lease would clear its own watermark as if the store had
     * obeyed it — the one shape where "the records stay parked" turns into "the records are forgotten".
     * Implementations report `{acked:true}` only when they actually consumed the prefix.
     */
    ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<MailboxAckResult>;
    releaseLease(scope: string, handle: string, owner: string): Promise<void>;
    /** Observation face (the "N pending" receipt figure) — count of currently-visible messages. */
    peekCount(scope: string, handle: string): Promise<number>;
    /** Drop the WHOLE mailbox for (scope, handle) — the row-deletion companion (F-13). */
    drop(scope: string, handle: string): Promise<void>;
    /** Age out mailboxes whose newest message BY `sentAt` (not by array/insertion order — RB-250②)
     *  is older than maxAgeMs. Returns mailboxes dropped. */
    reap(scope: string, now: number, opts?: {
        maxAgeMs?: number;
    }): Promise<number>;
}
/** The lane-mount verdict on a backend's cross-process declaration. */
export type MailboxCrossProcessVerdict = {
    ok: true;
} | {
    ok: false;
    code: typeof MAILBOX_CROSS_PROCESS_UNSAFE_CODE;
    reason: string;
};
/**
 * design/385 — may the cross-session lane (several OS processes draining one session box) mount over
 * this backend? `ok` only for an explicit `crossProcessSafe: true`. Every other shape refuses with a
 * named reason so the host can print it: absent (undeclared), `false` (process-local by design), or a
 * value of the wrong type (a malformed declaration is refused, not read as "probably fine").
 */
export declare function mailboxCrossProcessMountVerdict(store: MailboxStore): MailboxCrossProcessVerdict;
/** RB-250② (2026-07-28) class fix — the box age for `reap` is the MAX `sentAt`, not the
 *  last array element: `sentAt` is caller-supplied, so append order need not be time order, and taking
 *  the tail let a box whose freshest message was mid-array be swept — both bundled backends discarded
 *  a brand-new message while the CC adapter (stores/cc/mailbox-store.ts, the in-package reference)
 *  already reduces over MAX. Non-finite `sentAt` ⇒ undefined ("unknown age ⇒ never reaped", the CC
 *  adapter's documented posture — treating it as very old would let a malformed row delete a live
 *  box). Module-level export only; NOT re-exported from src/index.ts. */
export declare function newestSentAt(messages: readonly MailboxMessage[]): number | undefined;
/** A lease-facing copy of a stored record: every mutable member (hopChain, peerMeta) detached, so a
 *  consumer's mutation can never reach the record a redelivery serves. Shared by the bundled backends. */
export declare function detachMailboxMessage(m: MailboxMessage): MailboxMessage;
/**
 * In-process reference implementation (single-instance / tests). Same posture as
 * {@link import("./background-agent-store.js").InMemoryBackgroundAgentStore}: not default-mounted,
 * detached copies at both boundaries, single-event-loop atomicity.
 */
export declare class InMemoryMailboxStore implements MailboxStore {
    /** Process-local by construction (a Map): never a cross-process box, said so explicitly. */
    readonly crossProcessSafe = false;
    private boxes;
    private key;
    private box;
    append(scope: string, handle: string, msg: MailboxAppendMessage): Promise<number>;
    claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
    ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<MailboxAckResult>;
    releaseLease(scope: string, handle: string, owner: string): Promise<void>;
    peekCount(scope: string, handle: string): Promise<number>;
    drop(scope: string, handle: string): Promise<void>;
    reap(scope: string, now: number, opts?: {
        maxAgeMs?: number;
    }): Promise<number>;
}
