import type { SubagentEditedFile, SubagentStep } from "../agents/subagent-steps.js";
/** Terminal/live usage rollup persisted on the row (mirror of the task_notification/BCE fields). */
export interface BackgroundAgentUsage {
    totalTokens?: number;
    toolUses?: number;
    durationMs?: number;
    tokens?: number;
    turns?: number;
    costMicroUsd?: number;
}
/**
 * One durable background-agent execution row. Field discipline (design/151 §3.1, HIGH-4): revival
 * re-hydration data is LOOKUP KEYS ONLY (`agentType`/`model`/`name` — the CC 2.1.216
 * `.meta.json` sidecar shape) — never a serialized spec and never closures; a tier-3 revival rebuilds
 * the spec the same way a fresh named-teammate spawn would (roster/agent definition + the reviver's
 * deployment assembly).
 */
export interface BackgroundAgentRecord {
    /** Row key: the registry handle (`a…` domain), stable across restarts. */
    handle: string;
    /** Tenant/principal scope — put-time REQUIRED (assertOwnership parity: a row with neither a real
     *  scope nor an owner would be world-readable through the fallback; the registry writer always has
     *  both). */
    scope: string;
    /** task-scoped: the host taskId · session-scoped: the sessionId (registry owner semantics). */
    owner: string;
    sessionScoped: boolean;
    /** The child's store session id — the transcript / resume anchor. Absent at put (the child session
     *  is minted AFTER registration); bound via a follow-up CAS update. */
    sessionId?: string;
    /** Spawn-time root linkage — the durable enumeration key for task-scoped rows
     *  ({@link BackgroundAgentStore.listBySession}) and the restart read leg of the access predicate. */
    parentSessionId?: string;
    /** the ROOT host session of the delegation tree (fixed point down the internals
     *  chain; equals parentSessionId at depth 1). The recovery-face enumeration anchor: after a
     *  restart, intermediate spawner sessions are dead ends — "everything under this host session"
     *  keys on THIS field (listBySession matches it as a second arm), not on an alias walk. */
    rootSessionId?: string;
    parentTaskId?: string;
    name?: string;
    agentType?: string;
    description?: string;
    /** Revival lookup key (CC meta-sidecar parity); populated when known, never required. The team
     *  key is {@link rootSessionId} — one implicit team per session, no column of its own. */
    model?: string;
    /** design/151 §3.1d (HIGH-3): the WRITING process instance id (random per registry instance). A
     *  fallback read of a `running` row whose writer is not the reading instance serves an HONEST
     *  "outcome unknown here" — never a fabricated "running" liveness claim.
     *  S3 revival ORDER OBLIGATION: a cross-process revival must first CAS this field
     *  over to the reviving instance's writerId (the claim), THEN register-with-id — a fresh lane's
     *  first write against a row still carrying the dead process's writerId reads back "foreign" and
     *  poisons itself by design. Claim-CAS first is the only sequence the adjudication admits. */
    writerId: string;
    /**
     * design/153 §7.2c (r6 H-3) — the monotonic OWNERSHIP GENERATION, bumped by every ownership
     * transfer (tier-3 claim, 件3 resume claim finalize, the in-process retained-revive claim). A
     * ROLLBACK is itself a transfer, so every rollback shape mints a NEW epoch too — the reconciliation
     * sweep's reservation/running rollbacks, the parked-reservation rollback (which additionally syncs
     * this process's SURVIVING lane forward so its own later terminal write does not foreignize), and
     * the revival-claim compensation. No rollback restores a pre-claim epoch: an orphaned lane's
     * authority must never become resumable, and restoring the old generation would let the next claim
     * by the SAME writer land on the very (writerId, epoch) pair that lane still holds. What a rollback
     * restores is the pre-claim writerId and row shape — that is what makes the row addressable again;
     * the fresh generation is what keeps the abandoned lane from speaking for it. The
     * durable-lane adopt matches
     * `writerId + writerEpoch` BOTH: a stale lane of the SAME writer (pre-transfer snapshot still in
     * its chain) reads "foreign" instead of adopting the new rev and replaying its old record — the
     * lane-self-clobber ABA the epoch exists to close. Optional: legacy rows (undefined) keep the
     * pre-epoch writerId-only adopt semantics unchanged.
     */
    writerEpoch?: number;
    spawnedAt: number;
    updatedAt: number;
    settledAt?: number;
    /** design/153 §7.1: `"parked"` = durably suspended on a pending approval checkpoint (neither live
     *  nor terminal) — reap-exempt like `running`; cleared to a real state by resume claim, TaskStop
     *  (checkpoint arbitration first), or the reconciliation sweep (checkpoint expired/missing). */
    status: "running" | "parked" | "completed" | "failed" | "killed";
    /** Open enum (task-registry `StopSource` mirror). */
    stoppedBy?: string;
    /** The cross-channel completion correlation id
     *  (task-registry `SemaTaskHandle.completionId` mirror). Written alongside `status`/`settledAt` at
     *  the same durable terminal write that lands them, so a cross-restart fallback read (`TaskOutput`
     *  after the writing process is gone) REPLAYS this exact value — there is no second mint point here,
     *  this field only ever receives what the in-memory handle already minted before the write. */
    completionId?: string;
    /** design/144 §1.1 dedup-key continuity (LOW-15): revival cycles re-mint seq FROM the stored value,
     *  so `task_id:status:seq` keys never collide across restarts. */
    seq?: number;
    /**
     * design/153 §7.1 — the durable-approval checkpoint token this row is parked ON (`status:"parked"`
     * only; park mint writes it, resume claim / terminal settle clears it). The checkpoint token CAS is
     * the MASTER arbitration domain (r4 F-05): every parked-row CAS also verifies THIS binding, and the
     * row never adjudicates a park outcome on its own. A row CAS that loses after the checkpoint was
     * committed is compensated by EXPIRING the checkpoint (no "committed but undiscoverable" orphans).
     */
    parkedCheckpointToken?: string;
    /** design/153 §7.1 — when the park landed (epoch ms). Observability + reconciliation ordering only. */
    parkedAt?: number;
    /** design/153 §7.2 — the in-flight resume claim's compensable-ticket id (parked→running CAS writes
     *  it, a successful attach finalize clears it, a pre-attach failure rolls back BY it). A `running`
     *  row still carrying BOTH this and `parkedCheckpointToken` past the claim lease is a stale claim —
     *  the reconciliation sweep rolls it back to `parked`. */
    parkClaimId?: string;
    summary?: string;
    finalOutput?: string;
    /** The FULL result (workflow.ts NH-1 shape via {@link BackgroundAgentTaskHandle.resultFull}),
     *  set only when it differs from `finalOutput` — the restart/durable-fallback poll path needs the same
     *  pairing the live handle carries, or a process restart loses the very recoverability this field exists
     *  to preserve. */
    finalOutputFull?: string;
    /** The child's failure text (registry `handle.error` mirror; the reap-stop sentinel never lands here).
     *  The stale-running reap flip ALSO writes its attribution here (`??`-guarded) —
     *  `error` is the field the durable serving face renders (`serveDurableAgentRow`), while `summary`
     *  has no reader on that face, so an attribution written only to `summary` was invisible: a host
     *  crash read as a bare `failed` + "(no result text)". */
    error?: string;
    /** The classification triplet the in-memory handle carries under
     *  the SAME names (`errorCode`/`errorRetryable`/`errorKind`; see RB-386②/RB-405 G3 on
     *  {@link import("./task-registry-shared.js").BackgroundAgentTaskHandle}), written to this row at the
     *  same terminal write that lands `error` — so a cross-restart fallback read
     *  ({@link import("./task-registry-agent.js").serveDurableAgentRowLane}) can render the identical
     *  classification clause the live poll does, instead of the row structurally losing it once the
     *  writing process exits (the residual this field set closes). */
    errorCode?: string;
    errorRetryable?: boolean;
    errorKind?: string;
    errorRetryAfterMs?: number;
    resultIsPartial?: boolean;
    recentSteps?: SubagentStep[];
    editedFiles?: SubagentEditedFile[];
    usage?: BackgroundAgentUsage;
    /** The org scopes the row's most recent leg actually mounted. ABSENT is NOT "unknown ⇒
     *  unconstrained": the revival reads an absent field as the ZERO verdict (see
     *  `readDurableOrgAdmission` in memory-admission.ts), so a pre-#22 row and a row whose leg
     *  mounted no org plane both admit nothing on the org axis. Written by the leg itself once its
     *  prepare has adjudicated (task-registry `recordBackgroundAgentOrgAdmission`), so every revival
     *  cycle re-records its own — necessarily narrower — verdict. */
    admittedOrgScopes?: string[];
    /** The explicit org WRITE grant that verdict carried; `null` = the org write face was read-only.
     *  Same absent-reads-as-zero rule as {@link admittedOrgScopes} (absent ⇒ `null`). */
    admittedOrgWriteScope?: string | null;
    /** Optimistic-concurrency revision. The writer always CASes with its OWN held rev (design/151
     *  MED-8): a foreign row (stale leftover / another writer) has an unknown rev, so the CAS loses
     *  instead of clobbering. Bumped by the store on every successful `update`. */
    rev: number;
}
/**
 * The terminal payload a REVIVAL must erase from the row it is re-launching, as ONE list.
 *
 * A revived handle starts a fresh cycle on an existing row: status flips back to `running`, but every
 * field describing how the PREVIOUS cycle ended is still sitting there. Left standing, each one
 * surfaces the old cycle's answer under the new cycle's status — a stale `finalOutputFull` is served
 * in preference to the fresh `finalOutput` by the durable read face, a stale `errorCode`/`errorKind`
 * pair renders a "network / retryable" classification on a row that has since completed cleanly, a
 * stale `completionId` makes two different completions report one correlation id.
 *
 * There are TWO revival entry points — the in-process wake and the cross-process tier-3 claim-CAS —
 * and they maintained two hand-written copies of this list. The copies drifted: `finalOutputFull` and
 * the error triplet were added to one and not the other, and each addition was a separately-found
 * defect of the same shape. One list, both callers, so the next field added to the terminal snapshot
 * can only be forgotten once.
 *
 * Every member is optional on {@link BackgroundAgentRecord} (that is what makes them clearable);
 * identity, lifecycle, concurrency and GOVERNANCE fields are deliberately absent — a revival
 * inherits those. The org-admission freeze (`admittedOrgScopes` / `admittedOrgWriteScope`) is in
 * the governance group on purpose: clearing it would hand the next cycle a blank slate, which is
 * the widening the freeze exists to prevent.
 */
export declare const REVIVED_ROW_CLEARED_FIELDS: readonly ["settledAt", "stoppedBy", "completionId", "finalOutput", "finalOutputFull", "error", "errorCode", "errorRetryable", "errorKind", "errorRetryAfterMs", "resultIsPartial", "summary", "recentSteps", "editedFiles", "usage"];
/** Erase {@link REVIVED_ROW_CLEARED_FIELDS} from a record a revival is about to write back. */
export declare function clearRevivedRowTerminalPayload(record: BackgroundAgentRecord): void;
export declare function announceTranscriptIntegrityGapOnce(handle: string, scope: string | undefined, sink: ((handle: string, scope: string | undefined) => void) | undefined): void;
/** Content-free projection for list reads (design/151 HIGH-1: `summary`/`finalOutput`/`recentSteps`
 *  and friends NEVER ride a list — content is get-by-handle only, behind the full predicate). */
export interface BackgroundAgentRowSummary {
    handle: string;
    owner: string;
    sessionScoped: boolean;
    sessionId?: string;
    parentSessionId?: string;
    rootSessionId?: string;
    name?: string;
    agentType?: string;
    status: BackgroundAgentRecord["status"];
    spawnedAt: number;
    updatedAt: number;
    settledAt?: number;
    usage?: BackgroundAgentUsage;
}
/** Typed store error so callers branch on `code` (mirrors `WorkflowRunStoreError`). */
export declare class BackgroundAgentStoreError extends Error {
    readonly code: "agent_record.already_exists";
    constructor(code: "agent_record.already_exists", message: string);
}
/**
 * design/151 §3.1c — the fallback access predicate (S-1 定谳 class). Mirrors the live registry's
 * `canAccess` axes with ONE remaining durable-only addition: a task-scoped row is readable by its
 * PARENT SESSION (`parentSessionId` leg) — after a restart the host session is the row's only
 * legitimate owner (the spawn-time task ids are dead). The live registry still does not grant the
 * parentSessionId leg (an intermediate session must not steer a sibling turn's running child);
 * the ROOT arm, however, is now on BOTH faces — RB-236 (2026-07-29) aligned the live `canAccess`
 * with this predicate's root-session arm, closing the "running grandchild is not_found to
 * the session that owns the tree" window. Durable rows remain the settled/read/revive face.
 * Cross-session same-scope reads stay not_found (content-axis ruling).
 *
 * Scope is FAIL-CLOSED both ways (`undefined` never matches) — records are put with a real scope.
 */
export declare function canAccessAgentRecord(record: Pick<BackgroundAgentRecord, "scope" | "owner" | "sessionScoped" | "parentSessionId" | "rootSessionId">, access: {
    owner?: string;
    scope?: string;
    sessionId?: string;
}): boolean;
/**
 * The pluggable durable ledger seam. Contract notes for implementations (file/pg):
 * - `put` is create-once: throw {@link BackgroundAgentStoreError} `already_exists` on a live key —
 *   never silently overwrite (a leftover row from a dead process must not be clobbered by a
 *   fresh-minted colliding handle; the writer treats that as a loud no-durability degrade).
 * - `update` is a CAS (`WHERE handle=? AND scope=? AND rev=?`): return false on a missing row, a
 *   scope mismatch (multi-tenant isolation — a wrong-scope update must not win), or a rev mismatch.
 *   On success bump `rev` by EXACTLY 1 and persist (全景 S3c F-3: the tier-3 claim derives the
 *   post-claim rev as `expect.rev + 1` for its lane seed and rollback CAS — a larger bump would
 *   silently downgrade every revival to the adopt-heal slow path and every rollback to the
 *   staleRunning sweep; both bundled impls bump by 1, the contract follows them).
 * - `get`/`listBySession` are RAW row reads keyed by (handle|session, scope) — the full access
 *   predicate ({@link canAccessAgentRecord}) is applied by the ENGINE consumer (registry fallback),
 *   roster-store posture: a store implementation never widens visibility, and a deployment must not
 *   serve raw rows without the predicate.
 * - `reap` deletes only what the policy names; a `running` OR `parked` row is deleted by NO policy —
 *   the `staleRunningMaxAgeMs` arm first CAS-flips a stale `running` to `failed` (host interrupted),
 *   then it ages out as a terminal row. A `parked` row is FULLY reap-exempt (design/153 §7.1: its
 *   writer does not heartbeat — the child is durably suspended, silence is its normal state; the
 *   checkpoint-bound reconciliation sweep, not row age, owns its cleanup). With no options, reap is
 *   a no-op (retention is always an explicit policy).
 *   Sizing: a LIVE writer renews its running rows' `updatedAt` every
 *   `DURABLE_AGENT_HEARTBEAT_MS` (60s — task-registry writer lease), so row age measures SILENCE —
 *   of the WRITER PROCESS, specifically: a wedged child inside a healthy
 *   process keeps renewing and never goes stale, by design (in-process supervision owns that case).
 *   Set `staleRunningMaxAgeMs` ≥ 10× the heartbeat (≥10min). A premature flip still self-heals:
 *   the live writer's settle reads the row back, adopts the bumped rev (writerId match) and
 *   re-CASes the real terminal truth over the reaper's guess.
 *   ⚠️ Row deletion does NOT release the child SESSION (the store cannot reach the session store) —
 *   the blessed reap path is the registry-side orchestration (design/151 §3.1 MED-7 +
 *   C-2): it conditionally DELETES the row under its re-verified rev first, and only a WINNING
 *   delete releases the transcript session (release-first destroyed a live transcript when a
 *   concurrent revive won the CAS after the release). Calling raw `reap` alone strands transcripts.
 *   Cross-process caveat: a revive's durable `running` transition rides the async
 *   lane — between the in-memory revive and its flush, a DIFFERENT process's sweep still holds a
 *   terminal-looking row whose rev the flush has not yet bumped. In-process sweeps are gated on the
 *   live handle; cross-process revival (S3) must claim-CAS FIRST (see {@link BackgroundAgentRecord.writerId}).
 */
/** The stale-running reap flip's attribution, written to BOTH `summary` (legacy
 *  slot, kept for any out-of-repo reader) and `error` (the field {@link canAccessAgentRecord}-gated
 *  durable serving actually renders). One exported constant so the two store implementations (in-memory
 *  here, file twin) can never drift apart (RB-74/RB-111 family). Wording carries CC 2.1.220's three
 *  facts (220:950397): what happened (host process gone mid-run), the consequence (in-process state
 *  lost), and the action (check the worktree/output for partial work before assuming the task landed). */
export declare const STALE_RUNNING_REAP_ATTRIBUTION = "the host process was interrupted while this agent was running \u2014 its in-process state was lost. Check its worktree / output file for partial work before assuming the task landed (stale running row reaped).";
/**
 * REF-D18: the `updateIf` CAS predicate — the SINGLE shape the interface method and both the
 * In-Memory and File implementations reference (previously each re-declared it inline). Mechanism
 * finding this merge closes: a `class X implements Interface` clause does NOT catch a missing
 * optional field when the implementing method re-declares its own narrower inline param type (TS
 * method-parameter bivariance) — this exact duplicate-signature-drift class already produced a
 * shipped double-counting bug in this file's family (RB-74, then RB-87/RB-111 fixed it in one
 * implementation and not the other, same release; see the file-store twin's own comment).
 */
export interface BackgroundAgentUpdateIfExpect {
    rev: number;
    status?: BackgroundAgentRecord["status"];
    parkedCheckpointToken?: string | null;
    parkClaimId?: string | null;
}
/** REF-D18: `reap`'s retention-policy options — same single-shape rationale as {@link
 *  BackgroundAgentUpdateIfExpect} above. */
export interface BackgroundAgentReapOptions {
    /** Delete terminal rows settled longer ago than this. Absent = this arm is not applied. Must be a
     *  finite, non-negative number of milliseconds. */
    maxAgeMs?: number;
    /** Keep at most this many terminal rows, newest first. Absent = this arm is not applied. Must be a
     *  finite, non-negative whole number — see {@link assertBackgroundAgentReapOptions} for why a
     *  malformed value here is the dangerous one. */
    keep?: number;
    /** Flip `running` rows untouched for longer than this to a failed terminal. Absent = not applied.
     *  Must be a finite, non-negative number of milliseconds. */
    staleRunningMaxAgeMs?: number;
}
/** knob-hygiene #98 — refuse a malformed reap policy before any row is touched. Delegates to the one
 *  shared validator ({@link assertRetentionPolicy}) both bundled stores and the sibling mailbox /
 *  workflow-run / roster retention seams run through, so no implementation can drift from another. */
export declare function assertBackgroundAgentReapOptions(opts: BackgroundAgentReapOptions | undefined): void;
export interface BackgroundAgentStore {
    put(record: BackgroundAgentRecord): Promise<void>;
    get(handle: string, scope: string): Promise<BackgroundAgentRecord | null>;
    update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
        rev: number;
    }): Promise<boolean>;
    /**
     * design/153 §7.2c (r6 H-3) — the GUARDED CAS the park/claim lanes require: atomic
     * `WHERE rev [AND status] [AND parkedCheckpointToken] [AND parkClaimId]`. The plain `update`'s
     * rev-only guard is NOT enough there (a rev can move for unrelated reasons between read and
     * write while the park-critical fields changed meaning — reservation, rollback and finalize all
     * bind to the FIELD state, not just the revision). Same success semantics as `update` (bump rev
     * by exactly 1). An `expect` field set to a string matches equality; `null` matches ABSENCE.
     * REQUIRED method (BREAKING for store implementers, announced): a silently-ignoring legacy
     * implementation would fail OPEN on the exact CAS the approval chain's safety rests on.
     */
    updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: BackgroundAgentUpdateIfExpect): Promise<boolean>;
    /** Rows whose durable session anchor (`sessionScoped ? owner : parentSessionId`) OR root anchor
     *  (`rootSessionId` — the whole delegation tree under one host session) equals
     *  `sessionId`, newest-first by `spawnedAt`. Content-free projections (HIGH-1). `opts.status`
     *  filters one state; `opts.limit` keeps the newest N after the sort. */
    listBySession(scope: string, sessionId: string, opts?: {
        status?: BackgroundAgentRecord["status"];
        limit?: number;
    }): Promise<BackgroundAgentRowSummary[]>;
    /** ALL of a scope's rows (content-free, newest-first): the joint-reap orchestration's
     *  enumeration face (`TaskRegistry.reapDurableAgents` — conditional delete first, session release
     *  only after a winning delete). Deployment-op context, not a model-serving face —
     *  model-facing reads stay behind {@link canAccessAgentRecord}. */
    listByScope(scope: string, opts?: {
        status?: BackgroundAgentRecord["status"];
        limit?: number;
    }): Promise<BackgroundAgentRowSummary[]>;
    /** row-precise removal (the joint-reap deletion arm). Returns false on a miss.
     *  `expect.rev` makes it CONDITIONAL — a live writer's concurrent revival (heartbeat
     *  adopt bumps rev) must beat the reaper's stale snapshot, so the joint reap always deletes with
     *  the rev it just re-verified. */
    delete(handle: string, scope: string, expect?: {
        rev: number;
    }): Promise<boolean>;
    /** Enumerate the scopes that currently hold rows, so a deployment-op
     *  retention loop can drive {@link TaskRegistry.reapDurableAgents} per scope without external
     *  bookkeeping (a local backend under multi-tenant use otherwise grows principal-scoped rows
     *  forever). Optional: a store whose deployment tracks scopes elsewhere (pg: SELECT DISTINCT)
     *  may omit it. Deployment-op face, never model-serving. */
    listScopes?(): Promise<string[]>;
    /** Retention sweep for `scope` (explicit policy, see interface notes). Returns rows deleted+flipped. */
    reap(scope: string, now: number, opts?: BackgroundAgentReapOptions): Promise<number>;
}
/**
 * design/153 §7.1 (r4 F-03) — the parked-row ↔ checkpoint reconciliation sweep. `parked` rows are
 * reap-exempt (their writer does not heartbeat), so WITHOUT this sweep an expired/removed approval
 * checkpoint leaves the row parked forever. Deployment-op face (same posture as
 * `TaskRegistry.reapDurableAgents`): the deployment calls it on its retention cadence with BOTH
 * stores. Two arms, both master-arbitrated by the checkpoint (never by row age):
 *  1. parked rows whose checkpoint is EXPIRED or MISSING → CAS to `failed` (honest terminal, error
 *     names the cause). A `pending` or `resolved` checkpoint leaves the row alone (`resolved` =
 *     a resume is mid-flight — the claim path owns it).
 *  2. stale claims (r4 F-04): `running` rows still carrying BOTH `parkClaimId` and
 *     `parkedCheckpointToken` past `staleClaimMaxAgeMs` (claimed, then the claimer died before the
 *     attach finalize) → CAS BACK to `parked` iff the checkpoint is still pending (redeemable
 *     again); expired/missing → arm-1 shape (failed).
 *  3. stranded flip-lost orphans (server cross-probe B1, 2026-07-24): `parked` rows with NO claim
 *     whose checkpoint is RESOLVED (the consume flip never landed) → `failed` (approval consumed,
 *     outcome unknown). ⚠️ Arms 2-3 run ONLY when `staleClaimMaxAgeMs` is supplied (review F-5:
 *     a deployment that never passes it never reaps these shapes), and an in-process caller's own
 *     handles are excluded — the stranded arm therefore needs an OUT-OF-PROCESS sweep (or a later
 *     run after the owning process exits) to fire.
 * Every write is a rev-CAS — a live writer's concurrent transition wins and the sweep just skips.
 *
 * SPLIT-BRAIN FENCE (件1): failing a row whose PARKING PROCESS is still alive would
 * desynchronize its live handle (poll keeps saying parked, TaskStop mis-reports). An in-process
 * deployment therefore calls {@link import("./task-registry.js").TaskRegistry.reconcileParkedAgents}
 * (which handles ITS OWN handles live, then delegates here with `excludeWriterId` = its writerId);
 * calling this bare is correct only where no engine instance is live for the scope. Residual (multi-
 * replica shared store, ANOTHER live instance's rows): same documented posture as RB-38③ — that
 * instance's next durable write CAS-fails and poisons its lane loudly rather than silently diverging.
 */
export declare function reconcileParkedAgents(stores: {
    agentStore: BackgroundAgentStore;
    checkpointStore: Pick<import("./checkpoint-store.js").CheckpointStore, "get">;
}, scope: string, now: number, opts?: {
    staleClaimMaxAgeMs?: number;
    excludeWriterId?: string;
    excludeHandles?: ReadonlySet<string>;
}): Promise<{
    failed: number;
    rolledBack: number;
}>;
/**
 * REF-A3 — the query shape both `listBySession` (with {@link sessionId}) and `listByScope` (without it)
 * are expressed in. Named rather than inline because it crosses a module boundary (the file backend
 * imports it), and every field's ABSENCE is a defined "no filter" state rather than a missing value.
 */
export interface BackgroundAgentQuery {
    /** Tenant partition key, always in the WHERE — a row in another scope is never visible. */
    scope: string;
    /**
     * Session-anchored enumeration: a row qualifies when its durable anchor
     * (`sessionScoped ? owner : parentSessionId`) OR its `rootSessionId` (the whole
     * delegation tree under one host session) equals this. ABSENT ⇒ scope-wide enumeration.
     */
    sessionId?: string;
    /** Keep only this status. ABSENT ⇒ every status. */
    status?: BackgroundAgentRecord["status"];
    /** Keep the newest N, applied AFTER the sort. ABSENT ⇒ no cap. */
    limit?: number;
}
/**
 * REF-A3 — THE query projection for {@link BackgroundAgentStore.listBySession} /
 * {@link BackgroundAgentStore.listByScope}: filter → sort newest-first by `spawnedAt` → truncate →
 * content-free summary (HIGH-1). Pure (no store state, no I/O): it takes any row iterable, so the
 * in-memory backend passes its `Map` values and the file backend passes the shared ledger's. The two
 * backends held byte-identical hand copies of this before — the same one-of-two-implementations drift
 * hazard RB-60 / RB-74 / RB-87 / RB-111 each landed on, on this very surface.
 *
 * PUBLIC (a serving layer runs the same filter/sort/truncate the bundled backends do, instead of a
 * sixth hand copy). It is a projection over rows the CALLER supplied — it neither reads a store nor
 * authorizes anything: the summaries it returns are content-free but still name handles and session
 * anchors, so a deployment that hands any of them to an external caller applies
 * {@link canAccessAgentRecord} first (the {@link BackgroundAgentStore} interface notes state the same
 * obligation for raw rows: a store never widens visibility, and raw rows are not a serving face).
 */
export declare function queryBackgroundAgents(rows: Iterable<BackgroundAgentRecord>, query: BackgroundAgentQuery): BackgroundAgentRowSummary[];
/**
 * In-process reference implementation. NOT default-mounted (divergence ② above) — single-instance
 * deployments and tests wire it explicitly; it does not survive a restart, so with THIS store the
 * lifecycle flip buys retained transcripts only for the process lifetime (capability presence ≠
 * durability — the `SessionStore.fork` doctrine applies verbatim). `structuredClone` on both
 * boundaries (no aliasing between the caller's live objects and stored rows).
 */
export declare class InMemoryBackgroundAgentStore implements BackgroundAgentStore {
    private rows;
    private key;
    put(record: BackgroundAgentRecord): Promise<void>;
    get(handle: string, scope: string): Promise<BackgroundAgentRecord | null>;
    update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
        rev: number;
    }): Promise<boolean>;
    updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: BackgroundAgentUpdateIfExpect): Promise<boolean>;
    listBySession(scope: string, sessionId: string, opts?: {
        status?: BackgroundAgentRecord["status"];
        limit?: number;
    }): Promise<BackgroundAgentRowSummary[]>;
    listByScope(scope: string, opts?: {
        status?: BackgroundAgentRecord["status"];
        limit?: number;
    }): Promise<BackgroundAgentRowSummary[]>;
    delete(handle: string, scope: string, expect?: {
        rev: number;
    }): Promise<boolean>;
    listScopes(): Promise<string[]>;
    reap(scope: string, now: number, opts?: BackgroundAgentReapOptions): Promise<number>;
}
