/**
 * design/103 — 后台 shell seam(detached process)。「手」腿(`ExecutionEnv`)的 long-running 进程能力:启动一个不会
 * 立刻退出的命令、轮询它的输出、需要时杀掉它。让引擎能跑真实开发循环(起 dev server / build watcher → 轮询日志 →
 * 迭代),用于自足 make-real。
 *
 * 这是一个 **env 上的可选能力**(非一种独立 env 类型),所以不 `extends ExecutionEnv` —— 用交叉类型挂到具体实现上,并经
 * {@link hasBackgroundShell} 运行时检测(对齐 remote-env.ts 的 `hasDestroy`/`isRemoteExecutionEnv` 模式,零 vendor 接口改动)。
 *
 * 设计裁定(经 + workflow 5-lens 双轨证伪式复审收敛,design/103 v2):
 * - **不跨 durable suspend**:后台进程在每条退出路径被 dispose;Runner 必须在 suspendVM **之前** 调
 *   {@link BackgroundShellCapability.disposeBackgroundShells}(detached job 不在 suspendVM 的 in-flight 契约射程内)。
 * - **越界隔离**:`pollBackground`/`killBackground` 的 shellId MUST 被校验为本 env 自己 spawn 过的;非本 env 走 `not_found`。
 * - **按-id-可重读**:实现 MUST 保证按 shellId 跨多次独立调用可重复读取累积/增量输出(execStream 的 consume-once 不满足)。
 */
import type { ExecutionEnv, ExecutionEnvExecOptions, Result } from "../internal/harness-types.js";
/**
 * 一个 long-running / detached 进程的句柄。**env-local、非 durable、对调用方不透明** —— adapter 内部把它映射到真实进程/
 * provider job,**绝不**把可猜的 raw provider job id 暴露成 shellId(否则跨租户可枚举,design/103 §3.8 越界红线)。
 */
export type BackgroundShellId = string & {
    readonly __brand: "BackgroundShellId";
};
/** 一个后台进程的生命周期状态。`exited` 携带 exitCode;`failed` = 启动后异常终止(非被 kill)。 */
export type BackgroundShellStatus = "running" | "exited" | "killed" | "failed";
/**
 * 类型化错误码(对齐 `RemoteExecutionErrorCode` 风格)。`timeout`/`io` 覆盖 TOB 下的有界 liveness/传输失败 —— 接口规定
 * 方法 MUST NOT throw(返回 {@link Result}),所以挂掉的 provider RPC 必须有码可表达、而非违约抛异常。
 */
export type BackgroundShellErrorCode = "unsupported" | "not_found" | "spawn_failed" | "limit_exceeded" | "timeout" | "io";
/** 后台 shell 操作的类型化错误。`code` 给程序分类,`message` 给模型自纠(design/103 错误文案纪律)。 */
export declare class BackgroundShellError extends Error {
    readonly code: BackgroundShellErrorCode;
    readonly cause?: unknown | undefined;
    constructor(code: BackgroundShellErrorCode, message: string, cause?: unknown | undefined);
}
/**
 * `spawnBackground` 的 options。后台进程只用 `cwd`/`env`/`timeout`;流式回调(`onStdout`/`onStderr`)和 `abortSignal`
 * 都对「启动后轮询」语义无意义(输出进 tail buffer 等 poll;后台进程的生命周期由 kill/timeout/dispose 管,不绑启动它
 * 的工具调用的 signal),故显式 `Omit` 以免实现者误以为传 abortSignal 能约束进程。`timeout` 在后台是**硬墙**
 * (到点自杀 → status `killed`,design/103 §3.6)。
 */
export type BackgroundSpawnOptions = Omit<ExecutionEnvExecOptions, "onStdout" | "onStderr" | "abortSignal">;
/** 一次 {@link BackgroundShellCapability.pollBackground} 的返回:自上次 poll 以来的增量 + 当前状态 + cursor 元数据。 */
export interface BackgroundPoll {
    /** 自上次 poll 后的新增 stdout(已应用 `filter`,且 filter 先于截断 —— design/103 §3.4)。 */
    stdout: string;
    /** 自上次 poll 后的新增 stderr。 */
    stderr: string;
    /** 当前生命周期状态。 */
    status: BackgroundShellStatus;
    /** `status==="exited"` 时的退出码。 */
    exitCode?: number;
    /**
     * 本 cursor 之前有字节因 **8MB tail buffer 的 head-evict 永久丢失**(配合 {@link bytesDroppedBeforeCursor})。
     * 🔴 注意:这是**不可恢复**的丢失,不是「可再 poll 拉取」——进程产出超过 8MB tail 时最早段被挤掉。(单次 poll 增量
     * 超 per-poll 显示上限的 head+tail 截断发生在**工具层**,那是另一回事,由工具的截断 marker 诚实标注。)
     */
    truncated?: boolean;
    /** 从进程启动至今的总输出字节(cursor 元数据,供模型判断是否错过早期日志)。 */
    bytesFromStart?: number;
    /** 因 8MB tail buffer head-evict 在本 cursor 之前**永久丢失**的字节数(诚实标注,不谎称「全部」)。 */
    bytesDroppedBeforeCursor?: number;
    /** Present (`true`) only when the shell reached its terminal state via its OWN background timeout
     *  (the env's per-shell hard wall killed it) — distinguishes a self-inflicted timeout from an
     *  external kill, which otherwise produce identical `status:"killed"` frames. */
    timedOut?: boolean;
    /** The timeout budget (seconds) that was actually applied to this shell (requested value clamped
     *  to the env's hard cap). Reported alongside {@link timedOut}. */
    timeoutSec?: number;
}
/**
 * 「手」腿的后台进程能力。一个具体 `ExecutionEnv` 实现可选地附加它(交叉类型),经 {@link hasBackgroundShell} 检测。
 * 不支持的 env(StubExecutionEnv / SSH / ADB)= 不实现(或 `supported:false`),三个后台工具自动不挂(INERT)。
 */
export interface BackgroundShellCapability {
    /** 显式声明能力(像 `RemoteExecutionEnv.capabilities` 一样不猜)。per-env / per-adapter。 */
    readonly backgroundCapabilities: {
        /** 是否真支持后台 spawn;`false` → 三工具不挂(design/103 §3.8)。 */
        readonly supported: boolean;
        /** 单 env 同时存活的后台进程上限(防 fork bomb;超限 spawn 返回 `limit_exceeded`)。 */
        readonly maxConcurrent: number;
        /** 后台默认 timeout(秒)。**per-端下沉**(E2B 沙箱寿命 ≠ TOC 宿主),非单一 core 常量。 */
        readonly defaultBgTimeoutSec: number;
        /** 后台 timeout **硬上限**(秒)。fail-closed 到有限值 —— **绝不允许无界**(design/103 §3.6)。 */
        readonly maxBgTimeoutSec: number;
        /** design/116 detach([C]):env 是否支持把前台 exec 的运行中子进程「领养」为后台(exec options 的
         *  `detachSignal`)。缺省/false ⇒ detach 请求被忽略(exec 继续前台跑完)。 */
        readonly supportsDetach?: boolean;
        /** design/128 T1-1 留驻声明(TB 2026-07-08 翻红回归修):env 声明后台进程 **outlive the run** ——
         *  一切**自动**收割路径(runner 每退出路径的 `disposeBackgroundShells`、registry 的 run-teardown settle
         *  清扫、session release 的 reap)MUST 跳过 kill,把进程留作孤儿(宿主退出后由部署收尸,e.g. TB 容器)。
         *  **显式** TaskStop/KillShell 与 per-shell timeout 硬墙不受影响(host 在世期间照常工作)。
         *  实现侧 `disposeBackgroundShells` 自守 no-op 只护住了 dispose 一条路径;registry 直调 `killBackground`
         *  的清扫(settleKilledForOwner)在 1.257.3 绕穿了它把 TB 留驻服务全数击杀 —— 声明上浮到能力面,让每个
         *  自动收割调用方都看得见。缺省 false = 常规回收行为。 */
        readonly retainBackgroundProcesses?: boolean;
    };
    /**
     * 启动一条命令为后台进程,不等退出,返回 env-local 不透明句柄。MUST NOT block on 进程退出。`timeout` 是硬墙(到点自杀)。
     * 后台子进程的 env MUST 经与前台 `exec` **完全相同**的 secret-scrub(design/103 §3.1 红线),cwd 默认 = 追踪的逻辑 cwd。
     */
    spawnBackground(command: string, options?: BackgroundSpawnOptions): Promise<Result<{
        shellId: BackgroundShellId;
    }, BackgroundShellError>>;
    /**
     * 读一个后台进程**自上次 poll 以来的新增**输出 + 当前状态(cursor 语义)。退出后仍可读残余 + exitCode,直到被 dispose/reap。
     *
     * 🔴 越界契约:`shellId` MUST 被校验为**本 env 自己 spawnBackground 返回过**的;非本 env 一律 `not_found`,绝不解析外部 job id。
     * 🔴 重读契约:实现 MUST 保证「按 shellId 跨多次独立调用可重复读取累积/增量」(remote 的 execStream consume-once 不满足 —— 见 design/103 §5.2 两条路径)。
     */
    pollBackground(shellId: BackgroundShellId): Promise<Result<BackgroundPoll, BackgroundShellError>>;
    /** 杀一个后台进程(幂等:杀已死的是 no-op,返回 ok)。`shellId` 同 {@link pollBackground} 的越界校验。 */
    killBackground(shellId: BackgroundShellId): Promise<Result<void, BackgroundShellError>>;
    /**
     * OPTIONAL — the SYNCHRONOUS hard kill for an exit boundary with no later tick, where a ladder-style
     * {@link killBackground} (TERM, then a timer-driven KILL) never finishes a shell that ignores SIGTERM.
     * Contract (docs/background-process-lifetime.md): complete the kill before returning — POSIX one
     * `kill(-pgid, SIGKILL)` over the shell's process group (an env with no synchronous hard kill, e.g. win32,
     * answers `no_pgid` and the registry falls back to the ordinary kill); no TERM, no timer,
     * no await; never signal a pid the env cannot prove is still its own; never throw; idempotent; the pid never
     * leaves the env. Outcomes: `killed` (delivered now), `already_gone` (exited, or this seat already delivered
     * its kill, or the id is not its shell any more), `no_pgid` (no synchronous kill landed — nothing host-local
     * to signal, or the signal was refused; nothing claimed, retryable). Without this seat the registry's hard
     * face reads `no_pgid` for every shell.
     */
    killBackgroundNow?(shellId: BackgroundShellId): BackgroundHardKillOutcome;
    /**
     * 杀掉并清理**本 env 的所有**后台进程。Runner 在每条退出路径调:finish/abort/throw 在 run-loop tail finally,**suspend/
     * review 必须在 `suspendVM` 之前**(design/103 §3.7;detached job 不在 suspendVM in-flight 契约内,不能指望 adapter 隐式处理)。
     * Best-effort,MUST NOT throw(像 `cleanup`/`destroy`)。幂等。
     *
     * `except`(可选):这些 shellId **留活**(session 驻留 persistent Monitor 的进程 —— 它的全部意义
     * 就是跨 turn 存活;run-end 全灭会留下「registry handle 活着、进程死了」的孤儿 watch)。不认识此参数的旧
     * 实现照旧全灭 = 今天的行为(诚实降级,not silent corruption:watcher 会打出 env-death 终态通知)。
     * suspend/review 前的 dispose **不带** except(挂起整个 VM,进程死亡是既有契约)。
     */
    disposeBackgroundShells(opts?: {
        except?: readonly BackgroundShellId[];
    }): Promise<void>;
}
/** The closed outcome set of {@link BackgroundShellCapability.killBackgroundNow}. */
export type BackgroundHardKillOutcome = "killed" | "already_gone" | "no_pgid";
/** The retain lanes a face can see (absent = not declared). */
export interface BackgroundRetainLanes {
    /** The env's own declaration: `backgroundCapabilities.retainBackgroundProcesses`. */
    readonly env?: boolean;
    /** The task's declaration: `TaskSpec.retainBackgroundProcesses` of the run whose face is asking. */
    readonly task?: boolean;
    /** The row's `retained` mark (stamped at registration; see the registry row). */
    readonly marked?: boolean;
    /** The owning ISOLATED env is destroyed with this run: retention there is illusory; overrides every lane. */
    readonly envDying?: boolean;
}
/**
 * THE retain predicate — "is this background process kept alive past the automatic reaps?" Every face that
 * answers it calls this function (Bash description + receipts, Monitor receipt, the row stamp, teardown settle,
 * the run-boundary dispose at teardown and park-only parks, session reap, the exit hard face, the listing).
 * Any declared lane retains, unless the owning isolated env is being destroyed.
 * Contract: docs/background-process-lifetime.md.
 */
export declare function retainsBackgroundProcesses(lanes: BackgroundRetainLanes): boolean;
/** The env lane of {@link BackgroundRetainLanes}, read off an env (false for an env without the capability). */
export declare function envDeclaresRetain(env: ExecutionEnv): boolean;
/**
 * 结构 + 语义检测:`env` 是否暴露后台 shell 能力且声明 `supported:true`。挂载门控用此(对齐 `hasDestroy`)。
 *
 * 单谓词足够:remote-env 刻意拆 `isRemoteExecutionEnv`(结构)/`isSuspendable`(语义)是因为 isolation/suspendable 两轴
 * 彼此独立;这里 `supported` 是唯一语义轴,合进一个谓词不会丢信息。
 */
export declare function hasBackgroundShell(env: ExecutionEnv): env is ExecutionEnv & BackgroundShellCapability;
/**
 * The ONE way core runs an env-level background sweep.
 *
 * ## Why a single entry point
 * `disposeBackgroundShells` lives in the execution-env layer and cannot reach the task registry, so the
 * one class of kill a user actually notices — their `run_in_background` job disappearing — was the only
 * one that left no attribution: the row still read `stopped-by:"system"`, which is not a cause but the
 * default, i.e. "nothing recorded who did this". Diagnosing a real incident took a cross-repo timeline
 * argument to establish only that the kill had bypassed the registry.
 *
 * Attribution therefore has to be applied by the CALLER, immediately before the sweep — and this repo has
 * spent the week learning what happens when a rule is applied at some of its call sites. There were six.
 * So there is one function, and `test/defectscan-shell-gate-surface.test.ts`… (see the guard) forbids calling
 * `disposeBackgroundShells` anywhere in `src/core/` except here.
 *
 * `attribution` is taken structurally rather than as a `TaskRegistry` import — this module is below the
 * registry in the dependency order, and inverting that to get a log line would be the wrong trade.
 */
export interface EnvSweepAttribution {
    markStopSourceForEnvSweep(env: unknown, source: string, except?: readonly string[]): string[];
}
export declare function sweepBackgroundShells(env: ExecutionEnv & BackgroundShellCapability, attribution: EnvSweepAttribution | undefined, opts?: {
    except?: readonly string[];
}): Promise<void>;
/**
 * THE env-wide dispose at a run boundary: the run's teardown (every exit path) and the park-only park (a
 * durable park that does not pause the env — its workspace, and the processes on it, persist on the target).
 * One rule for "which background shells does this boundary take":
 *
 * - nothing, when THE retain predicate holds for the lanes the boundary sees (the env's declaration, the
 *   run's `TaskSpec` lane; `envDying` overrides both);
 * - otherwise every registered shell row still running on the env is spared (`runningShellIds`, any owner):
 *   what the run's own settle kept (session-resident rows, in-window Monitors, retained rows) and every other
 *   run's or session's rows sharing the env. Unregistered env shells are disposed;
 * - `envDying` (an isolated owned env destroyed with this run) spares nothing: its processes die with it.
 *
 * A park and a teardown ask the same question because a park-only park IS the run ending for now: the run's
 * teardown follows every park, its settle collects the run's own rows that end with it, and its dispose
 * spares the rest — a park that took more would take what the run's end keeps (a session's loop, killed
 * while its approval card is on screen). The sweep before a snapshot pause is a different question (the VM
 * pause must not capture detached jobs; resume would revive untracked zombies), which is why those sites
 * call {@link sweepBackgroundShells} with no keep-list.
 */
export declare function sweepRunBoundaryBackgroundShells(env: ExecutionEnv & BackgroundShellCapability, registry: EnvSweepAttribution & {
    runningShellIds(env: unknown): readonly string[];
}, lanes: {
    task: boolean;
    envDying?: boolean;
}): Promise<void>;
