import type { AgentTool, ExecutionEnv } from "../internal/harness.js";
import type { ToolEffect } from "./types.js";
import { type BackgroundHardKillOutcome, type BackgroundShellCapability, type BackgroundShellId } from "./background-shell.js";
import type { WorkflowRunStore } from "./workflow-run-store.js";
import { type BackgroundAgentStore } from "./background-agent-store.js";
import type { TaskNotificationPayload } from "./task-notification.js";
import type { ToolResultStore } from "./tool-result-store.js";
import { type SemaTaskType, type StopSource, type SemaTaskStatus, type TaskRetrievalStatus, type UnifiedTaskOutput, type UnifiedTaskResult, type SemaTaskHandle, type TaskAccess, type BackgroundAgentTaskHandle, type MonitorTimers, type RegisterMonitorInput, type RegisterWorkflowInput, canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE } from "./task-registry-shared.js";
export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
export type { ParkedClaimTicket, RegisterBackgroundAgentInput } from "./task-registry-shared.js";
import type { RegisterBackgroundAgentInput, ParkedClaimTicket } from "./task-registry-shared.js";
import { type AgentStateSource } from "./agent-state.js";
import type { CheckpointSeat } from "./checkpoint-seat.js";
export type { SemaTaskType, StopSource, SemaTaskStatus, TaskRetrievalStatus, UnifiedTaskOutput, SemaTaskHandle, TaskAccess, MonitorTimers, RegisterMonitorInput, RegisterWorkflowInput, };
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
export interface TaskKind {
    type: SemaTaskType;
    effect: ToolEffect;
}
export interface RegisterBackgroundBashInput extends TaskAccess {
    shellId: BackgroundShellId;
    env: ExecutionEnv & BackgroundShellCapability;
    description?: string;
    toolUseId?: string;
    id?: string;
    now?: number;
    /** (design/129 discipline): session-resident background bash — `owner` MUST be the
     *  sessionId (a later turn's fresh per-turn owner reaches it via `access.sessionId`). Survives the
     *  run teardown (settle/evict skip + dispose except-list); terminal anchor = `reapSessionBackground`. */
    sessionScoped?: true;
    /** design/116 §7 G2b: when set, the registry WATCHES the process (1s poll, increments spooled with a
     *  rolling bound) and fires ONE completion task-notification at the terminal state — the background-bash
     *  half of CC's LocalShellTask completion notify. Also flips TaskOutput to re-readable spool reads. */
    onTerminal?: (notification: TaskNotificationPayload) => void;
    /** CC 2.1.209 对齐批A A5: the task's OUTPUT FILE — the launch receipt advertises it and the watcher
     *  mirrors every polled increment into it (append-only, unbounded — unlike the rolling in-memory
     *  spool), so "Read the output file path" is a real alternative to TaskOutput. Terminal
     *  task-notifications carry the same path (`output_file`). Primarily meaningful with `onTerminal`
     *  (only the watcher mirrors increments live); a watcher-less row still gets ONE final best-effort
     *  append at TaskStop/run-teardown (the tail drain), so the file is written even without a watcher —
     *  there is just no live mirror. Callers create the file (empty) before registering. */
    outputFile?: string;
    /** The LAUNCHING task's retain declaration (`TaskSpec.retainBackgroundProcesses`) — the task lane of
     *  {@link retainsBackgroundProcesses}. Carried onto the row as its `retained` mark at registration
     *  (together with the env lane), so faces that never see the spec — the session reap, the exit-boundary
     *  hard face — answer the same retain question the launch receipt answered. Passing it (true or false)
     *  STATES the lane: a later run teardown does not re-judge the row. Absent = the launcher does not know the
     *  lane; the owning run's teardown supplies it. */
    retainBackgroundProcesses?: boolean;
}
/** One row's receipt from {@link TaskRegistry.killAllBackgroundShellsNow} (closed `outcome` set; `retained`
 *  present when the row was retained, whatever the outcome). */
export interface BackgroundShellHardKillReceipt {
    taskId: string;
    type: "background_bash" | "monitor";
    owner?: string;
    retained?: true;
    outcome: BackgroundHardKillOutcome | "retained_skipped";
}
export interface TaskPollOptions {
    workflowStore?: WorkflowRunStore;
    /** design/151 S1a: the durable agent ledger — a live-registry miss on an `a*` id falls back to it
     *  (terminal snapshot / honest cross-instance "running elsewhere"), behind {@link canAccessAgentRecord}. */
    agentStore?: BackgroundAgentStore;
    filter?: string;
    /** design/116 W3 (CC TaskOutput `block`): wait until the task reaches a terminal state (or `timeoutMs`)
     *  instead of returning the instantaneous snapshot — saves the model blind re-poll turns. The TOOL layer
     *  defaults this to TRUE (CC 187 `block: default(!0)`, 裁定 2026-07-02); registry callers pass it explicitly. */
    block?: boolean;
    /** Max wait for `block` (ms). Default 30s, capped at 600s (CC 187). */
    timeoutMs?: number;
    /** Abort signal — a blocked wait must stop when the tool call is cancelled. */
    signal?: AbortSignal;
    /** Mirrors {@link import("./types.js").TaskSpec.oneShot} (see {@link TaskToolOptions.oneShot}
     *  for the full contract): forwarded to the background_agent lane's still-running poll body so it
     *  does not teach "you will be notified" when this run has no later turn to receive one. */
    oneShot?: boolean;
    /** design/158 S1 (RB-205-B 半): the offload store a CLIPPED background_agent result's full text
     *  spills to — the disclosure gains a `ReadToolResult` ref instead of the middle staying gone for
     *  good (see {@link import("./task-registry-agent.js").spillClippedAgentResult}). Absent (a
     *  deployment that never wired a tool-result store) ⇒ legal degrade, byte-identical to pre-S1 output. */
    toolResultStore?: ToolResultStore;
    /** The run's checkpoint seat: a PARKED background_agent row's approval card is read from it for the
     *  answer's `agentState` ({@link import("./agent-state.js").readAgentState}). Absent ⇒ the card reads
     *  `unreadable`; nothing else changes. */
    checkpointSeat?: CheckpointSeat;
}
export interface TaskStopOptions {
    workflowStore?: WorkflowRunStore;
    /** design/151 S1a: durable-ledger fallback for the stop face (an off-process row reads `not_local` —
     *  workflow-fallback parity — instead of a misleading not-found). */
    agentStore?: BackgroundAgentStore;
}
export interface TaskToolOptions extends TaskAccess {
    registry: TaskRegistry;
    workflowStore?: WorkflowRunStore;
    /** design/151 S1a: durable agent ledger — threads into pollTask/stopTask fallback reads. */
    agentStore?: BackgroundAgentStore;
    /** the task's soft wall-clock deadline (absolute ms epoch), same source as the Bash
     *  foreground clamp (design/130 P2b). When present, a BLOCKING TaskOutput wait is clamped so it
     *  cannot eat the write-out window: the model gets an honest, visible clamp note instead of a
     *  silent 10-minute blind wait into the finalize wall (TB rs**: 9.1min poll = the exact tax).
     *  CC posture note (anchors/2.1.212/print-mode-drain.md): CC classifies WIND-DOWN waits, and its
     *  block cap rides per-query budgets — a deadline-aware wait is the sema equivalent on the tool face. */
    deadlineMs?: () => number | undefined;
    /** design/157 B17 S2 (fable 终判 D4) — whether a terminal <task-notification> is actually wired for
     *  this mount. Drives the composed description's notification sentence: the pre-S2 literal advertised
     *  the notification unconditionally, which was a false promise on deployments that never wired
     *  `taskNotification` (B17 方案查出的既有假广告). Default true (a direct public-API consumer keeps the
     *  historical wording); prepare-task passes the real wiring state. */
    notificationWired?: boolean;
    /**
     * Mirrors {@link import("./types.js").TaskSpec.oneShot}: this run has no later turn for an
     * async background notification to land in (a headless `-p` process exits once the turn ends).
     * Threaded into the background_agent lane's non-blocking/blocked-timeout "still running" poll body
     * (see {@link import("./task-registry-agent.js").pollBackgroundAgentLane}), which otherwise taught
     * "you will be notified when it completes" — the same failure form `run_workflow`'s launch note was
     * fixed for (RB-215/run-workflow-tool.ts). Default undefined ⇒ byte-compat (pre-RB-220 wording).
     */
    oneShot?: boolean;
    /** design/158 S1 (RB-205-B 半): the offload store a CLIPPED background_agent result's full text
     *  spills to — see {@link TaskPollOptions.toolResultStore} (forwarded verbatim into `pollTask`).
     *  Absent ⇒ legal degrade, byte-identical to pre-S1 output. */
    toolResultStore?: ToolResultStore;
    /** The run's checkpoint seat — see {@link TaskPollOptions.checkpointSeat} (forwarded verbatim into
     *  `pollTask`). The first-party mount passes the same seat the Agent tool parks children into. */
    checkpointSeat?: CheckpointSeat;
}
/**
 * RB-49 B-3 (2026-07-25): the row-metadata projection `getAccessibleTask` returns. Was spelled out as a
 * hand-written literal in THREE places (this signature, the internal cast below, and the SendMessage
 * consumer in agents/subagent.ts) with an identical field list — adding a row field meant editing three
 * spots with zero compiler enforcement (the projection-antipattern family).
 * One exported name, three consumers, drift becomes a type error.
 */
export interface AccessibleTaskRow {
    task_id: string;
    type: SemaTaskHandle["type"];
    status: SemaTaskHandle["status"];
    toolUseId?: string;
    sessionScoped?: boolean;
    description?: string;
    name?: string;
    agentType?: string;
    owner?: string;
    scope?: string;
    parentTaskId?: string;
    parentSessionId?: string;
    rootSessionId?: string;
    /** The row's registration instant (the original spawn time for a first-cycle row): the
     *  revive projection needs it so a revive frame's `startedAt` anchors the ROW, not the wake call. */
    createdAt?: number;
}
export declare class TaskRegistry {
    private handles;
    /** observers of the design/129 session terminal anchor — `reapSessionBackground`
     *  fires each hook (swallow-guarded) AFTER reaping, so session-anchored side state (the session-scoped
     *  subagent retain ledger) releases on the SAME deployment call. Registered idempotently per consumer. */
    private sessionReapHooks;
    /** design/144 §1.2 (child gate): one-shot quiescence watchers keyed by OWNER — each set fires (and is
     *  removed) when that owner's running background-task count reaches zero. Populated by
     *  {@link onBackgroundQuiescence}; poked from every terminal transition / running-row eviction site.
     *  1.284 C3: callbacks carry a REASON — "quiescent" (the count really reached zero) vs "evicted"
     *  (cap overflow dropped the watcher; the owner's tasks may still be running — the subscriber must
     *  NOT treat this as quiescence). */
    private bgQuiescenceWatchers;
    /** One host-callback isolation scope for this registry's five notification seams (reap terminal
     *  notifier, session-reap hooks, quiescence fire + cap-overflow eviction, terminal-once send). Same swallow
     *  as the five hand-written try/catches it replaced, now counted per site and readable for diagnostics. */
    private readonly notifier;
    /** The read face for this registry's CONTAINED host-callback failures (site → count).
     *  The registry has no deployment error sink of its own to disclose to (it is process-global,
     *  constructed before any RunnerDeps exists), so readability IS its disclosure grade: a deployment
     *  that suspects a broken observer polls this instead of grepping for silence. Empty on the
     *  healthy path. */
    notifierFailureCounts(): ReadonlyArray<{
        site: string;
        count: number;
    }>;
    /** design/157 B13 一期(#coreView 解法,B13 档「私有成员解法」定稿): the MON+WF lane free functions'
     *  only seam back into the class — exactly the 5 `RegistryCore` members those two lanes' `this.*`
     *  calls actually reached (grep-verified, see task-registry-shared.ts's own doc comment). Declared
     *  AFTER `handles` (its `handles` field below is captured eagerly at construction, so `this.handles`
     *  must already be assigned); the 4 method arrows close over `this` lazily and don't share that
     *  ordering constraint. Two-期 extends this to `DurableAgentCore` (durable-agent 车道, per the B13
     *  「私有成员解法」 note) without touching this shape. */
    private readonly core;
    durableAgentArmed(id: string): boolean;
    durableAgentRowProbe(id: string): (() => Promise<boolean>) | undefined;
    beginDurableClaim(id: string): boolean;
    endDurableClaim(id: string): void;
    reapDurableAgents(scope: string, deps: {
        store: BackgroundAgentStore;
        /** `listPlaced` (subagent transcript persistence, optional) arms the partition-orphan leg —
         *  see {@link reapDurableAgentsLane}. Pass the deployment's SessionStore directly (the
         *  TtlSessionStore over a placement-capable repo exposes it). */
        sessions?: {
            unpin?(sessionId: string): unknown;
            release(sessionId: string): Promise<void> | void;
            listPlaced?(kind: "subagent", opts?: {
                olderThanMs?: number;
                scope?: string;
            }): Promise<Array<{
                sessionId: string;
                placedAt: number;
            } & ({
                scope: string;
                handle: string;
            } | {
                tupleIncomplete: true;
            })>>;
        };
        mailbox?: {
            drop(scope: string, handle: string): Promise<void>;
        };
    }, policy: {
        now?: number;
        maxAgeMs?: number;
        keep?: number;
        staleRunningMaxAgeMs?: number;
    }): Promise<{
        rowsReaped: number;
        sessionsReleased: number;
        skippedNoSessions: number;
        orphanPlacedReleased: number;
    }>;
    releaseDurableTranscriptAnchor(id: string): void;
    bindBackgroundAgentSession(id: string, sessionId: string): void;
    registerBackgroundAgent(input: RegisterBackgroundAgentInput): string;
    /** #22 — persist this leg's org-memory admission verdict on the durable row (the cross-process
     *  revival's only carrier). See {@link recordBackgroundAgentOrgAdmissionLane}. */
    recordBackgroundAgentOrgAdmission(id: string, verdict: import("./memory-admission.js").OwnOrgAdmissionVerdict): void;
    parkBackgroundAgent(id: string, park: {
        checkpointToken: string;
        seq?: number;
        resolveStop?: () => Promise<boolean>;
    }): "parked" | undefined;
    reconcileParkedAgents(stores: {
        agentStore: import("./background-agent-store.js").BackgroundAgentStore;
        checkpointStore: Pick<import("./checkpoint-store.js").CheckpointStore, "get">;
    }, scope: string, now: number, opts?: {
        staleClaimMaxAgeMs?: number;
    }): Promise<{
        failed: number;
        rolledBack: number;
    }>;
    claimParkedAgent(stores: {
        agentStore: import("./background-agent-store.js").BackgroundAgentStore;
        checkpointStore: Pick<import("./checkpoint-store.js").CheckpointStore, "get">;
    }, handle: string, access: TaskAccess): Promise<{
        ok: true;
        ticket: ParkedClaimTicket;
    } | {
        ok: false;
        reason: "not_found" | "not_parked" | "binding_broken" | "checkpoint_not_pending" | "claim_lost" | "store_unreachable";
    }>;
    rollbackParkedClaim(stores: {
        agentStore: import("./background-agent-store.js").BackgroundAgentStore;
        checkpointStore: Pick<import("./checkpoint-store.js").CheckpointStore, "get">;
    }, ticket: ParkedClaimTicket): Promise<"rolled_back" | "failed" | "lost" | "retry">;
    consumeParkedFlip(id: string, stores: {
        agentStore: import("./background-agent-store.js").BackgroundAgentStore;
    }, ticket: ParkedClaimTicket): Promise<boolean>;
    finalizeParkedResume(id: string): void;
    /** `outcome.cycle` names the revive cycle this settle speaks for (absent = 0 = the original
     *  spawn cycle). A spawn leg never passes it; a caller that settles by LIFECYCLE AUTHORITY (reap /
     *  session release — "whatever is running under this row dies") passes the row's current cycle. See
     *  {@link settleBackgroundAgentLane} for why a stale stamp must be a no-op. */
    settleBackgroundAgent(id: string, outcome: {
        status: "completed" | "failed" | "killed";
        result?: string;
        resultFull?: string;
        error?: string;
        errorCode?: string;
        retryable?: boolean;
        errorKind?: string;
        retryAfterMs?: number;
        stoppedBy?: StopSource;
        seq?: number;
        cycle?: number;
    }): "completed" | "failed" | "killed" | undefined;
    /** design/164 — record one tool-lifecycle beat on a running background-agent row; see
     *  {@link noteBackgroundAgentActivityLane} for why the in-memory row needed a stall signal at all. */
    noteBackgroundAgentActivity(id: string, now?: number): void;
    /** design/164 — collect SESSION-SCOPED background agents that have gone silent for `staleMs`, the
     *  stall-based replacement for the retired wall-clock ceiling on that lane. Caller-supplied policy
     *  like every other retention knob here; see {@link reapStaleSessionBackgroundAgentsLane}. */
    reapStaleSessionBackgroundAgents(staleMs: number, now?: number): number;
    abortBackgroundAgentsForOwner(access: TaskAccess, opts?: {
        skipSessionScoped?: boolean;
        sessionScopedOnly?: boolean;
    }): number;
    /** A durable row's answer with its state: only a parked row waits on the card read; any other row is
     *  answered in place. */
    private serveDurableAgentRow;
    /** The row facts {@link import("./agent-state.js").readAgentState} reads for a background agent this
     *  caller can see — the live handle only (a durable-only row is projected by its reader from the record,
     *  {@link import("./agent-state.js").agentStateSourceOfRecord}). Same access guard as a poll; unknown and
     *  out-of-scope ids read identically (undefined). */
    agentStateSource(taskId: string, access: TaskAccess): AgentStateSource | undefined;
    resolveBackgroundAgentByName(name: string, access: TaskAccess, opts?: {
        preferRunning?: boolean;
    }): {
        status: "found";
        handle: BackgroundAgentTaskHandle;
    } | {
        status: "ambiguous";
        message: string;
    } | {
        status: "not_found";
        suggestion?: string;
    };
    markRetainedContinuation(id: string): void;
    /** The row's current stop-cycle counter, read by the spawn lanes right after registering to
     *  thread into the child's `RunInternals.cycleSeq`; see {@link backgroundAgentCycleSeqLane}. */
    backgroundAgentCycleSeq(id: string): number | undefined;
    /** Subagent transcript persistence (delegation entry caps) — one delegation tree's ACTIVE
     *  (running/pending) a* handles in THIS process, keyed `(scope, rootSessionId)`; SYNCHRONOUS by
     *  contract (the caps check and the registration form one atomic segment). See
     *  {@link activeDelegationHandlesLane}. */
    activeDelegationHandles(scope: string, rootSessionId: string): string[];
    /** ASYNC (revive arbitration) — the durable half is a guarded ownership claim on the row (awaited before the
     *  in-memory flip), so this leg and a cross-process claim arbitrate in one domain instead of both
     *  believing they own the cycle. See {@link reviveBackgroundAgentLane}. */
    reviveBackgroundAgent(id: string, access: TaskAccess, abort?: AbortController): Promise<{
        ok: true;
        cycle: number;
    } | {
        ok: false;
        reason: "not_found" | "still_running" | "recycling";
    }>;
    settleRevivedAgent(id: string, cycle: number, outcome: {
        status: "completed" | "failed" | "killed";
        result?: string;
        resultFull?: string;
        error?: string;
        errorCode?: string;
        retryable?: boolean;
        errorKind?: string;
        retryAfterMs?: number;
    }): "completed" | "failed" | "killed" | undefined;
    unmarkRetainedContinuation(id: string): void;
    attachAgentNotify(id: string, notify: NonNullable<BackgroundAgentTaskHandle["notify"]>, cycle?: number): void;
    /** RB-375 A1 — hang the reap-time terminal notifier on a live background-agent row (see
     *  {@link BackgroundAgentTaskHandle.onReapTerminal}). No-op on unknown/terminal rows: a row that
     *  already settled has its unwind (or the settle itself) owning the terminal story. */
    attachAgentTerminalNotifier(id: string, fn: () => void): void;
    /** RB-375 A1 — the child-unwind side of the terminal-notification once-latch: returns `true` when
     *  the CALLER now owns sending the terminal faces (and claims the latch), `false` when the reap
     *  sweep already sent them (the caller must stay silent — the registry row still takes the settle's
     *  payload/backfill either way; only the announcement is deduplicated). Unknown/GC'd row ⇒ `true`
     *  (no latch survives eviction; the L1-1 late-report posture is unchanged). */
    claimAgentTerminalNotify(id: string): boolean;
    deliverToRunningAgent(id: string, access: TaskAccess, notification: import("./task-notification.js").TaskNotificationPayload, opts?: {
        priority?: import("./task-notification.js").SystemInjectionPriority;
    }): Promise<{
        ok: true;
        disposition: "queued" | "parked" | "pending";
    } | {
        ok: false;
        reason: "not_found" | "not_running" | "no_channel" | "queue_full";
    }>;
    runningBackgroundAgentLabels(access: TaskAccess): string[];
    private pollBackgroundAgent;
    private stopBackgroundAgent;
    /** design/151 §3.1d (HIGH-3): this process instance's durable-row writer identity (random per
     *  registry instance). A fallback read of a `running` row written by a DIFFERENT instance must
     *  never claim liveness — it serves the honest "outcome unknown here" instead. */
    readonly writerId: string;
    /** the writer-liveness LEASE: while any armed durable row is running, one lazy
     *  unref'd interval bumps their `updatedAt` (an empty patch), so row age measures SILENCE, not
     *  run length. A `staleRunningMaxAgeMs` reap policy must therefore be ≫ this period (the store
     *  contract recommends ≥10min) — and even a premature flip self-heals: the real settle's
     *  read-back adopts the bumped rev and re-CASes the truth over the reaper's guess. */
    /** 增量 R-1 — handles a reap sweep is CURRENTLY adjudicating (between its live-handle check
     *  and the delete/release outcome). A revive admitted inside that await window would race the
     *  conditional delete with its own durable transition still unflushed (old rev ⇒ the delete wins,
     *  the session is released under an ADMITTED resume, and the revive's queued write poisons on
     *  "gone"). Membership is synchronous on both sides, with three readers (the `claimingHandles` twin
     *  below carries the same fence set): reap marks BEFORE its terminal check, a revival lane refuses
     *  while marked (the row is being recycled), and the terminal-handle
     *  GC skips a marked id instead of evicting it. */
    private reapingHandles;
    /** The post-delete half of the window above (see {@link DurableAgentCore.reapedHandles}): the sweep's
     *  guarded delete has WON, and the fence is still held while the mailbox is dropped and the session
     *  released. The fence's readers are unchanged — this only splits what a refused revival is TOLD,
     *  since a caller advised to retry a row that no longer exists would retry forever. */
    private reapedHandles;
    /** design/151 §7.3 (F-8) — handles a revival lane is CURRENTLY claiming (between the claim-CAS
     *  decision and the revive registration/rollback). BOTH lanes are writers: the tier-3 SendMessage
     *  claim, and (since the retained revive started claiming its own row) the in-process retained-revive
     *  path, which holds the mark across its store round trip. Synchronous membership on both sides, the
     *  `reapingHandles` twin, with three readers: the reap sweep skips a mid-claim row (its rev is about
     *  to move), the sibling revival lane refuses one (`reviveBackgroundAgent` recycling — a transient
     *  hold, told apart from a row that is actually gone), and the
     *  terminal-handle GC skips it — so the revival lanes, the reaper and the GC can never adjudicate the
     *  same handle concurrently in-process. */
    private claimingHandles;
    private durableHeartbeatTimer;
    mintTaskId(type: SemaTaskType): string;
    registerBackgroundBash(input: RegisterBackgroundBashInput): string;
    /** Background output-file read exemption: true iff `canonicalPath` is EXACTLY the registered output
     *  file of a background bash row that (a) the caller can access (same owner/scope/session visibility as
     *  a TaskOutput poll — {@link canAccess}) and (b) was registered on the caller's OWN execution env —
     *  `env` identity is part of the match, same discipline as {@link runningShellIds}. A canonical path only names a file within ONE env's filesystem
     *  namespace: on a shared registry, a row minted on env A must never exempt a same-named outside-root
     *  file in env B's namespace (same session or not), so instance equality — never a string proxy — is
     *  required. The output file is minted by the ENGINE itself (createTempFile
     *  at spawn/adopt) in the OS temp dir — structurally outside the hands containment root — and its path
     *  is handed to the model in the launch receipt and the completion notification; Read consults this so
     *  it can honor that instruction without widening the root fence (exact registered file only, no
     *  directory/prefix admission). Membership dies with the row (per-run evict / session reap / terminal
     *  GC), so the exemption never outlives the task's addressability. Exact string compare: both sides are
     *  `canonicalPath` outputs for the same env, so a symlinked temp dir cannot cause a miss or a false hit. */
    isBackgroundOutputFile(canonicalPath: string, access: TaskAccess, env: unknown): boolean;
    /** [c209-D]: display-cap-free EXACT id lookup with the same access guard as a poll — identity
     *  resolution must NEVER ride the display-capped `list()` (with >LIMIT visible rows the cap can evict
     *  the addressed id, and a name-layer fallback then MISROUTES the message to a同名 row instead of an
     *  honest not-found). Unknown and out-of-scope ids read identically (info-hiding, undefined both). */
    getAccessibleTask(taskId: string, access: TaskAccess): AccessibleTaskRow | undefined;
    /** design/134 §3.3 (R3 single-source): record WHO is about to kill `id`, at the initiation site,
     *  BEFORE the abort()/status flip — the ordering is load-bearing: the guard below refuses markers on a
     *  non-running handle, so a caller that flips first loses its claim and attribution falls back to
     *  "system". First-marker-wins: an earlier marker (e.g. a service-wire "user") is never overwritten.
     *  Applies to background_agent, background_bash AND monitor handles (the markable kinds — the monitor
     *  watcher/stop lanes mark and read it too); workflow cancellation is out of scope. */
    markStopSource(id: string, source: StopSource): void;
    /**
     * Attribute an ENV-LEVEL blanket sweep before it runs.
     *
     * The incident: a user's `run_in_background` Bash died mid-run reporting `stopped-by:"system"` with a
     * zero-byte output file, and diagnosing it took a cross-repo investigation with a timeline argument.
     * `"system"` is not a cause — it is the DEFAULT, i.e. the signature of a kill that never passed through
     * this registry. Every kill that does pass through records who did it; `disposeBackgroundShells` lives in
     * the execution-env layer, cannot reach the registry, and so left the one class of kill a user actually
     * notices as the only one with no attribution at all.
     *
     * This marks every background_bash row on `env` that the sweep is about to take, so the row the user
     * reads says what happened to it. `except` mirrors the sweep's own keep-list exactly — a spared shell
     * must not be marked, or the next reader would be told a live process had been killed.
     *
     * Attribution only: it never kills anything. The caller has already demonstrated kill capability on the
     * env, which is why there is no access guard (same reasoning as {@link markStopSourceByShellId}).
     */
    markStopSourceForEnvSweep(env: unknown, source: StopSource, except?: readonly string[]): string[];
    /** design/134 KNOWN-ISSUES close-out (env-direct TaskStop): the stop-source annotation face
     *  addressed by the ENV-level shellId — for a kill initiator that holds only the env handle (the
     *  registry-less TaskStop band) and is about to `env.killBackground` directly, i.e. without going
     *  through {@link stopTask}. Resolves the shellId → unified task id and delegates to
     *  {@link markStopSource}, so the running-only + first-marker-wins guards hold unchanged.
     *  ⚠️ `env` is REQUIRED for any effect (RB-479-A②, 5.0.0): a shellId without its env is ambiguous
     *  by construction on a shared registry, so an env-less call is a NO-OP — this marker is
     *  best-effort attribution, not data. No access guard — attribution only (never kills); the
     *  caller already demonstrated kill capability on the env. */
    markStopSourceByShellId(shellId: string, source: StopSource, env?: unknown): void;
    /** Withdraw a pending stop-source marker after the kill it announced FAILED (小优化批 2026-07-13):
     *  a marker left by a failed env-direct kill would win first-marker-wins against the NEXT, real stopper
     *  (e.g. a later user TaskStop reads "parent"). Clears ONLY the exact still-pending marker the caller
     *  minted — same (shellId, env) resolution as {@link markStopSourceByShellId}; a row already settled,
     *  or carrying a different source, is left alone. */
    clearPendingStopSourceByShellId(shellId: string, source: StopSource, env?: unknown): void;
    /** design/134 §3.3: narrow terminal read for consumers (subagent sinkEmit/notify) — only a KILLED row
     *  has an attribution; "system" is the defensive floor for a row that went killed without a landed
     *  stoppedBy (e.g. env-direct flips). Returns undefined for running/completed/failed/evicted rows. */
    getStopAttribution(id: string): StopSource | undefined;
    /** P1-3 — the fill-once completion correlation id (see {@link UnifiedTaskOutput.completionId}), for a
     *  notification-construction call site that already has the task's `id` and needs to attach it to the
     *  payload it is building. Deliberately NOT restricted to a subset of task types (unlike
     *  {@link getStopAttribution}) — `completionId` lives on the shared {@link SemaTaskHandle} base and
     *  every registered task kind, workflow included, carries it the same way. Undefined until the task's
     *  own settle point has run ({@link mintCompletionId}), or for an id this registry never held. */
    getCompletionId(id: string): string | undefined;
    /** Bulk stop-source attribution for one owner — the registry-side primitive that
     *  replaces the service's `list()+markStopSource` per-row loop, which had TWO holes: ① `list()` didn't
     *  expose `sessionScoped`, so on the resume leg (canonical taskId==sessionId, rebuilt taskConfig without
     *  spec.taskId) task-scoped and session-scoped children share the same owner KEY VALUE and can't be told
     *  apart; ② `list()`'s 500-row display cap silently skipped rows on >500-children runs.
     *  Same owner-filter semantics as {@link abortBackgroundAgentsForOwner}: {@link canAccess} + the EXPLICIT
     *  `sessionScoped` flag (design/129 — never inferred from key values), over a DIRECT handle-map walk
     *  (no list() cap in the path). Covers every markable kind ({@link markStopSource}'s own set:
     *  background_agent / background_bash / monitor); each row is delegated to `markStopSource`, so the
     *  running-only + first-marker-wins guards hold unchanged — an existing claim is never overwritten.
     *  Returns how many rows NEWLY took the marker (already-marked / non-running rows don't count). */
    markStopSourceForOwner(access: TaskAccess, source: StopSource, opts?: {
        skipSessionScoped?: boolean;
    }): number;
    /** The run-teardown KILLED-receipt producer for the SHELL lanes (background_bash + per-run
     *  monitor). The runner teardown disposes the processes (`disposeBackgroundShells`) and evicts the rows
     *  (`clearBackgroundForOwner`) — after which NO producer can ever mint the killed notification: the bash
     *  watcher's next poll sees a vanished shell ("nothing to notify about") or its evicted handle and just
     *  stops. So a mid-turn abort (or a normal turn end) silently swallowed children the model had been
     *  promised notifications for ("You will be notified when it completes — do not poll"). The AGENT lane
     *  already has its producer (the child promise's settle → notify); this is the same receipt for
     *  the shell lanes — call it BEFORE dispose/evict so the sink chain (onTerminal/onEvent → the run's
     *  notification wrapper, whose lane is already down at teardown) PARKS the receipt on the per-session
     *  pending store and the session's next run drains it (the pending lane, one shape for all producers).
     *
     *  Attribution (design/134 §3.3): `markStopSource(source)` first — first-marker-wins, so a deployment's
     *  earlier explicit mark (e.g. a service-wire "user" on abort) is never overwritten; `stoppedBy` then
     *  lands from the marker. Rows without a sink still settle (registry truth), they just have no receipt
     *  to send. Session-scoped rows are skipped under `skipSessionScoped` (they outlive the turn; their
     *  terminal anchor is `reapSessionBackground`). KNOWN small race, recorded: a child that exited within
     *  the last watcher-poll interval (~1s) but whose exit the watcher hasn't observed yet still reads
     *  "running" here and is settled `killed` — the receipt is at worst one status coarser, never silent.
     *
     *  (HIGH): the settle EXECUTES the kill itself (await `killBackground`, Result-typed)
     *  before minting the receipt — previously the receipt claimed "killed" while the actual kill was left to
     *  the later `disposeBackgroundShells`, whose failure was swallowed (process alive + registry cleared =
     *  the receipt lied). A failed kill now mints an HONEST receipt naming the failure (the env dispose is
     *  still the idempotent backstop). Returns how many rows were settled. */
    settleKilledForOwner(access: TaskAccess, opts?: {
        source?: StopSource;
        skipSessionScoped?: boolean;
        envDying?: boolean;
        /** Spec-level retain (`TaskSpec.retainBackgroundProcesses`): treat every shell-lane row as if its
         *  env had declared retain — skip entirely (no kill, no killed receipt: nothing died, a receipt
         *  would lie). Same `envDying` exception as the env-capability gate: retain on a dying owned env
         *  is illusory, so those rows still settle with the honest killed receipt. */
        retainProcesses?: boolean;
    }): Promise<number>;
    /** A-005.4/5: the best-effort kill `reapSessionBackground`'s monitor/background_bash arms fire is
     *  fire-and-forget by DESIGN (the reap is synchronous and settles the row regardless of the kill
     *  outcome — see the callers) but was fire-and-FORGET-the-evidence-too: a resolved `{ok:false}` was
     *  never read, and even a promise rejection landed on `.catch(() => undefined)`. Reads the Result,
     *  drops the benign `not_found` case (already dead — same filter the awaited TaskStop kill path uses,
     *  see `killFailed` above), and routes anything else through `shutdownDebug` — the SAME kill-path
     *  forensics channel the row-scoped calls right above these call sites already use for this reap, so
     *  a `SEMA_DEBUG_SHUTDOWN=1` "who killed my process" hunt no longer dead-ends at a silently-swallowed
     *  reap-time failure. Never throws (a forensics call must not fault the reap it is reporting on). */
    private reportSessionReapKillFailure;
    /** design/129: the session-scoped children's TERMINAL anchor — call from your session release/sweep.
     *  Aborts every running session-scoped background child registered under `sessionId` and settles it
     *  `killed` (the child's own catch/then would also classify the abort as killed; this makes the registry
     *  state immediate rather than waiting on the child's promise). Idempotent.
     *  RETAINED shell-lane rows (THE retain predicate over the row's mark and the env's declaration) are left
     *  whole — row, watcher and process (the deliver-a-living-service contract; their terminals: TaskStop, own
     *  exit, env destruction, the hard face under `includeRetained: true`). Every other running shell-lane row
     *  this session owns is killed and settled. {@link reapAllSessionBackground} delegates here.
     *  RB-375 A3 — RETURN-VALUE semantics: the count covers DIRECT hits of this sweep only (rows whose
     *  own arm above matched). Children the reaped agents themselves spawned (`sessionScoped:false`
     *  grandchildren) die COLLATERALLY, via each reaped agent's own teardown
     *  (`abortBackgroundAgentsForOwner` in the child's unwind) — this function never sees those rows,
     *  so they are NOT counted here.
     *  RB-375 A1 — a directly reaped `background_agent` row's terminal notification is sent
     *  SYNCHRONOUSLY by this sweep (through the spawn-lane-hung {@link BackgroundAgentTaskHandle.onReapTerminal},
     *  once-latched against the child's own unwind), so consumers observe the terminal immediately
     *  instead of after the in-flight tool honors the abort (unbounded). */
    reapSessionBackground(sessionId: string, scope?: string): number;
    /** The FULL-registry reap for the ENGINE's own exit path. TOC form:
     *  the engine process IS the session terminal (the shell has no line to call — live evidence:
     *  detached bg children reparent to PID 1 and outlive the engine), so the engine's SIGHUP/SIGTERM/
     *  normal-exit terminus calls THIS. Enumerates every running session-scoped row's (sessionId, scope)
     *  pair — `owner` = the sessionId by design/129 discipline for background_bash/monitor/background_agent
     *  (RB-215 note: NOT for workflow, whose `owner` is the launching LEG's taskId; its session anchor is
     *  `originatingSessionId` — see {@link reapSessionBackground}'s own workflow arm for why) — and runs
     *  the per-session reap on each pair; retained rows are left alone (row and process), exactly like the
     *  per-session anchor. Timing discipline: call at the hardShutdown TERMINUS, never at drain start — an
     *  in-flight turn may still be TaskOutput-following a bg process. Returns total rows reaped.
     *
     *  Its kills go through each env's `killBackground` (possibly a TERM-then-timer ladder), and it reaches
     *  session-resident rows only: every exit terminus calls {@link killAllBackgroundShellsNow} after it. */
    reapAllSessionBackground(): number;
    /** The EXIT-BOUNDARY hard face over every shell-lane row (`background_bash` + `monitor`), session-resident or
     *  not (docs/background-process-lifetime.md is the contract). SYNCHRONOUS (no await, no timer) and
     *  IDEMPOTENT. Visits every row except `completed` (a `killed` row may sit inside a softer stop's grace
     *  window; a `failed` one may have lost its watcher, not its process). A retained row (THE retain predicate)
     *  is left alone under `includeRetained: false` — no default — unless an explicit stop already settled it
     *  `killed`. Every other visited row goes through the seat {@link BackgroundShellCapability.killBackgroundNow}.
     *  One receipt per visited row: `killed` (the seat delivered its kill now) / `already_gone` / `retained_skipped`
     *  / `no_pgid` (no synchronous kill landed — no seat, the seat said so or threw; the env's ordinary
     *  `killBackground` is then dispatched, nothing claimed). Only a running row the seat killed is settled
     *  `killed` (stop source "system", no notification). `reason` is forensics only.
     *  `sessionId` + `scope` narrow the visit to the rows {@link reapSessionBackground} (sessionId, scope) keys on
     *  — the same access (`canAccess` with owner = session = sessionId, in that scope): every session-resident
     *  row of the session, plus its run rows where the run's task id is the session id (the default); a run
     *  named with its own task id is not reached. Absent ⇒ the whole registry. Same receipts either way. */
    killAllBackgroundShellsNow(opts: {
        includeRetained: boolean;
        reason?: string;
    } & ({
        sessionId?: undefined;
        scope?: undefined;
    } | {
        sessionId: string;
        scope: string;
    })): BackgroundShellHardKillReceipt[];
    /** subscribe to `reapSessionBackground` (the design/129 session terminal anchor).
     *  Returns an unsubscribe. Hooks are best-effort observers; a throwing hook is swallowed. */
    onSessionReap(hook: (sessionId: string, scope?: string) => void): () => void;
    /** design/144 §1.2 — the child gate's liveness read: how many BACKGROUND tasks (bg bash / monitor /
     *  bg agent) registered under EXACTLY this owner key are still running. Deliberately an owner-equality
     *  walk, NOT {@link canAccess}: an owner-less handle is world-*readable* but attributable to nobody —
     *  counting it against every owner would park every parent's completion frame behind a stranger's task.
     *  Workflow rows are out of the gate's task class by design (§1.2 names bg bash/monitor/grandchild
     *  agents; a workflow's terminal already notifies on its own lane). */
    countRunningBackgroundForOwner(owner: string): number;
    /** design/144 §1.2 — hard cap on concurrently watched owners (each parked parent notification holds
     *  exactly one watcher; the retain-ledger caps bound this in practice — the cap is a defensive floor).
     *  1.284 C3: overflow notifies the OLDEST owner's watchers with reason "evicted" (CANCELLATION
     *  semantics, not a fake quiescence — the pre-fix synchronous no-arg fire read as "count reached
     *  zero" while the owner's tasks were still running, an early FALSE completion). The subscriber
     *  decides its own degradation (the child gate delivers its parked frame immediately, undecorated —
     *  宁发勿丢); a silently leaked/never-fired watcher would lose the frame forever. */
    private static readonly BG_QUIESCENCE_WATCHERS_MAX;
    /** design/144 §1.2 (child gate, 补发钩子) — subscribe a ONE-SHOT callback fired when `owner`'s running
     *  background-task count reaches zero (reason "quiescent"). Exactly-once discipline for the N-children
     *  race: every terminal transition pokes, only the poke that OBSERVES count==0 fires, and firing removes
     *  the whole owner set first (count-zero judgement + once latch). NEVER fires synchronously from this
     *  call — the caller checks the count itself in the same synchronous block (count > 0 → subscribe; no
     *  await between, so no missed-settle window). 1.284 C3: a cap-overflow eviction fires the victims with
     *  reason "evicted" instead, and ASYNCHRONOUSLY (queueMicrotask) — the new watcher is installed before
     *  any victim runs, closing the re-entrant window where a victim's callback observed a half-updated map.
     *  Returns an unsubscribe. Callbacks are swallow-guarded observers. */
    onBackgroundQuiescence(owner: string, cb: (reason: "quiescent" | "evicted") => void): () => void;
    /** design/144 §1.2 — poke the quiescence watchers of `owner` after a terminal transition (or a
     *  running-row eviction). Cheap no-op without watchers; fires + removes the owner's whole set only
     *  when the running count is zero (the LAST settling task's poke wins — count-zero judgement). */
    private pokeBgQuiescence;
    /** fidelity R1-3: the terminal-notification once-latch. Checks-and-sets BEFORE sending (single-threaded,
     *  no await between check and set — atomic), so a racing producer pair (a teardown settle vs an in-flight
     *  watcher tick that passed its status gate before the settle landed) emits exactly ONE terminal notice.
     *  The latch stays set even if the sink throws — a faulty sink must not re-arm it. */
    private notifyTerminalOnce;
    /** G2b watcher (design/116 §7): 1s poll loop that spools increments (rolling bound) and fires ONE
     *  completion notification at the terminal state. `unref()`d — a watcher never keeps the process alive.
     *  Stops itself on terminal, on a vanished shell (env disposed), or when the handle is evicted. */
    private startBashWatcher;
    /** Every shell-lane row still RUNNING on this env, any owner — the run-boundary dispose's except-list (the
     *  run teardown and the park-only parks: `sweepRunBoundaryBackgroundShells`).
     *  A run's teardown disposes only shells it can attribute to itself: its settle sweep has already killed
     *  and settled the rows of this run that end with it, so whatever registered row is still running here is
     *  either something that settle kept (session-resident, an in-window Monitor, retained) or belongs to
     *  another run or session sharing the env. Unregistered env shells are still disposed. */
    runningShellIds(env: unknown): BackgroundShellId[];
    /** design/135 G2: register a spawned background process as a MONITOR — the watcher polls at the batch
     *  window (200ms), turns each completed stdout line into an event, batches lines within one tick into
     *  ONE notification, enforces the non-persistent timeout, auto-stops an event storm, and fires exactly
     *  one terminal notification (exit code / timeout / auto-stop / env-side kill). */
    registerMonitor(input: RegisterMonitorInput): string;
    registerWorkflow(input: RegisterWorkflowInput): string;
    pollTask(id: string, access: TaskAccess, opts?: TaskPollOptions): Promise<UnifiedTaskResult>;
    stopTask(id: string, access: TaskAccess, opts?: TaskStopOptions): Promise<UnifiedTaskResult>;
    /** S2b — bounded delivery-confirmation wait (see deliverToRunningAgent). Test-tunable. */
    deliverConfirmTimeoutMs: number;
    private lastGcAt;
    /**
     * The data source behind a shell's task list and footer pill: list the caller's OWN tasks as a bounded DISPLAY
     * projection — never the internal handles (no shellId/env/abort). Same owner/scope guard as poll/stop;
     * a caller only ever sees its own tasks. Sorted newest-first, capped at `limit` (default 50).
     * CC parity note: CC's /tasks is a UI/SDK surface, NOT an LLM tool — expose this via SDK/service,
     * do not mount it as a model tool by default.
     */
    list(access: TaskAccess, opts?: {
        limit?: number;
    }): Array<{
        task_id: string;
        type: SemaTaskType;
        status: SemaTaskStatus;
        /** design/129 lifetime flag, surfaced for observability — on resume-leg deployments the
         *  canonical taskId==sessionId, so task- and session-scoped rows share an owner KEY VALUE and only this
         *  explicit flag tells them apart. Bulk attribution should use {@link markStopSourceForOwner}, not a
         *  list()-driven loop (the 500-row cap). */
        sessionScoped: boolean;
        /** Present on a shell-lane row (`background_bash` / `monitor`) that THE retain predicate keeps alive
         *  past the automatic reaps — the "keeps running" half of an exit listing: the controlled-exit reap and
         *  the hard face under `includeRetained: false` leave it running; every other running shell row stops. */
        retained?: true;
        description?: string;
        toolUseId?: string;
        createdAt: number;
        updatedAt: number;
    }>;
    /** Throttled {@link gc} for hot-path callers (design/115 review B5): prepare-task runs once per task,
     *  and a full-map sweep per prepare is pure overhead — sweep at most once per minute. */
    maybeGc(now?: number): number;
    gc(now?: number, terminalTtlMs?: number): number;
    clearBackgroundForOwner(access: TaskAccess): number;
    private resolveInternal;
    private pollBackgroundBash;
    private pollWorkflow;
    private stopBackgroundBash;
    private pollMonitor;
    private stopMonitor;
    private stopWorkflow;
}
export declare const defaultTaskRegistry: TaskRegistry;
export declare function createTaskOutputTool(opts: TaskToolOptions): AgentTool;
export declare function createTaskStopTool(opts: TaskToolOptions): AgentTool;
