/**
 * Remote execution seam (design/48 §5) — the type-only contract a **remote/containerised** `ExecutionEnv`
 * implementation (E2B/Firecracker, owned by the service control plane) must satisfy so the agent's "hand"
 * (design/44) can act inside an isolated, *stateful*, *suspendable* workspace instead of the in-process
 * `NodeExecutionEnv`.
 *
 * **This file is a SEAM, not an implementation.** The real `RemoteContainerExecutionEnv` (E2B SDK, warm
 * pools, snapshot lifecycle, tier routing) lives in the service and is driven by real integration needs —
 * exactly like `CheckpointStore` (design/45). Core owns the seam shape; service owns the backend.
 *
 * Design rulings folded in (design/48 §10/§11 DeepSeek council):
 * - **#1 [blocker] interface extension, not capability probing**: `RemoteExecutionEnv extends ExecutionEnv`.
 *   The base {@link ExecutionEnv} is UNCHANGED — `NodeExecutionEnv`/`StubExecutionEnv` need zero edits, and a
 *   consumer that only needs buffered fs/shell keeps accepting the base type.
 * - **#3 [major] do not overload `exec`**: `exec()` stays buffered; streaming is a NEW {@link execStream}.
 * - **#4 [major] suspend × in-flight exec must not hang**: see {@link suspendVM}.
 * - **#8 [major] post-resume consistency**: see {@link postResumeInit}.
 * - **#10 naming**: the isolation level is `sandboxTier` (not `tier`) to avoid clashing with the existing
 *   timeout-/degradation-"tier" in the codebase.
 * - **#16 suspend/resume disambiguation**: the VM-lifecycle methods are `suspendVM`/`resumeVM` to keep them
 *   distinct from the Runner gate-resume and design/45 wake/resume semantics.
 *
 * **Deferred (NOT modelled here — they are runtime, not seam):** actually suspending a running task and
 * persisting the {@link WorkspaceHandle} into a design/45 `Checkpoint`; the `status:"suspended"` orchestrator
 * mapping; tier *classification* (the static table + Tier1-safe allow-list + fail-closed routing) — all are
 * service/design-45 implementation-time concerns. Core only declares the seam those will plug into.
 */
import type { ExecutionEnv } from "../internal/harness-types.js";
/** Snapshot identifier returned by {@link RemoteExecutionEnv.suspendVM}; opaque to core, resolved by the provider. */
export type SnapshotId = string;
/**
 * Token identifying a still-running remote session, used by {@link RemoteExecutionEnv.reconnect} to re-attach
 * the control plane's transport after a control-plane replica crash/failover. The underlying VM state lives
 * independently of any single control-plane TCP connection (a Firecracker process survives a dropped client),
 * so reconnect re-binds rather than re-creates.
 */
export type SessionToken = string;
/** Isolation routing level (design/48 §2). v1 runs everything as Tier 2; classification is DEFERRED to service. */
export type SandboxTier = 1 | 2;
/**
 * Serializable identity of a remote workspace (design/48 §5 gap 1). The control plane persists this into its
 * durable state (and design/45 `Checkpoint`) so a *different* replica can reconnect/resume the same workspace.
 */
export interface WorkspaceHandle {
    /** Provider/VM/sandbox id (e.g. an E2B sandbox id). */
    sandboxId: string;
    /** Provider identifier (e.g. `"e2b"`), so the control plane routes reconnect/destroy to the right backend. */
    provider: string;
    /** Absolute mount path of the workspace root inside the remote env; remote paths normalize against this. */
    mountPath: string;
    /** Set when this env was produced by resuming a snapshot ({@link RemoteExecutionEnv.resumeVM}), else undefined. */
    snapshotId?: SnapshotId;
    /** Token for {@link RemoteExecutionEnv.reconnect} when the VM is still running (vs suspended to a snapshot). */
    sessionToken?: SessionToken;
    /** How a checkpointed workspace is restored on resume (1.257.2 hardening):
     *  `"park_only"` = durable-park-only suspend degrade (non-suspendable env; workspace persists on the
     *  target, resume skips `resumeVM`). Absent = legacy/snapshot handle — resume treats a MISSING
     *  `snapshotId` as corruption (fail-closed) unless the resumed env is itself non-suspendable
     *  (tolerance for park handles minted by 1.257.1 before this field existed). */
    restoreMode?: "park_only";
    /**
     * design/380 O1④ — explicit identity of the physical/target device this workspace lives on
     * (device lane; an SSH/ADB adapter MAY also stamp it). MINTER-STATED: consumers read this field,
     * never parse an identity out of `sandboxId`. Plain string → rides CheckpointState.workspaceHandle
     * (all-string whitelist) with zero schema movement.
     */
    deviceId?: string;
}
/**
 * A reference to a secret injected at {@link RemoteExecutionEnv.connect}/{@link RemoteExecutionEnv.postResumeInit}
 * time (design/48 §5 gap 7) — never baked into the image. The control plane resolves {@link ref} to a value out of
 * band; only the reference travels through core.
 */
export interface SecretRef {
    /** Logical name the secret is exposed under (an env-var name in the remote env for `destination:"remote"`). */
    name: string;
    /** Opaque locator the control plane resolves to a value (vault path, KMS key id, …). */
    ref: string;
    /**
     * Where the resolved secret is consumed (design/61 §9 B, council). Default `"remote"` (the original
     * semantics): injected **into the remote env** as `name` (E2B env-var) — visible to code running there.
     * `"adapter"`: used by the **adapter itself** to establish/authenticate the connection (an SSH private key,
     * an ADB token) and **MUST NOT be injected into the target machine/device** — a credential that reaches an
     * un-isolated target's environment is the lethal trifecta (design/53 §C). An adapter MUST honor this.
     */
    destination?: "remote" | "adapter";
}
/** Configuration for {@link RemoteExecutionEnv.connect}. */
export interface RemoteConnectConfig {
    /** Resume from this snapshot instead of provisioning a fresh workspace. */
    snapshotId?: SnapshotId;
    /** Secrets to inject at connect time (design/48 §5 gap 7). */
    secrets?: SecretRef[];
    /** Abort provisioning/connection. */
    abortSignal?: AbortSignal;
}
/**
 * Options for the VM-lifecycle ops {@link RemoteExecutionEnv.suspendVM} / {@link RemoteExecutionEnv.resumeVM}
 * (design/49 v1.5, code-ready council BUG#2). Parity with {@link RemoteConnectConfig.abortSignal} /
 * {@link ExecStreamOptions.signal}: without an abort hook a hung provider pause/restore (network partition)
 * would pin the calling worker forever. The implementation races the op against this signal + an internal
 * timeout; on abort it returns `{ok:false}` with code `"aborted"`.
 */
export interface VmLifecycleOptions {
    /** Abort a hung pause/restore. */
    abortSignal?: AbortSignal;
    /**
     * (resume workspace-root fidelity) — the CHECKPOINTED {@link WorkspaceHandle} of the workspace
     * being restored, passed by the engine on the `resumeVM` leg. An adapter SHOULD bind the restored
     * workspace root (cwd / mount) to `priorHandle.mountPath` rather than its CURRENT deployment
     * configuration: across a config-knob change window (e.g. a mount-path rotation) the resumed task's
     * execs and artifacts must land in the root the task was checkpointed under, not the new one. An
     * adapter that cannot honor the prior root MUST still return its ACTUAL `mountPath` in the restored
     * handle (never echo `priorHandle.mountPath` unhonored) — the engine compares the two and surfaces a
     * divergence observation. Absent on non-resume ops and from pre-1.360 engines (adapters treat absence
     * as the legacy current-config behavior).
     */
    priorHandle?: WorkspaceHandle;
}
/**
 * One chunk streamed from {@link RemoteExecutionEnv.execStream} (design/48 §5 gap 3, council BUG#3). A run is a
 * sequence of `stdout`/`stderr` chunks terminated by exactly one `exit` chunk carrying the typed exit code.
 *
 * The async iterator THROWS a {@link RemoteExecutionError} if the stream fails before an `exit` chunk arrives
 * (transport loss, or a {@link RemoteExecutionEnv.suspendVM} that aborts the in-flight command) — that is the
 * streaming analogue of the base {@link ExecutionEnv}'s never-throw `Result` contract, and it guarantees a
 * consumer is never left awaiting a chunk that will never come.
 */
export type OutputChunk = {
    type: "stdout";
    data: string;
} | {
    type: "stderr";
    data: string;
} | {
    type: "exit";
    exitCode: number;
};
/** Options for {@link RemoteExecutionEnv.execStream}. */
export interface ExecStreamOptions {
    /**
     * Isolation routing hint (design/48 §2; renamed from `tier` per §11 #10). The *classification* that decides
     * a command's tier — static tool table + Tier1-safe sub-command allow-list, fail-closed to Tier 2 — is the
     * env/control-plane's job and is DEFERRED; this param only lets a caller pass an explicit route when known.
     */
    sandboxTier?: SandboxTier;
    /** Working directory; relative paths resolve against the workspace root. */
    cwd?: string;
    /** Extra environment variables for the command. */
    env?: Record<string, string>;
    /**
     * Total command wall-clock timeout in seconds. **🔴 Per-command — independent of the env/sandbox lifetime.**
     * A remote adapter MUST NOT fall back to the sandbox lifetime when this is unset (service [45]: an E2B adapter
     * that defaulted a missing per-command timeout to `cfg.timeoutMs` = the sandbox lifetime made a seconds-long
     * command wait the *whole sandbox lifetime*, ~30 min, on a provider RPC hang). "No `timeout`" means "no
     * wall-clock cap on a *making-progress* command" — NOT "wait until the env dies"; liveness is still bounded by
     * {@link readTimeoutMs} (see below). Distinct concerns: `timeout` caps a slow-but-progressing command;
     * `readTimeoutMs` catches a *hung/unreachable* one.
     */
    timeout?: number;
    /** Abort the command. Aborting ends the stream with a {@link RemoteExecutionError} (code `"aborted"`, no `exit` chunk). */
    signal?: AbortSignal;
    /**
     * Idle/liveness read timeout (ms). If no {@link OutputChunk} arrives within this window, `execStream` ends
     * with a {@link RemoteExecutionError} (code `"timeout"`) instead of hanging. **Needed in practice for remote
     * backends** (design/48 §5, service [7]): E2B issue #1128 — a *streaming* call sets no read timeout (only unary
     * does), so an unreachable sandbox hangs the worker forever. Distinct from `timeout` (total wall-clock).
     *
     * 🔴 **Liveness contract (service [45]/[59]):** a remote adapter SHOULD enforce a default liveness bound even
     * when this is unset (a missing idle timeout MUST NOT mean "wait forever / until the env dies"), and that bound
     * MUST cover the **command-creation/handshake RPC**, not only the post-first-chunk loop — a provider hang
     * typically occurs at creation, before any chunk arrives, so an idle timer started only after the first chunk
     * misses it. On trigger, throw {@link RemoteExecutionError} with code `"timeout"` (retryable by the caller per
     * the command's idempotency — the adapter MUST NOT blind-retry).
     */
    readTimeoutMs?: number;
    /** Per-call output cap (design/48 §5 gap 8): stop streaming after this many bytes to avoid a `find /` flooding the control plane. */
    maxOutputBytes?: number;
}
/** Stable error codes for the remote seam's lifecycle/streaming operations (design/48 §5/§11 BUG#2). */
export type RemoteExecutionErrorCode = 
/** A lifecycle/exec op failed because the workspace is (or became) suspended. */
"suspended"
/** {@link RemoteExecutionEnv.suspendVM} refused because a command was still in flight (council #4). */
 | "command_in_flight"
/** Connect/resume/reconnect failed to reach or provision the remote workspace. */
 | "connect_failed"
/** A post-resume consistency step failed (design/48 §5/#8) — caller must destroy the env. */
 | "post_resume_failed"
/** The command/stream was aborted via its `AbortSignal`. */
 | "aborted"
/**
 * A command's wall-clock {@link ExecStreamOptions.timeout} or liveness {@link ExecStreamOptions.readTimeoutMs}
 * was exceeded — including a provider RPC hang at command creation (service [45]/[59]). **Retryable** by the
 * caller per the command's idempotency (the adapter MUST NOT blind-retry). Distinct from `"aborted"` (a
 * caller-driven `AbortSignal`) and `"connect_failed"` (workspace provisioning, not a per-command hang).
 */
 | "timeout"
/**
 * The op is not supported by this adapter's capabilities (design/61 §9 A) — e.g. `suspendVM`/`resumeVM` on a
 * non-suspendable SSH/ADB env. Permanent for this env; check {@link RemoteExecutionEnv.capabilities} first.
 */
 | "unsupported"
/**
 * A **transient** authentication step that is expected to succeed on a retry (design/61 §9 C) — e.g. an ADB
 * device awaiting first-time authorization (connect once → user authorizes → connect again succeeds).
 * **Retryable** (typically retry-exactly-once). Distinct from {@link "auth_failed"} so a real rejection isn't
 * retried and a bounded ADB retry doesn't false-kill an SSH transient.
 */
 | "auth_transient"
/**
 * **Permanent** authentication failure (design/61 §9 C) — wrong/rejected SSH key, revoked token. **NEVER
 * retry** (retrying burns the whole attempt budget and can lock accounts). The caller must surface it.
 */
 | "auth_failed"
/**
 * The transport connection dropped mid-session (TCP RST, network partition, peer close) — design/61 §9 C/#14.
 * **Retryable** by re-establishing the connection (idempotent for a persistent SSH/ADB session). An adapter
 * MUST map raw transport errors (DNS `ENOTFOUND`, TCP RST) onto a typed code, not leave them as `"unknown"`,
 * else a retry whitelist misses them.
 */
 | "transport_lost"
/**
 * design/380 O12 — the op was COMMITTED to the remote target and its outcome is UNKNOWABLE (the target
 * went unreachable / restarted / was revoked after dispatch-commit; an execStream cut where the target
 * protocol's own server has ruled the outcome unknowable). NEVER auto-retried and deliberately NOT in
 * {@link RETRYABLE_REMOTE_ERROR_CODES}: the command may have already executed — a retry whitelist that
 * contained it would re-drive committed side effects. Distinct from `transport_lost` (connection story
 * known, idempotency-gated retry after reconnect). Message MUST carry the verify-first sentence ("may
 * have already executed on the target; verify its effect before re-running").
 */
 | "outcome_unknown"
/** Unclassified provider/transport failure. */
 | "unknown";
/**
 * The RETRYABLE half of {@link RemoteExecutionErrorCode} — the codes whose own contract above says a retry
 * can succeed (`auth_transient`: retry-exactly-once after the device is authorized; `connect_failed`: the
 * workspace could not be reached/provisioned this attempt; `timeout`: a liveness bound tripped;
 * `transport_lost`: re-establish the connection). Every OTHER code is permanent for this attempt
 * (`auth_failed`/`unsupported`), caller-driven (`aborted`), unclassifiable (`unknown`), or
 * outcome-unknowable (`outcome_unknown` — design/380 O12: the op may have ALREADY executed, so a retry
 * re-drives committed side effects) — retrying them burns budget or, worse, re-drives a rejected
 * credential or a committed op.
 *
 * ONE list, two consumers, so a caller's retry decision and its DISCLOSURE can never disagree: the engine
 * retries only IDEMPOTENT ops on these codes (see {@link withRetry}'s red line — a snapshot-taking
 * `suspendVM` is never retried), and it stamps `retryable` on the failure it reports to the caller for the
 * ops it does NOT retry, so a deployment can decide (refresh the credential and re-resume vs give up)
 * without parsing prose.
 */
export declare const RETRYABLE_REMOTE_ERROR_CODES: readonly RemoteExecutionErrorCode[];
/** Is `code` in the {@link RETRYABLE_REMOTE_ERROR_CODES} family? */
export declare function isRetryableRemoteErrorCode(code: RemoteExecutionErrorCode): boolean;
/**
 * Error surfaced by {@link RemoteExecutionEnv} lifecycle ops and by {@link RemoteExecutionEnv.execStream}'s
 * iterator. Mirrors the vendored `ExecutionError` shape but carries the remote-specific code set; it is kept
 * separate from the vendored `ExecutionErrorCode` union deliberately — those codes only need to widen once the
 * design/45 suspend/resume *runtime* lands and the Runner/orchestrator layer must thread `"suspended"`.
 */
export declare class RemoteExecutionError extends Error {
    readonly code: RemoteExecutionErrorCode;
    constructor(code: RemoteExecutionErrorCode, message: string, cause?: Error);
}
/**
 * A remote, stateful, suspendable {@link ExecutionEnv} (design/48 §5). Extends the base with workspace identity,
 * connection lifecycle, streaming exec, and post-resume reconciliation. Every method follows the base contract:
 * **never throw/reject** — encode failures in the returned `Result` (the one exception is {@link execStream},
 * whose iterator may throw mid-stream; see {@link OutputChunk}).
 *
 * Lifecycle methods are typed but their RUNTIME wiring (task suspension, `Checkpoint` persistence of the
 * {@link WorkspaceHandle}, cross-replica resume) is implemented with design/45 — see the file header.
 *
 * 🔴 **Universal liveness contract (service [45]/[59] class-fix) — applies to EVERY remote RPC**, not just
 * `execStream`: the inherited {@link ExecutionEnv} filesystem ops (`readTextFile`/`writeFile`/`listDir`/…), the
 * streaming/buffered exec, AND the VM-lifecycle ops below (`connect`/`reconnect`/`suspendVM`/`resumeVM`/
 * `postResumeInit`/`destroy`). Each is a network round-trip to a provider that can hang. The contract:
 * 1. **Every remote RPC SHOULD enforce a bounded default liveness/idle timeout** even when no `abortSignal`/
 *    timeout is given — a hung provider call MUST NOT wait until the env/sandbox lifetime expires (the
 *    service [45] bug, but generalized: it was found on `exec`, and `files.read`/`resumeVM`/etc. share the
 *    exact exposure). An optional `abortSignal` (where present) composes with, but does not replace, this default.
 * 2. **🔴 Bound on LIVENESS (no progress), not a fixed short wall-clock** — distinguish *hung* from *slow but
 *    progressing* (search [60] / service [50] data-transfer sharpening). A **data-transfer** op — a large-workspace
 *    `suspendVM` snapshot, a big `writeFile`/`readTextFile` — may legitimately run for minutes; a fixed short
 *    wall-clock would false-kill the (durable-checkpoint *命门*) path. So such ops bound on an **idle/no-heartbeat**
 *    window (reset by observable progress), not total elapsed time. Cheap control RPCs (`isRunning`, a small
 *    `connect` handshake) may use a short wall-clock. The adapter sizes the bound per op class.
 * 3. **A liveness breach surfaces as a typed, retryable error**: `RemoteExecutionError` code `"timeout"` for the
 *    lifecycle/stream ops; for the inherited filesystem ops (which return the vendored `Result<_, FileError>`),
 *    a `FileError` — but still **bounded, never an unbounded wait**. Retry is the caller's decision per the op's
 *    idempotency; the adapter MUST NOT blind-retry.
 */
export interface RemoteExecutionEnv extends ExecutionEnv {
    /**
     * What this adapter can actually do (design/61 §9 A, council). The seam was built for E2B (a full
     * `{isolation:true, suspendable:true}` adapter); **partial-capability** adapters (SSH/ADB: `{false, false}`
     * — a real machine/device, not snapshotable and not isolated) declare it here. **Required + explicit** so
     * the orchestrator never has to guess: there is no safe default (assuming isolation when there is none is a
     * security hole; assuming suspendable crashes on `suspendVM`). Use {@link isSuspendable}/{@link isIsolated}
     * rather than the structural {@link isRemoteExecutionEnv} (method presence ≠ semantics — a stub `suspendVM`
     * that returns `"unsupported"` still satisfies the structural check). Extensible: add fields as new
     * partial-capability classes appear.
     */
    readonly capabilities: {
        /** True iff actions are contained (E2B microVM); false for a real target (SSH host / ADB device). The
         *  orchestrator tightens autonomy + the design/37 policy gate when this is false (design/53 zero-trust). */
        isolation: boolean;
        /** True iff the env can be snapshot/`suspendVM`'d (E2B); false for SSH/ADB (durable suspend N/A).
         *
         *  ⚠️ LOAD-BEARING INVARIANT (opus review 1.257.2): `suspendable:false` on a remote env also asserts
         *  the workspace is EXTERNALLY DURABLE — it persists on the target across the env object's lifetime
         *  (true for SSH hosts / ADB devices). The durable-park-only suspend degrade rests
         *  on this: it skips `suspendVM` and trusts the factory to reconnect to the SAME workspace on resume.
         *  An adapter for a non-suspendable EPHEMERAL backend (e.g. a snapshot-less container torn down with
         *  the transport) must NOT be modeled as `suspendable:false` remote — it would silently take the
         *  park-only branch and resume onto a fresh empty workspace. Model such a backend as a per-task env
         *  without durable suspend instead (the human gate refuses it), or extend capabilities with an
         *  explicit workspace-durability flag before building one. */
        suspendable: boolean;
        /** True iff this adapter's {@link FileSystem.canonicalPath} is the AUTHORITY on where a path lands —
         *  it answers for the very filesystem the commands run on, and a failure to answer is a failure of
         *  the resolver rather than a namespace it cannot see into. Optional, and absent is not a
         *  declaration: an adapter that says nothing keeps the reading every adapter has had, in which a
         *  chain nothing could be established about leaves the lexical verdict standing. Declaring it says
         *  the opposite — that an unanswerable chain is a question, not an absence — and the read
         *  boundary's execute-time recheck then refuses rather than passing the written spelling through.
         *  Read STRICTLY through {@link canonicalPathIsAuthoritative}, like every other capability whose
         *  `true` changes what a containment leg does; a wrong-SHAPE value reads absent and is announced. */
        canonicalPathAuthoritative?: boolean;
    };
    /** Identity of the connected workspace (design/48 §5 gap 1). Synchronous: it is data the env already holds. */
    workspaceHandle(): WorkspaceHandle;
    /** Provision/attach the remote workspace and inject secrets. Returns the resulting {@link WorkspaceHandle}. */
    connect(config?: RemoteConnectConfig): Promise<{
        ok: true;
        value: WorkspaceHandle;
    } | {
        ok: false;
        error: RemoteExecutionError;
    }>;
    /**
     * Snapshot the workspace and (provider permitting) stop billing; returns the {@link SnapshotId} to resume from.
     *
     * **Council #4 — must not leave an in-flight command hung.** If a {@link Shell.exec}/{@link execStream} is in
     * flight, an implementation MUST either (a) refuse with `RemoteExecutionError("command_in_flight")`, or (b)
     * abort the in-flight command — buffered `exec` then resolves an `Err` and an in-flight {@link execStream}
     * iterator throws `RemoteExecutionError("suspended")`. It must NOT silently snapshot and leave the command's
     * promise/iterator pending forever.
     *
     * **🔴 Atomicity contract (design/49 v1.5, code-ready council BUG#1/#13).** This is the FIRST durable side
     * effect of a remote durable-suspend, so its all-or-nothing semantics are load-bearing for the Runner's
     * commit ordering:
     * - `{ok:true}` ⇒ the VM is paused at `value` (a `SnapshotId`); the caller may now persist a checkpoint.
     * - `{ok:false}` ⇒ the VM is left in its ORIGINAL (running) state, untouched — the caller can safely fall
     *   back to the synchronous `onAsk` gate as if no suspend was attempted (Runner: design/49 §4①).
     * An implementation MUST NOT return `{ok:false}` after it has already paused/snapshotted the VM. The gate
     * path means no command is in flight (the batch's prior calls are awaited, the gated call has not run), so
     * `command_in_flight` should not arise; if it does, the Runner treats it as an ordinary `{ok:false}` and
     * falls back to `onAsk` — see {@link RemoteExecutionErrorCode}.
     */
    suspendVM(options?: VmLifecycleOptions): Promise<{
        ok: true;
        value: SnapshotId;
    } | {
        ok: false;
        error: RemoteExecutionError;
    }>;
    /** Restore the workspace from a snapshot. Idempotent + re-entrant (a replica that crashed mid-suspend can be
     *  superseded by another resuming the same {@link SnapshotId} without corruption — aligns with design/45 resolve-CAS).
     *  Pass `options.abortSignal` so a hung restore cannot pin the resuming worker (design/49 BUG#2). */
    resumeVM(snapshotId: SnapshotId, options?: VmLifecycleOptions): Promise<{
        ok: true;
        value: WorkspaceHandle;
    } | {
        ok: false;
        error: RemoteExecutionError;
    }>;
    /** Re-attach the control-plane transport to a still-running VM after a control-plane reconnect (design/48 §5 gap 4). */
    reconnect(sessionToken: SessionToken): Promise<{
        ok: true;
        value: WorkspaceHandle;
    } | {
        ok: false;
        error: RemoteExecutionError;
    }>;
    /**
     * Re-establish consistency AFTER {@link resumeVM}/{@link reconnect} (design/48 §5 gap, council #8): re-fetch git
     * remote refs + invalidate stale package/index caches, and re-inject secrets (short-lived credentials may have
     * expired while suspended). Any failed step → `Err("post_resume_failed")` and the caller MUST destroy the env.
     *
     * Note: a resumed guest's network/long-lived connections (git remote / API / registry) are NOT guaranteed to
     * survive the snapshot (decision 2), so this is also where the VM-internal agent rebuilds those.
     *
     * 🔴 Ordering red line (design/48 §5/#6): at-rest encryption of the memory snapshot must be ensured BEFORE
     * secrets are injected — never let plaintext credentials land in an unencrypted snapshot. That encryption is a
     * service-side property of the snapshot store; this method must fail-and-clean if it cannot be guaranteed.
     *
     * `opts.abortSignal` (design/384 slice 2, ADDITIVE — implementations are not forced to change): an
     * early-release channel for a caller whose wait on this init is bounded (the park fence's paused-VM
     * compensation). Best-effort exactly like {@link suspendVM}'s — an implementation may ignore it; the
     * caller bounds its own wait and treats a deaf adapter as failed.
     */
    postResumeInit(opts?: {
        abortSignal?: AbortSignal;
    }): Promise<{
        ok: true;
        value: void;
    } | {
        ok: false;
        error: RemoteExecutionError;
    }>;
    /**
     * Stream a command's output (design/48 §5 gap 3) — the standard path for long build/test runs. Distinct from
     * the buffered base `exec()` (council #3: no overload). Yields `stdout`/`stderr` chunks then exactly one `exit`
     * chunk; the iterator throws {@link RemoteExecutionError} if the stream fails before `exit` (see {@link OutputChunk}).
     *
     * 🔴 **Timeout/liveness contract (service [45]/[59]) — applies to BOTH `execStream` and the base `exec`:** a
     * per-command timeout is **independent of the env/sandbox lifetime** and an adapter MUST NOT fall back to the
     * lifetime when none is given (else a hung provider RPC pins the worker for the whole sandbox lifetime). An
     * adapter SHOULD enforce a bounded default liveness/idle timeout that also covers the command-creation RPC, and
     * surface a liveness/timeout breach as a typed, retryable {@link RemoteExecutionError} code `"timeout"`. See
     * {@link ExecStreamOptions.timeout} / {@link ExecStreamOptions.readTimeoutMs}.
     */
    execStream(command: string, options?: ExecStreamOptions): AsyncIterable<OutputChunk>;
    /**
     * Tear down the workspace and release provider resources. Best-effort; must never throw (like `cleanup`).
     * **Must be idempotent** — safe to call more than once (the second call is a no-op). The Runner calls it
     * once on task end, and `prepareTask` calls it on a prepare-time throw; a remote impl may also be reaped,
     * so a defensive double-call must not error or double-bill.
     *
     * contract note: on a NON-isolated env (no `capabilities.isolation` — host lane, SSH host),
     * destroy() is workspace/object-level cleanup and must NOT reap still-running background processes —
     * that is `disposeBackgroundShells`' job, which honours the timeout/session keep-alive except-list
     * (monitor timeout anchor). An isolated env (container/VM) naturally takes everything down;
     * the Runner's envDying settle accounts for that with an accurate killed receipt beforehand.
     */
    destroy(): Promise<void>;
}
/** Context handed to an {@link ExecutionEnvFactory} for each task (design/48 §5 / §7 Q7). Deliberately minimal:
 *  per-task identity is enough to allocate/route a per-task container; richer routing (tenant/principal) is
 *  captured in the factory closure by the trusted control plane that builds it. */
export interface ExecutionEnvFactoryContext {
    /** Resolved session id for the task — the stable identity of its per-task workspace. */
    sessionId: string;
    /**
     * design/380 O1① — the run TREE's placement root (fixed point), minted by prepare AFTER session
     * resolution as `internals.placementRoot ?? internals.rootSessionId ?? sessionId` — a top-level
     * run's own resolved sessionId, every descendant's inherited root, verbatim (delegation and
     * workflow spawn chains re-thread an explicit `placementRoot` — C12; a bare DURABLE RESUME crosses
     * too since design/380 O1③: the suspend mint stamps the fixed point on the checkpoint and the
     * resume restores it into trusted internals — formerly the pinned residual). A target-bound
     * factory (device lane) keys its placement lookup on THIS, never on `sessionId` (a child's fresh
     * session id must not read as a new placement). Always present and non-empty — an empty value is
     * an assembly error the engine refuses loudly at the mint (`config.placement_root_invalid`) and a
     * factory MUST refuse loudly too (loud-bad-value posture), never default around.
     */
    placementRootSessionId: string;
    /** Caller-supplied task id, when set on the `TaskSpec`. */
    taskId?: string;
    /**
     * design/97 CORE-6 — per-task ISOLATION request threaded from the workflow's `ctx.agent({ isolation })` via
     * the TRUSTED `RunInternals` channel (never from the untrusted `TaskSpec`). When `"worktree"`, a
     * control-plane factory **MUST either mint a git-worktree-rooted env for this agent (e.g. via
     * {@link addWorktree}) WITH a `destroy()` that removes the worktree, or THROW.** It MUST NOT silently
     * return a non-isolated env (the shared base env, or a child rooted at `parentCwd`): the spawn may be
     * governed by a relaxed write baseline (`WorkflowGovernanceBaseline.worktreeBase`) that is safe ONLY
     * inside a worktree — a silent degrade would point those writes at the shared working tree. A factory
     * throw fails the spawn (fail-closed: the child never starts) — the honest outcome. The engine
     * additionally rejects the observable degrade shapes at its consumption point (no factory configured;
     * the shared static env returned as-is; an in-process env rooted at the base root or the parent's cwd),
     * but full worktree verification is the factory's contract — there is no receipt field on
     * {@link ExecutionEnv}. Unset = default (the factory's normal per-task env).
     */
    isolation?: "worktree";
    /**
     * Field-reported (sub-agents landing in an EMPTY sandbox): the PARENT task's
     * effective working root, threaded (like `isolation`) via the TRUSTED `RunInternals` channel when this
     * task is a sub-agent (workflow `ctx.agent` / Task delegation). CC parity: a Task sub-agent inherits the
     * main session's cwd. A single-user/TOC factory SHOULD root the child env here (unless `isolation`
     * requests a worktree — that wins); a multi-tenant/TOB factory minting isolated containers may ignore
     * it. Absent on top-level tasks. Static shared-`executionEnv` deployments need nothing — the child
     * already shares the parent env (and its cwd).
     */
    parentCwd?: string;
}
/**
 * A **trusted control-plane** factory that mints a per-task {@link ExecutionEnv} (design/48 §5 answers core
 * gap-a / §7 Q7). Lives on `RunnerDeps` (deployment-level) — NOT on `TaskSpec` — so an untrusted caller can
 * never inject its own env and escape the sandbox (design/44 §7 Q4 red line; decision 3: the Docker
 * fast-lane must be control-plane-assigned, never task-selectable).
 *
 * The remote model is "one container per task", so the factory is invoked once per task. The Runner owns the
 * lifetime of a factory-produced env: if it implements {@link RemoteExecutionEnv.destroy} (see {@link hasDestroy}),
 * the Runner calls it when the task ends.
 */
export type ExecutionEnvFactory = (ctx: ExecutionEnvFactoryContext) => ExecutionEnv | Promise<ExecutionEnv>;
/** True when `env` exposes a lifecycle {@link RemoteExecutionEnv.destroy} the Runner should call on task end. */
export declare function hasDestroy(env: ExecutionEnv): env is ExecutionEnv & Pick<RemoteExecutionEnv, "destroy">;
/** Structural check that `env` implements the remote seam (lifecycle + streaming surface + declared capabilities). */
export declare function isRemoteExecutionEnv(env: ExecutionEnv): env is RemoteExecutionEnv;
/**
 * The RESTORE half of the suspend contract — the methods the RESUME leg calls after a `suspendVM`
 * ({@link RemoteExecutionEnv.resumeVM} + {@link RemoteExecutionEnv.postResumeInit}). Returns the names an
 * adapter is MISSING (empty = complete).
 *
 * Split out from {@link isRemoteExecutionEnv} deliberately: that predicate answers "can this env be driven
 * as a remote env at all" (a park-only SSH/ADB host legitimately never restores a VM and is checked with
 * it), while THIS one answers "if I snapshot this env now, will the resume leg find a way back". Naming the
 * missing methods lets the suspend-time guard say WHICH part of the adapter is absent instead of failing on
 * the resume leg — a different process, hours later — with a bare `env.resumeVM is not a function`.
 */
export declare function missingRestoreSurface(env: ExecutionEnv): readonly ("resumeVM" | "postResumeInit")[];
/**
 * Can this env be durably suspended/snapshotted (design/61 §9 A)? Use this — NOT the structural
 * {@link isRemoteExecutionEnv} — before calling `suspendVM` (a non-suspendable SSH/ADB env declares
 * `capabilities.suspendable: false` but still has the method, which returns `"unsupported"`). Non-remote
 * (in-process) envs are not suspendable.
 *
 * A composite type guard (structural AND capability) so a `suspendVM` call site narrowed by THIS predicate
 * gets the {@link RemoteExecutionEnv} type without a cast — `true` always implies the structural check too.
 * The capability is read STRICTLY, like the containment ones: a non-boolean reads as NOT suspendable and
 * the census announces it. This took anything truthy, so `suspendable: "false"` was snapshotted — the one
 * key of the three whose malformed reading fell on the unsafe side.
 *
 * It also requires the RESTORE surface ({@link missingRestoreSurface}). A suspend is only durable
 * if something can undo it; an adapter that offers `suspendVM` but no `resumeVM` used to pass this guard,
 * take a real snapshot, commit a real checkpoint — and then blow up on the resume leg with an untyped
 * TypeError. "Suspendable" now means the whole round trip, so a half-adapter is rejected at the suspend site
 * where the deployment can still be told which method it owes.
 *
 * ⚠️ A capability-DECLARING but restore-incomplete adapter must NOT be re-routed to the durable-park-only
 * degrade by this predicate returning false — park-only rests on the workspace being externally durable
 * (see {@link RemoteExecutionEnv.capabilities}.suspendable), which a snapshot backend does not have. Call
 * sites split the two cases on the DECLARED `capabilities.suspendable` flag, not on this predicate alone.
 */
export declare function isSuspendable(env: ExecutionEnv): env is RemoteExecutionEnv;
/**
 * Is this env an isolated sandbox (design/61 §9 A, design/53)? `false` for a real SSH host / ADB device AND
 * for any in-process env (the host process is not isolated). The orchestrator tightens autonomy + the
 * design/37 policy gate when this is `false` (zero-trust on an un-isolated target). The safe default is
 * `false` — isolation must be explicitly declared, never assumed, and that is why the flag is read
 * STRICTLY: every consumer of a `true` here RELAXES something (autonomy, a policy tier, a containment
 * leg that the sandbox is then answerable for), so a value that is not the boolean `true` — absent,
 * a string, a truthy object from a JS adapter the compiler never saw — is not a declaration and lands
 * on the checking side. This is the reading every consumer of the containment question comes through,
 * and the only one that also NARROWS; a caller already holding a `RemoteExecutionEnv` may spell the
 * same strict `!== true` test itself (the worktree-isolation refusal in the runner's prepare does).
 * What no caller may do is infer isolation from the remote INTERFACE — implementing the seam is not
 * declaring containment, and a host-lane adapter drives the operator's own machine through it.
 */
export declare function isIsolated(env: ExecutionEnv): boolean;
/**
 * Is CONTAINMENT this env's own job rather than the engine's — i.e. may a containment leg stop at the
 * written spelling and leave the rest to the target? True exactly for a DECLARED isolated sandbox: the
 * sandbox is the security boundary there, and its filesystem is not the one this process can resolve
 * (`canonicalPath` is an RPC into somebody else's namespace).
 *
 * The one question the `bash`/`bash_readonly` read boundary asks before deciding whether to re-resolve
 * a candidate through the filesystem, and the one both faces' DISCLOSURE reads, so a card can never
 * promise a refusal the leg does not issue (or omit one it does). Deliberately NOT the remote INTERFACE:
 * a non-isolated adapter (host lane, SSH host — see {@link RemoteExecutionEnv.destroy}'s contract note)
 * implements the whole seam while driving an ordinary machine whose paths its own `canonicalPath`
 * answers exactly, and reading the shape as containment made that deployment's boundary lexical-only.
 */
export declare function boundaryIsTheSandbox(env: ExecutionEnv): boolean;
/**
 * Does this env DECLARE that its `canonicalPath` is the authority on where a path lands?
 *
 * The question the execute-time read boundary asks about its own resolver's SILENCE. Resolving a
 * spelling walks it from the deepest answerable ancestor; when no ancestor answers at all, two
 * different worlds produce the same `undefined`: a resolver that is authoritative and is FAILING (an
 * RPC into the machine the command will run on, a transport that dropped), and an env that simply does
 * not answer path questions (an in-memory double, an adapter with no resolution surface). The first is
 * a question and the second is an absence, and reading both as the absence is what leaves a containment
 * leg silently open on an RPC-backed lane.
 *
 * So the adapter says which it is, and says it the way {@link isIsolated} is said — strictly, because a
 * `true` here makes a containment leg REFUSE where it used to pass. ABSENT is the reading every adapter
 * has had, so no existing env moves; a PRESENT non-boolean reads absent HERE too but is announced at the
 * env's admission point, once per key — this reader does not validate. A non-remote env keeps the same
 * reading, which is why the local lane is unchanged: its resolver answers `/`, so the arm this gates
 * has no case to fire on.
 */
export declare function canonicalPathIsAuthoritative(env: ExecutionEnv): boolean;
