/**
 * The commit-side vocabulary of a durable park: which holder a COMMITTED checkpoint publishes to, the abort-classification
 * marker the loop stamps on the contaminated siblings, and the deadline arithmetic every mint shares (the retention
 * defaults and the one TTL sanitizer). Moved whole out of the orchestrator so the boundary-parks phase (resource /
 * platform / review) and the park-ask phase (the approval park) reach DOWN for the same publication seam and the same
 * numbers, and the driver keeps reaching down for the marker its harness stamps. Pure: no engine state; the only clock
 * is the caller's.
 */
import type { CheckpointGate, CheckpointToken, PendingAction } from "../checkpoint-store.js";
import type { WorkspaceHandle } from "../remote-env.js";
import type { Prepared } from "./contracts.js";
/** Default resource-checkpoint retention (30 days, the spec's
 *  `RESOURCE_SUSPEND_TTL_SEC`). Stamped onto the checkpoint `deadline` so the durable reaper can GC an
 *  ORPHANED resource suspend; a legitimate auto-resume fires long before this. Override via
 *  `TaskSpec.resourceSuspend.ttlMs`. */
export declare const DEFAULT_RESOURCE_TTL_MS: number;
/**
 * How long AFTER a governance window frees a `usage_window` checkpoint stays reapable-free.
 * The retention TTL answers "was this abandoned?"; a usage-window suspend is not abandoned while the window
 * it waits on is still full, so its deadline is pushed to `retryAfterMs + this`. One hour gives a host's
 * scheduler a realistic chance to pick the resume up (a cron tick, a queue drain) before the row is
 * treated as garbage — small next to the 30-day retention it is compared against, and large next to any
 * polling interval a driver would sanely use.
 */
export declare const USAGE_WINDOW_REAP_MARGIN_MS: number;
/** Abandonment TTL stamped on an UNATTENDED human/irreversible_ask suspend (no
 *  `durableApproval` = no operator/resolver wired). Without it the checkpoint has `deadline=undefined` and the
 *  reaper (which filters on a non-null deadline) can NEVER GC it → a pinned session + a stranded paused VM +
 *  an orphan checkpoint leak forever. A `durableApproval` suspend keeps the operator's `ttlMs` (undefined =
 *  unbounded by the operator's choice; D-D `terminalAt` is the eventual liveness backstop there). 30 days. */
export declare const DEFAULT_UNATTENDED_APPROVAL_TTL_MS: number;
/**
 * A caller-supplied TTL is only a TTL if it is a finite, positive number.
 * `TaskSpec.durableApproval.ttlMs` / `resourceSuspend.ttlMs` are typed as a bare `number`, and the mint
 * arithmetic was `Date.now() + ttlMs` with NO validation — so `Number(process.env.APPROVAL_TTL_MS)` on an
 * unset/malformed env var (a config-center miss, an upstream arithmetic slip) silently produced
 * `deadline = NaN`. Every reaper comparison is `deadline <= cutoff`, and `NaN <= anything` is FALSE, so a
 * checkpoint that LOOKS bounded (it carries a ttlMs and a deadline) became permanently IMMORTAL: no reap
 * cutoff, however far in the future, could ever expire it → a pinned session + a stranded paused VM, and
 * a misconfigured deployment silently defeating its own abandoned-approval cleanup with no error anywhere.
 *
 * Handling follows the codebase's established treatment of malformed numeric config — sanitize to the
 * documented default rather than throw — `task-registry-shared.ts`'s `TASK_OUTPUT_DEFAULT_CHARS` fallbacks
 * (non-numeric / non-finite / non-positive all return the documented default) and `media-byte-cap.ts`'s
 * `!Number.isFinite(limitBytes) || limitBytes <= 0` guard are the two live precedents: garbage in a bound
 * field must never widen the bound, and
 * killing an in-flight task over it would cost more than it saves. `undefined` (the deliberate
 * "unbounded, D-D terminalAt is the backstop" choice) is preserved as-is and is NOT what this rejects.
 */
export declare function sanitizedTtlMs(ttlMs: number | undefined): number | undefined;
/**
 * WHICH tool call a committed durable park is holding this run — `undefined` when nothing parked, or
 * when the park that did commit holds no call (a resource slice, a plan review).
 *
 * SINGLE derivation on purpose. Two consumers need this answer: the abort-classification seam, which
 * stamps it into the `details` of the results the loop mints for the contaminated siblings, and the
 * `tool_end` projection, which puts it on the wire. Reading it from one function keeps the two from
 * disagreeing about what a pause with no call means. They are not otherwise
 * interchangeable: the frame face applies strictly narrower conditions on top of this answer (see
 * `tool_end.gatedCallId`), so a frame may omit an id this function returns — never the reverse.
 * "Which holder wins" is no longer a question: there is one holder, and a pause that holds no call
 * publishes absence.
 *
 * The frame side must not read the id back out of a tool RESULT even though the marker is there: a
 * result's `details` is written by the tool (and replaceable by post-tool hooks), so lifting a
 * cross-call attribution from it would let any failing tool name an arbitrary call and put a phantom
 * approval wait on someone else's frame. Same rule, same reason, as `tool_end.gate`.
 *
 */
export declare function gatedCallIdOf(p: Pick<Prepared, "pausedRef">): string | undefined;
/**
 * The abort-classification marker for the loop's short-circuit "Operation aborted" family: when a gate
 * committed a durable park this run (`pausedRef.current`, whatever the pause's kind), the contaminated
 * sibling frames carry something a consumer can key
 * on instead of parsing the (contract-stable) result text (ruled 2026-08-04). The commit is published
 * BEFORE the abort fires, so this read is race-free at mint time.
 *
 * `gatedCallId` NAMES the call the gate is holding, so a consumer can attribute a contaminated frame to
 * its cause instead of inferring the pair from batch adjacency. Absent, never approximated: a park that
 * holds no tool call (a resource slice, a plan review) publishes no id, and this marker does not invent
 * one from whatever call happens to be in flight.
 *
 * Module-level rather than a closure in `prepareTask` (extract, don't accrete) — the
 * marker's shape and its rules belong beside {@link gatedCallIdOf}, the derivation it reads.
 */
export declare function parkContaminationMarker(refs: Pick<Prepared, "pausedRef">): Record<string, unknown> | undefined;
/**
 * THE COMMIT-SIDE PUBLICATION. Called AFTER `commitSuspendSaga` returns true (the checkpoint is durably
 * committed) to publish the run's one committed pause for the run loop's terminal assembly. There is no
 * routing: the cause carries the committed gate whole, and WHICH terminal status it reads
 * (`suspended` / `needs_review`) is the gate kind's registry row — the two holders the review family and
 * the approval family used to be split across, and the branch that chose between them, are gone.
 * (Review checkpoints themselves are minted by a PROFILE, out of core scope; core owns this seam and the
 * gate/status/resume discriminants.)
 *
 * Module-level, taking the holder as a parameter (extract, don't accrete) — beside the
 * two readers of what it publishes ({@link gatedCallIdOf}, {@link parkContaminationMarker}).
 */
export declare function publishCommittedSuspend(refs: Pick<Prepared, "pausedRef">, token: CheckpointToken, gate: CheckpointGate, scope: string, remoteHandle: WorkspaceHandle | undefined, checkpointId?: string, 
/** The COMMITTED row's pendingAction — passed whole, not as a pre-extracted id, so no call site can
 *  hand this seam an id the committed row does not actually park on. */
pendingAction?: PendingAction): void;
