import type { RunInternals, RunnerSelfSeat } from "../core/runner/contracts.js";
import type { TaskSpec } from "../core/types.js";
/** design/122 D2 — per-child resume cap (default 8): a child can be revived at most this many times. */
export declare const SUBAGENT_RESUME_CAP = 8;
/**
 * design/122 D1 — one RETAINED child ledger row. `specSnapshot` is a FROZEN plain value-copy of the spawn
 * childSpec (r1-m5: retaining the live `ctx`/spec-builder closures would pin the parent run's whole prepare
 * graph — harness ref, tool wrappers — in memory for up to ttl×max); `internalsSnapshot` is the spawn-time
 * trusted internals copy (parentToolCallId / onForwardEvent / onSubagentSpawn — the grandchild chain stays
 * unbroken on resume, D2). `release` captures the DELEGATION runner (deployment-scoped, allowed) and
 * performs the unpin + explicit release that restores throwaway semantics.
 */
export interface SubagentRetainEntry {
    childSessionId: string;
    /**
     * Dual-runner wake fix — the DELEGATION runner that OWNS the child session (its `sessions` store minted
     * and holds `childSessionId`, and its run face executes resumes). The Agent tool can be CALLER-mounted
     * with its own runner (e.g. a server's throwaway in-memory subRunner) while SendMessage/AgentTranscript
     * are core-mounted with the HOST runner — two DIFFERENT session stores. A resume/transcript acquire on
     * the host runner's (durable) store then fails `not_found` for a child that only ever lived in the spawn
     * runner's store ("resumable:true 停机帧 → resume.session_not_found"). Resume MUST go through this runner
     * when present; the mount runner is only the fallback for pre-existing rows. In-process pointer only —
     * retain is NOT durable, so cross-process semantics are unchanged (the ledger dies with the process →
     * resume.evicted, the 1.283 ruling). Lifetime: the entry's `release` closure already captures the same
     * runner, so this adds no new retention class.
     */
    runner?: RunnerSelfSeat;
    agentName?: string;
    /** fix-C5: PER-ENTRY retain TTL override (ms). A SESSION-scoped ledger is shared by two purposes with
     *  different windows (failure-evidence retain = FAILED_SESSION_RETAIN_TTL_MS vs a session-scoped bg
     *  child's opt-in retain = the run's configured ttl); the ledger-level `ttlMs` is fixed by whichever
     *  purpose CREATED the ledger first, which silently truncated (or stretched) the other's window.
     *  Expiry (lazy sweep + active timer) uses this when present, the ledger default otherwise. */
    ttlMs?: number;
    specSnapshot: Readonly<TaskSpec>;
    internalsSnapshot: RunInternals;
    /** True while the initial run OR a resume run is in flight (resume re-entry rejects `steering.still_running`). */
    running: boolean;
    /** True once the initial run settled at least once. */
    settled: boolean;
    /** Last settle timestamp (epoch ms) — the retain-TTL base. */
    settledAt: number;
    resumeCount: number;
    /** design/144 §1.1 — the child's STOP-CYCLE number (207-equivalent per-stop notification identity).
     *  Minted 1 at registration (the spawn run's settle notifies cycle 1); every resume that actually
     *  LAUNCHES bumps it, so the resumed run's settle notifies 2, 3, … The background_agent notification
     *  producers ride it as `TaskNotificationPayload.seq`, which uniquifies the `task_id:status[:seq]`
     *  dedup key per stop cycle — without it a stop→wake→stop chain's second `completed` frame collided
     *  with the first and was silently swallowed by the SystemInjectionQueue/inbox/shell dedup layers. */
    cycleSeq: number;
    /** design/144 §1.2 (child gate) — the PARKED completion notification of this child's spawn settle:
     *  present while a `completed` frame sits deferred behind the child's still-running background tasks
     *  (owner key = childSessionId). `seq` is the parked frame's stop-cycle number (park does not change
     *  cycle semantics); `cancel` unsubscribes the registry quiescence watcher and voids the frame.
     *  RULING (recorded in design/144 §1.2): a resume/wake VOIDS the parked frame — the wake opens the
     *  next stop cycle, whose own settle produces this child's next frame; delivering the stale parked
     *  "completed (idle)" after the child is running again would break "one cycle, exactly one frame".
     *  1.284 C2 RULING (宁发勿丢): entry EVICTION (TTL / LRU / abandon / disposeAll / session reap) voids
     *  the park PROMISE, not the frame — `flush` delivers the park-time snapshot IMMEDIATELY (summary
     *  undecorated, honest ungated degradation) and unsubscribes the quiescence watcher; the pre-fix
     *  leak left the watcher alive (a late quiescence poke delivered a ZOMBIE notification for an entry
     *  long gone, and a never-settling grandchild pinned watcher + captured frame forever). */
    deferredNotify?: {
        seq?: number;
        cancel: () => void;
        flush: () => void;
    };
    /** The in-flight resume run's abort controller (parent-terminal cleanup aborts it — r1-M2). */
    activeAbort?: AbortController;
    /** Unpin + explicitly release the retained session (best-effort; the TTL reaper is the backstop). */
    release: () => Promise<void>;
}
/**
 * HRD-AGO-5 — which DETACHED (fire-and-forget) cleanup failed. The ledger frees slots synchronously and
 * releases the session in the background; every one of those background calls can reject, because
 * {@link SubagentRetainEntry.release} is caller-supplied and reaches a session store / pin.
 */
export type RetainEvictionTrigger = "ttl_sweep" | "ttl_timer" | "lru_overflow" | "session_ledger_lru" | "session_reap";
/** HRD-AGO-5 — deployment-side sinks for the retain ledger's background failures. */
export interface RetainLedgerHooks {
    /**
     * A detached eviction's `release` (unpin + session release) FAILED. The row is already gone from the ledger
     * (containment is synchronous), so this is not recoverable state — it is the honest signal that a retained
     * session may still be pinned in the session store, which was previously either an `unhandledRejection` on
     * the host process or nothing at all. A throwing sink is swallowed (it cannot strand the reaper).
     */
    onEvictionError?: (info: {
        parentToolCallId?: string;
        trigger: RetainEvictionTrigger;
        error: Error;
    }) => void;
}
/**
 * design/122 D1 — the parent-run-scoped RETAIN ledger (`TaskSpec.retainSubagentSessions`). Created by
 * `prepareTask` when the spec opts in, threaded to delegation tools as the TRUSTED `ctx.subagentRetain`,
 * and disposed by the Runner in the task's terminal `finally` (D4: abort in-flight resumes + unpin +
 * release every retained session — retain is NOT durable; resume is reachable only while the parent run
 * lives). Leaks are double-bounded by `max` + `ttlMs`.
 */
export declare class SubagentRetainLedger {
    readonly ttlMs: number;
    readonly max: number;
    private entries;
    /** Tombstones: ids that WERE (or would have been) retained but got evicted — their resume rejects `resume.evicted`.
     *  fix-C2: capped FIFO (see `tombstone`) — a long-lived SESSION ledger kept producing unique failed
     *  toolCallIds, so an uncapped set grew without bound for the session's whole life. */
    private evictedIds;
    /** fidelity R4-1: per-entry unref'd TTL timers, armed at `markSettled` — ACTIVE expiry enforcement.
     *  The lazy `sweepExpired` used to be the only reaper, so a ledger nobody touched again (no later
     *  spawn/resume on it) kept its pinned sessions until process end. Cleared on evict/abandon/dispose;
     *  the lazy sweep stays as the backstop (and the only reaper for absurd over-clamp TTLs). */
    private ttlTimers;
    private isDisposed;
    private readonly hooks;
    private failedEvictions;
    private lastFailure;
    /** RB-463: the shared host-callback isolation scope (this ledger's own `onEvictionError` sink lives on it).
     *  The EVICTION counters above are a different quantity and stay: they count evictions that failed, not
     *  invocations of the sink that threw. */
    private readonly notifier;
    constructor(config: true | {
        ttlMs?: number;
        max?: number;
    }, hooks?: RetainLedgerHooks);
    /** HRD-AGO-5 — how many DETACHED evictions failed to release their session on this ledger. Non-zero means
     *  some retained session may still be pinned in the session store. Observable even with no sink wired. */
    get evictionFailureCount(): number;
    /** HRD-AGO-5 — the most recent detached-eviction failure (see {@link evictionFailureCount}). */
    get lastEvictionError(): Error | undefined;
    /** RB-463 diagnostics — how many times the deployment's `onEvictionError` SINK itself threw (distinct from
     *  {@link evictionFailureCount}, which counts the evictions being reported). */
    get evictionSinkFailureCount(): number;
    /**
     * HRD-AGO-5 — record a failure from a DETACHED call this ledger made (or that a module-level helper made on
     * its behalf, e.g. the session-ledger LRU disposal). Counts it, keeps the last error, and offers it to the
     * sink; a throwing sink is swallowed so a broken observer cannot strand the reaper.
     */
    noteDetachedFailure(trigger: RetainEvictionTrigger, error: unknown, parentToolCallId?: string): void;
    /**
     * HRD-AGO-5 — the ONLY way this class issues a background eviction. `evict` removes the row + tombstones it
     * synchronously and only the `release` is async, so the caller never waits; but a bare `void evict(...)` made
     * a rejecting `release` an `unhandledRejection` on the host process (a library killing its embedder over a
     * best-effort cleanup) and left no trace that a session went unreleased. Every detached site routes here.
     */
    private evictDetached;
    /** HRD-AGO-5 — `disposeAll` for a caller that cannot await it (the module-level session-ledger LRU). Same
     *  contract as {@link evictDetached}: containment is synchronous, the failure is reported, never thrown. */
    disposeAllDetached(trigger: RetainEvictionTrigger): void;
    /** fix-C2: hard cap on tombstones. A tombstone is only an anti-resurrection sentinel (an evicted id's
     *  resume reads the honest `resume.evicted` instead of the generic not-retained text) — dropping the
     *  OLDEST one merely degrades that id's error text, so FIFO eviction is a safe bound. */
    private static readonly TOMBSTONES_MAX;
    /** fix-C2: record an eviction sentinel, FIFO-bounded (re-tombstoning an id refreshes its position). */
    private tombstone;
    /** fix-C5: an entry's own retain window when it carries one, the ledger default otherwise. */
    private effectiveTtlMs;
    get disposed(): boolean;
    get size(): number;
    get(parentToolCallId: string): SubagentRetainEntry | undefined;
    wasEvicted(parentToolCallId: string): boolean;
    /** LAZY TTL sweep: evict every settled idle entry whose retain TTL
     *  elapsed (unpin + release + tombstone), so an expired session is freed at the NEXT ledger touch
     *  (every spawn-registration and resume entry call this) rather than only when its own resume is tried.
     *  fidelity R4-1: no longer the only reaper — `markSettled` also arms a per-entry ACTIVE timer, so
     *  expiry fires by time even on a ledger nobody touches again. This sweep stays as the backstop.
     *  The parent-terminal `disposeAll` stays the final backstop. `evict` removes the row + tombstones
     *  synchronously; the release itself is fire-and-forget best-effort. */
    sweepExpired(now?: number): void;
    /** Register a child at SPAWN (running until `markSettled`). Over `max`: the oldest settled idle entry is
     *  evicted (unpin+release); when every slot is still running, the NEW child is NOT retained (tombstoned →
     *  its resume rejects `resume.evicted`) and `undefined` is returned — the caller falls back to throwaway. */
    register(parentToolCallId: string, entry: Omit<SubagentRetainEntry, "running" | "settled" | "settledAt" | "resumeCount" | "cycleSeq" | "activeAbort" | "deferredNotify">): SubagentRetainEntry | undefined;
    /** Flip a child to settled (idle, resumable) — called when the initial run's stream drains.
     *  fidelity R4-1: also (re-)arms the entry's own TTL timer, so the retain window expires by TIME,
     *  not only at the next ledger touch. */
    markSettled(parentToolCallId: string): void;
    /** fidelity R4-1 — arm/re-arm the ACTIVE per-entry TTL reaper (unref'd: never keeps the process
     *  alive). Idempotent with the lazy sweep: the callback re-checks the same settled/idle/elapsed
     *  predicate, so a resume in flight (or an entry already evicted/re-settled) is left alone. */
    private armTtlTimer;
    private clearTtlTimer;
    /** 1.284 C2 — an entry leaving the ledger takes its park promise with it: deliver the parked frame
     *  NOW (undecorated; 宁发勿丢 ruling — see the deferredNotify field doc) and unsubscribe the registry
     *  quiescence watcher. Without this, EVERY release path (TTL timer / lazy sweep / LRU register
     *  overflow / abandon / disposeAll / session reap) leaked the watcher: a late quiescence poke then
     *  delivered a zombie notification for a long-gone entry, and a never-settling grandchild pinned the
     *  watcher + captured frame forever. */
    private flushDeferredNotify;
    /** Drop a row WITHOUT releasing (durable-pause posture: a committed checkpoint owns the session/pin —
     *  releasing here would orphan it, mirroring the delegation tool's own no-release guard). */
    abandon(parentToolCallId: string): void;
    /** Evict one row: flush its parked notification (C2), abort its in-flight resume (if any), unpin +
     *  release the session, tombstone the id. */
    evict(parentToolCallId: string): Promise<void>;
    /** design/122 D4 — parent-run terminal cleanup: abort EVERY in-flight resume + unpin + release EVERY
     *  retained session (throwaway semantics restored; no orphan run burns tokens — same posture as
     *  `abortBackgroundAgentsForOwner`). Idempotent; further register/resume attempts are refused. */
    disposeAll(): Promise<void>;
}
/** Look up (never create) the session-scoped retain ledger for `sessionId` — SendMessage's read side.
 *  fix-C1: a read hit refreshes `touchedAt` (TRUE LRU) — a session kept alive ONLY by SendMessage resumes
 *  otherwise looked stale to the cap eviction below and got its ledger disposed (aborting in-flight
 *  resumes) while 63+ other sessions churned. */
export declare function getSessionRetainLedger(sessionId: string): SubagentRetainLedger | undefined;
export declare function getOrCreateSessionRetainLedger(sessionId: string, config: {
    ttlMs: number;
    max: number;
}, hooks?: RetainLedgerHooks): SubagentRetainLedger;
/** Anchor ② — dispose the session's retain ledger (abort in-flight resumes, unpin + release every retained
 *  child session) and drop it. Idempotent; wired to `TaskRegistry.onSessionReap` at retain time. */
export declare function releaseSessionRetainLedger(sessionId: string): Promise<void>;
export declare function ensureSessionReapHook(registry: import("../core/task-registry.js").TaskRegistry): void;
/**
 * design/122 D2 — the SINGLE resume-prompt builder: a constant TRUSTED frame core owns (operator authority +
 * an unpredictable correlation marker) around the operator content, which stays fenced DATA (same posture as
 * steer — content is never authority, and `delimitUntrusted` neutralizes fence/tag break-out sentinels).
 */
export declare function createResumePrompt(marker: string, content: string, origin?: "operator" | "peer"): string;
