import type { MailboxPeerFromMode } from "../core/mailbox-store.js";
import type { CrossSessionInboundVerdict } from "./cross-session-judge.js";
import { type PeerIdleNoticeKind } from "./peer-notices.js";
import { type PeerClock } from "./peer-held-queue.js";
export interface PeerIdleSubscriber {
    /** The requester's session id (reply routing). */
    readonly requester: string;
    /** The requester's display name as its record carried it (display only). */
    readonly requesterName?: string;
    /** The requester's self-attested mode class (gates the detail line through the parity judge). */
    readonly fromMode?: MailboxPeerFromMode;
    readonly selfSent: boolean;
    readonly requestedAt: number;
}
export interface PeerIdleTargetNotice {
    kind: Exclude<PeerIdleNoticeKind, "expired">;
    finishedAt?: number;
    /** `idle` only, and only when the parity judge DELIVERS for that subscriber (CC gates it on a
     *  pid-verified registry match + parity; sema has parity alone, §7). */
    detail?: string;
}
export interface PeerIdleTargetSink {
    /** Route one notice to one subscriber (the binding appends it to the requester's box). */
    notify: (sub: PeerIdleSubscriber, notice: PeerIdleTargetNotice) => void;
    /** The parity judge over the subscriber's attestation (detail gate, CC `p(d)`). */
    judge: (sub: PeerIdleSubscriber) => CrossSessionInboundVerdict;
    /** The held buffer's size: while > 0 the session is NOT idle to a subscriber (CC `c0e`). */
    heldCount?: () => number;
    /** The last turn's text, for the one-line detail (CC `getLastTurnText`). */
    lastTurnText?: () => string | undefined;
    /** Host announce: a subscription was recorded/refreshed (CC `onSubscribed`). */
    onSubscribed?: (sub: PeerIdleSubscriber, outcome: "recorded" | "refreshed", live: number) => void;
}
export type PeerIdleSubscribeOutcome = "recorded" | "refreshed" | "full";
export interface PeerIdleTarget {
    configure(sink: PeerIdleTargetSink): void;
    configured(): boolean;
    /** CC `tYt` (sans pid classes): same requester ⇒ refresh; table full ⇒ `"full"` (the caller answers
     *  `unavailable`). Already idle ⇒ the fire is (re)scheduled so a late subscriber hears it. */
    subscribe(sub: PeerIdleSubscriber): PeerIdleSubscribeOutcome;
    /** CC `A`: drop subscribers older than the TTL, silently. Returns how many. */
    sweep(now?: number): number;
    /** The session started working (a run began): cancel a pending fire. */
    busy(): void;
    /** The session's run ended with nothing queued: schedule the one-shot `idle` fire (debounced; parked
     *  while the held buffer is non-empty and re-checked). `finishedAt` = when the turn ended. */
    idle(finishedAt: number): void;
    /** A graceful session end: fire `exited` (or `idle`, when the session was in fact idle — CC `$Rt`). */
    exited(finishedAt?: number): void;
    list(): readonly PeerIdleSubscriber[];
    size(): number;
}
export declare function createPeerIdleTarget(opts?: {
    clock?: PeerClock;
    tableCap?: number;
    ttlMs?: number;
}): PeerIdleTarget;
export interface PeerIdleOutstanding {
    /** The target's session id. */
    readonly target: string;
    /** The target's label as the requester knew it at request time (bounded by the producer). */
    readonly label: string;
    readonly requestedAt: number;
}
export interface PeerIdleRequesterSink {
    /** The 12 h timer fired with no newer ask to the same target: the requester answers itself `expired`. */
    expired: (o: PeerIdleOutstanding) => void;
}
export interface PeerIdleRequester {
    configure(sink: PeerIdleRequesterSink): void;
    configured(): boolean;
    /** CC `rYt`: record an outstanding ask (keeps the last 3 per target; the table caps at 32 across
     *  targets — `cap` is the degraded send verdict). */
    request(target: string, label: string): {
        ok: true;
        priors: number;
    } | {
        ok: false;
        reason: "cap";
    };
    /** CC `oYt`: a notice arrived for a target — `idle`/`exited`/`expired` clear every ask to it,
     *  `unavailable` clears the oldest one. Returns the label the requester knew, for rendering. */
    settle(target: string, kind: PeerIdleNoticeKind): {
        cleared: number;
        label?: string;
    };
    labelFor(target: string): string | undefined;
    list(): readonly PeerIdleOutstanding[];
    size(): number;
}
export declare function createPeerIdleRequester(opts?: {
    clock?: PeerClock;
    cap?: number;
    priorsKept?: number;
    ttlMs?: number;
}): PeerIdleRequester;
export interface PeerIdleMachine {
    readonly key: string;
    readonly target: PeerIdleTarget;
    readonly requester: PeerIdleRequester;
}
export declare function peerIdleMachineKey(scope: string, sessionId: string): string;
/** The idle machine of a session (created on first use; `clock` honored at creation). */
export declare function peerIdleMachineFor(scope: string, sessionId: string, opts?: {
    clock?: PeerClock;
}): PeerIdleMachine;
export declare function peerIdleMachineIfAny(scope: string, sessionId: string): PeerIdleMachine | undefined;
/** Test seam: forget every machine (timers are NOT settled — the crash form). Never called by production code. */
export declare function resetPeerIdleMachinesForTests(): void;
