UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

952 lines • 70.6 kB
import type { RunnerSelfSeat } from "../core/runner/contracts.js";
import type { AgentDefinition, Model, ModelRef, TaskLimits, TerminalCause, ToolSpec } from "../core/types.js";
export { childThinkingSeat, deploymentSubagentThinking, deploymentSubagentTierModel } from "./child-model-seat.js";
import { type ExecutionEnv } from "../internal/harness.js";
import type { TaskNotificationPayload } from "../core/task-notification.js";
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX } from "../config/defaults.js";
/**
 * The two GRANULARITY facts a delegation's terminal `usage` carries beside tokens/turns/cost. The
 * workflow lane's completion notification publishes `tool_uses` (summed `stats.toolCalls`) and
 * `duration_ms` (wall clock), and the Agent tool's own SYNC report publishes both in its `<usage>`
 * footer, so a delegation TERMINAL face (the background/fork/revive notification and its observer
 * twin) that reported neither would have the same child answering "how much work, how long" on one
 * lane and refusing on the other.
 *
 * PROVENANCE is the workflow lane's — `stats.toolCalls`, the RUN's own count — because that is the
 * quantity the other side of the comparison publishes, and because it is available on every terminal
 * lane, including the revive lane, which has no parent-side forwarding observer at all.
 *
 * WHAT THIS DOES NOT CLOSE, since the honest scope is narrower than "one name, one meaning": the Agent
 * tool's SYNC `<usage>` footer keeps its own `tool_uses`, counted by the parent-side
 * `toolStatsCounter`, and the two numbers differ in BOTH directions for the same child — that counter
 * EXCLUDES the delegation tool's own calls and INCLUDES descendants' (the reference implementation's
 * nested fold), while `stats.toolCalls` counts the direct child's calls and counts its delegations
 * among them. A child that itself delegates therefore reports two different `tool_uses` depending on
 * whether it ran synchronously or in the background. That footer is a byte-pinned anchored face;
 * reconciling the two is a separate ruling, and this is where the divergence is written down.
 *
 * Knownness follows `delegatedCostField`'s rule exactly: `toolCalls` is optional on the stats face
 * ("undefined means the gateway reported no usage", and a checkpointed resume folds it per-leg), so the
 * key is ABSENT rather than zeroed — a fabricated 0 reads as "ran no tools", which is a claim, not a
 * gap. Duration is always knowable (the caller holds the lane's launch instant), so it is unconditional.
 *
 * ONE mint, in the model-facing `TaskNotificationPayload.usage` spelling (rendered into `<usage>` as JSON,
 * where the workflow lane and the Agent sync footer both spell them snake_case); the observer
 * ({@link import("../core/types.js").BackgroundChildEvent}) face's camelCase `toolUses`/`durationMs` is
 * that frame's projection (`backgroundChildTerminal`), so the two faces cannot report different numbers.
 *
 * Exported at MODULE level (not from the package index — the same posture as `completedAgentCard` and
 * `classifySubagentError`): the ABSENT arm is the point of the knownness rule and is not reachable
 * through a real child. This runner initializes `stats.toolCalls` to a number on every path, so only a
 * `TaskResult` from elsewhere — a foreign runner, an older checkpoint — carries the
 * optional-and-missing shape the stats face declares, and a pin that only sees a live child cannot
 * tell "absent" apart from "zero".
 */
export declare function delegatedGranularityFieldsWire(stats: {
    toolCalls?: number;
}, elapsedMs: number): {
    tool_uses?: number;
    duration_ms: number;
};
export { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX };
import { SubagentRetainLedger } from "./retain-ledger.js";
import { type PeerSelfRef } from "./peer-admission.js";
export type { SubagentStep, SubagentEditedFile } from "./subagent-steps.js";
/** What {@link layeredReviewSample} produced: the text actually sent, plus the honest read/total counts. */
interface ReviewSample {
    text: string;
    readChars: number;
    totalChars: number;
    /** False ⇒ `text` IS the whole field (short enough to fit the budget); true ⇒ windows + gap markers. */
    sampled: boolean;
    /** How many gap markers THIS sampler emitted — the count a reader can hold the text against. */
    gaps: number;
}
/** Layered head/middle/tail sample of `text` under `budget`, with each skipped span marked in band so the
 *  reader can see that the material is discontinuous. Exported for tests (window determinism). */
export declare function layeredReviewSample(text: string, budget?: number, windows?: number): ReviewSample;
/** Default model-facing name of the delegation tool (CC 2.1.187 `Agent`; legacy alias `Task`). */
export declare const DEFAULT_SUBAGENT_TOOL_NAME = "Agent";
/**
 * extraTools 快审 S4 — per-root-spawn-tree cap on `extraTools` factory evaluations. The factory runs
 * once per spawn at EVERY level (the nested delegation tool threads it down), so an unbounded tree is
 * depth × breadth × turns factory calls. 拍值理由: the default maxDepth is 3 (RB-292; it was 5 when this
 * budget was set, so the cap only ever got roomier relative to the tree) and legitimate trees stay
 * well under a few dozen spawns — 64 is comfortable headroom above any real workload while capping the
 * amplification at a constant. Exceeding it degrades THAT spawn to zero injected tools (the child still
 * runs) with the same generic-note + host-sink disclosure as a factory failure (S3 posture).
 */
export declare const EXTRA_TOOLS_MAX_FACTORY_CALLS_PER_TREE = 64;
/** RB-292 — default delegation nesting depth. CC 2.1.220 `bee()` @272064 falls back to `aHu = 3`
 *  @272076 (cli250.js:37597); the previous value 5 was CC 2.1.201's `Y4t` and is a stale anchor. Semantics (off-by-one)
 *  are pinned on {@link SubagentToolOptions.maxDepth}. */
export declare const DEFAULT_SUBAGENT_MAX_DEPTH = 3;
/**
 * extraTools 快审 S3 — the FIXED model-facing disclosure for a failed factory evaluation. Deliberately
 * generic: the original error (a registry/config fault) can carry paths/hostnames/credentials, and this
 * note reaches the model face AND the persisted transcript. The raw error goes to
 * {@link SubagentToolOptions.onExtraToolsError} only.
 */
export declare const EXTRA_TOOLS_FAILED_NOTE = "note: extraTools evaluation failed \u2014 the injected tool set was skipped for this spawn.";
/** extraTools 快审 S4 — the fixed model-facing disclosure for a budget-exhausted (skipped) evaluation. */
export declare const EXTRA_TOOLS_BUDGET_NOTE = "note: extraTools evaluation was skipped \u2014 this spawn tree exhausted its factory-call budget (64); no dynamic tools were injected for this spawn.";
/**
 * CC206-B 欠账 — the Agent completed card's structured `toolStats` (CC 206:541193-541201 `sfd` schema:
 * seven counters, `.optional()`; counting logic 206:540431-540491 `_my`). CC derives the counts from the
 * child's transcript (assistant `tool_use` blocks) and FOLDS nested delegations' toolStats in via each
 * user message's `toolUseResult.toolStats`; sema counts on the shared forward-event sink instead — the
 * sink re-threads DESCENDANT events too (grandchildren bubble through, see subagent-steps.ts header), so
 * the cumulative semantics match without a fold step. Delegation calls themselves are excluded from every
 * bucket (CC excludes `gi`=Agent and `yU`=Task, 206:540453-540455). `linesAdded`/`linesRemoved` mirror CC
 * `i3r` (206:399357-399375): LINE COUNTS OF THE EDIT ARGUMENTS (new/old_string, Write content, NotebookEdit
 * new_source) — not a real diff; honest to CC's own approximation.
 *
 * RB-303 — ANCHOR REFRESHED to CC 2.1.220 (schema @409081-409090, counting `U2_` @408252-408312).
 * The seven counters below are byte-for-byte the same seven, and the counting rules are unchanged
 * (Agent/Task excluded; nested folds via each user message's `toolUseResult.toolStats`). Two 220
 * facts the 206 anchor predates:
 *  • 220 declares an EIGHTH, optional field: `frameCount`. It counts calls to CC's Artifact tool
 *    (`F2_ = ARTIFACT_TOOL_NAME` @409045) with `input.action !== "list"`, and it is the one counter
 *    that also joins the "did this child do anything at all" sum (@408306-408312). sema has no
 *    Artifact tool, so there is no call to count: the field is deliberately NOT added (an
 *    always-absent counter is noise on every consumer). Registered as a divergence candidate — if a
 *    deployment ever mounts an artifact-shaped tool, this is where it lands.
 *  • 220's Agent result carries `modelsUsed` (ordered distinct models, emitted only when length > 1
 *    @408398) and `resolvedModel` (@466120-466127) beside toolStats. sema reports the resolved model
 *    through `TaskResult.model` and has no mid-run swap history on the delegation card. Also
 *    registered rather than added: the swap list needs a per-turn model ledger sema does not keep,
 *    and inventing a one-element array would be a face without a mechanism.
 */
export interface SubagentToolStats {
    readCount: number;
    searchCount: number;
    bashCount: number;
    editFileCount: number;
    linesAdded: number;
    linesRemoved: number;
    otherToolCount: number;
}
/**
 * design/135 §0 (Fork 收编) — the BUILT-IN `subagent_type` value that routes an `Agent` call to the fork
 * execution path (CC-exact abstraction shape: fork lives in `Agent.subagent_type`'s value domain, not in a
 * separate tool). Always present in the `subagent_type` enum — with or without `spec.agents` — UNLESS the
 * deployment defines its own agent named "fork" (the definition wins; noted in the tool description).
 */
export declare const FORK_SUBAGENT_TYPE = "fork";
/**
 * The BUILT-IN "general-purpose" `subagent_type` alias (CC 2.1.207 parity — 锚 pretty.js:459482 `oje`:
 * CC's agent registry always contains a built-in `general-purpose` definition with `tools: ["*"]`, and an
 * OMITTED `subagent_type` defaults to it, :492377 `t ?? oje.agentType`). CC-ecosystem prompts/skills
 * therefore hardcode `subagent_type: "general-purpose"` freely. sema's equivalent of that built-in is the
 * plain clean-context delegation (full tool pool, SUBAGENT_PROMPT persona) — so the literal FOLDS to the
 * omitted-type path instead of bouncing as an unknown type. A deployment (or builtin) agent definition of
 * the SAME NAME wins and disables the fold (fork-shadowing precedent, CC merge order: later sources
 * override the built-in entry).
 */
export declare const GENERAL_PURPOSE_SUBAGENT_TYPE = "general-purpose";
/** The `subagent_type` words a default deployment accepts with no agent of its own: the two built-in definitions' names
 *  and the two built-in routes (a same-named deployment definition shadows the built-in); hosts render pickers from it. */
export declare const BUILTIN_SUBAGENT_TYPES: readonly ["Explore", "Plan", "fork", "general-purpose"];
/** One word of {@link BUILTIN_SUBAGENT_TYPES}. */
export type BuiltinSubagentType = (typeof BUILTIN_SUBAGENT_TYPES)[number];
/**
 * design/136 §6 盲点① (1.256) + §2.1.a (BREAKING batch) — the fork-GOVERNANCE predicate for the
 * `Agent(subagent_type:"fork")` route (the ONLY fork face since the standalone `Fork` tool was retired,
 * design/136 §2.1: the capability moved wholesale into `Agent.subagent_type`'s value domain — CC-exact shape).
 *
 * `TaskSpec.enableFork` is opt-OUT (design/136 §2.1.a default flip, matching CC — fork is available by
 * default, bounded only by the durable session capability): only an EXPLICIT `enableFork:false` denies
 * (`denied:"task"`); `runtimeCaps.allowFork:false` denies per-principal (`denied:"principal"` —
 * tighten-only, mirrors allowWorkflows). Undefined on both axes ⇒ governance does not object; the
 * capability three-checks in the execute path (insideFork / sessionId / hasSessionFork) still apply.
 *
 * prepare-task computes this once per run and threads it to the Agent tool as `ctx.forkAccess`
 * (Runner-held, never a model argument). A denial is HONEST (`fork.disabled` result), never a silent
 * downgrade to a clean delegation. A directly-constructed `createSubagentTool` outside a Runner has no
 * `ctx.forkAccess` ⇒ capability checks only.
 */
export declare function forkGovernanceDenial(enableFork: boolean | undefined, allowFork: boolean | undefined): "task" | "principal" | undefined;
/**
 * design/135 — the trusted per-call worktree-isolation lane for the Agent tool (CC `isolation: "worktree"`).
 * `mint` creates a detached git worktree under the task root via {@link addWorktree} (throws when the root is
 * not a git repository — the caller surfaces the honest error text); `finish` implements CC's "auto-cleaned
 * if unchanged": the worktree is removed when the child left `git status` clean, and KEPT (returning "kept")
 * when the child made changes, so the parent can inspect/merge before removing it. Runner-filled onto
 * {@link import("../core/types.js").ToolExecuteContext.worktreeIsolation}; never a model argument.
 */
export interface SubagentWorktreeIsolation {
    /** Create a detached worktree for a child. Throws when the base root is not a git repository. */
    mint(childId: string): Promise<{
        worktreeDir: string;
    }>;
    /** CC "auto-cleaned if unchanged": remove the worktree when clean, keep it when the child made changes. */
    finish(worktreeDir: string): Promise<"pruned" | "kept">;
}
/** Is `result` one of the delegation tool's refusals before any child ran ({@link notStartedResult})? The brand only: the identity
 *  of its `details` object, never what the result says. */
export declare function isNotStartedDelegationResult(result: unknown): boolean;
/**
 * Build the {@link SubagentWorktreeIsolation} helper over a base env + repo root. Called by `prepare-task`
 * (closure over the run's ExecutionEnv/taskRootPath — trusted, mirrors the scheduler/fork mounts). The
 * worktree-rooted env handed to {@link addWorktree} is a thin prototype view over the base env with `destroy`
 * MASKED (an own `destroy: undefined`), so tearing the worktree down can never cascade into destroying the
 * parent's own (possibly factory-owned) env.
 */
export declare function createSubagentWorktreeHelper(baseEnv: ExecutionEnv, repoRoot: string): SubagentWorktreeIsolation;
/** Names an `AgentDefinition.name` must not collide with (would confuse the enum vs a real tool name). Includes the
 *  default delegation tool name itself, so a child agent can't be named "Agent" even when a custom `opts.name` is set. */
export declare const RESERVED_AGENT_NAMES: ReadonlySet<string>;
/** Cap for the inline-defused worker-controlled report metadata lines (blocked_reason / error). Matches the
 *  ask-question.ts inline-value convention (300) — a one-line operator-facing reason, not a bulk payload. */
export declare const REPORT_FIELD_MAX = 300;
/**
 * The machine-readable failure class stamped on a FAILED sub-agent report (the Agent card's `error_kind`
 * beside `retryable`). A sema form, not a CC one: CC's kind lives on an error class and gates
 * partial-output handback, not retry; sema's pair answers the parent's one question — re-delegate or not.
 *  · `rate_limit` / `network` / `timeout` — transient (re-delegating the same subtask may succeed);
 *  · `governance`                            — a registered policy-plane refusal; retryability is the
 *                                              governance table's transient/terminal split;
 *  · `logic`                                 — a coded failure re-delegating unchanged cannot fix
 *                                              (auth / invalid_request / http / output.* / budget axes);
 *  · `unknown`                               — the child reported NO code: nothing structural to classify,
 *                                              and the class is not guessed from the message (not retried).
 */
export declare const SUBAGENT_ERROR_KINDS: readonly ["rate_limit", "timeout", "network", "logic", "governance", "unknown"];
export type SubagentErrorKind = (typeof SUBAGENT_ERROR_KINDS)[number];
/**
 * Classify a FAILED child run into its `error_kind` + `retryable` pair from the child's STRUCTURED
 * terminal code alone (the `TerminalCause.failed.code` taxonomy: brain codes, `limits.*`, `budget.*`,
 * governance codes). The message is never read: a class read out of prose moved when the prose moved,
 * and a remote's own error text could steer it. A child with no code is `unknown`. Returns undefined
 * for a non-failed child (no error to classify).
 */
export declare function classifySubagentError(terminal: TerminalCause): {
    errorKind: SubagentErrorKind;
    retryable: boolean;
} | undefined;
/**
 * The Agent family's **completed** `tool_end.structured` card, assembled from an EXPLICIT field
 * whitelist over the child `TaskResult`.
 *
 * `TaskEvent.tool_end.structured`'s 双面契约 (core/types.ts) states that a host MUST render a
 * delegation's landing report from `structured` and MUST NOT scrape the model-facing text — so this
 * card has to carry `type:"agent"` and survive the card projector's allowlist. A bare `{...child}`
 * spread does not: with no `type` key the projector drops it, and the one card the contract names is
 * the one card that can never arrive.
 *
 * ### Reconciling with "never leak the whole child TaskResult"
 * tool-output-projection.ts's own header calls the subagent `details` its example of what must NOT
 * reach the public stream. That statement is about the SPREAD, and it stays true: what rides here is a
 * fixed, enumerated, host-facing field set — not the child result object. The distinction is
 * load-bearing rather than stylistic, because `TaskResult` carries fields that must never leave the
 * process:
 *
 * - **`checkpointToken`** — the resume CAPABILITY ("never log it or put it in a URL", per its own
 *   JSDoc). A durable pause is short-circuited into `durablePauseDelegationReport` well before this
 *   point, but that is NOT what keeps the token off the wire: a child whose orchestrator mapped an
 *   unexpected `suspended` down to `failed` (`errorCode:"unexpected.suspended"`) arrives here carrying
 *   one, and a spread would publish it with the completed card.
 * - **`checkpointGate`** — the token's other half (who must approve what); useless without the token
 *   and part of the same capability story.
 *
 * Everything admitted below is already on the MODEL face of the same tool result (status, result text,
 * failure attribution, run totals, the transcript id) or an additive key on `details`
 * (`resolvedModel` / `toolStats` / `worktreePath`). So the WIRE carries exactly the fields a host
 * needs to render the report, and nothing new becomes readable. New `TaskResult` fields do NOT join
 * automatically — that is the point: admitting one is a deliberate edit here.
 *
 * ### What this whitelist actually governs (wider than "the wire")
 * `details` is the tool result's structured face, so this set is ALSO what the in-process consumers
 * see: the parent's `afterToolCall` hook, the persisted tool-result message in the parent transcript,
 * and anything reading `toolResult.details` — not only `tool_end.structured`. Omitting a field here
 * withholds it from all of them, which is why dropping a machine-actionable field (`retryAfterMs`,
 * the wait hint paired with `errorCode:"usage.window_exhausted"`; `degraded`, the reduced-quality
 * fact) is a real loss. The capability fields stay excluded on the same reading — the whitelist's
 * reach is the reason their exclusion is worth anything.
 *
 * The card keeps `type:"agent"` (rather than minting a second word): CC models the Agent tool's output
 * as one union discriminated by `status`, and both sema cards already carry `status` —
 * `"async_launched"` for the receipt, the child's terminal status here. A consumer branches on it.
 *
 * Exported at MODULE level (not from the package index, same as `classifySubagentError`) so the leak
 * boundary can be pinned against a child result carrying a token: the `unexpected.suspended` shape is
 * reachable in production but expensive to stage, and a pin that only sees an ordinary completed
 * child cannot tell this whitelist apart from a spread.
 */
export declare function completedAgentCard(child: {
    taskId: string;
    sessionId: string;
    /** The child's terminal cause. The card speaks its projection (`status` + the failure-attribution keys), never the
     *  cause's own members: a paused token, or the pause a nested boundary wrapped on `nestedPause`, is the resume CAPABILITY. */
    terminal: TerminalCause;
    model?: string;
    result: string;
    salvagedOutput?: string;
    /** The child run's provider-boundary fault assertion, verbatim from its `TaskResult.apiFailure`.
     *  PRESENCE is the claim ("this child's terminal failure came from the transport/provider, not from
     *  this deployment refusing to send, a limit, or unusable model output"); the members are whatever
     *  the failing attempt stated about itself (`status` when the provider answered with one, `requestId`
     *  when it named one). An EMPTY object is therefore meaningful and must not be normalized away: it
     *  says "provider fault, unlabelled", which is a different statement from absence ("not a provider
     *  fault"). Absent on every non-provider terminal and on every completed child. */
    apiFailure?: {
        status?: number;
        requestId?: string;
    };
    retryAfterMs?: number;
    degraded?: unknown;
    structuredOutput?: unknown;
    stats: unknown;
}, extras: {
    subagentType?: string;
    error?: {
        errorKind: SubagentErrorKind;
        retryable: boolean;
    } | undefined;
    worktreePath?: string;
    toolStats?: SubagentToolStats;
    /** Ruled 2026-08-05 (tier-fallback discriminator): the caller REQUESTED a model word that did not
     *  bind (no roster / not on the roster) and the child ran on its inherited default instead. The
     *  fallback behavior itself is unchanged and deliberate; this makes it machine-visible on the
     *  delegation face (closed set, single member today). Absent = the requested word (or no word)
     *  bound normally. */
    modelFallback?: "inherit_no_tier_binding";
    /** RB-201 FO-3 — the handback review's warning line, when a completed child's work was flagged.
     *  It rides as its OWN key rather than being folded into `result`, because `result` is contracted
     *  to be the child's final assistant text verbatim: a structured consumer diffing or re-delivering
     *  it must not receive an engine-authored sentence inside that field. Absent when nothing was
     *  flagged (or no review was armed), so a consumer must not read presence as a verdict source for
     *  anything else. */
    handbackWarning?: string;
    /** design/180 half A — the child's delegation runtime-provenance attestation, reduced from its
     *  recorder aggregate at delivery. Present only when the parent chain armed the recorder; the
     *  parent-side judgment (the content-origin wrap) reads it off this card — `clean` retires the
     *  static face verdict for this call, `external` marks with the observed-event reason, `unknown`
     *  (or absence) keeps the static floor, whose mark action follows the parent run's deployment
     *  evidence standard (design/324, `RunnerDeps.memoryDelegationEvidence`). Deliberate whitelist
     *  admission (see the boundary note). */
    attestation?: import("../core/memory-engine/delegation-provenance.js").DelegationAttestation;
}): Record<string, unknown>;
/**
 * ruled 2026-08-04 — the DELEGATION-REPORT vocabulary for a child that durably paused at an approval
 * gate. Distinct from the orchestrator-facing `unexpected.suspended` / `unexpected.needs_review`
 * TaskResult codes (`suspend-guard.ts`, unchanged): those say "an orchestrator got a status it does not
 * handle"; these say what actually happened to THIS delegation. The report used to speak the
 * `unexpected.*` pair plus "treat this delegation as failed", which is a false statement about a run
 * that is alive, holds a checkpoint, and is waiting for a human.
 */
export declare const SUBAGENT_SUSPENDED_AWAITING_APPROVAL = "suspended.awaiting_approval";
/** {@link SUBAGENT_SUSPENDED_AWAITING_APPROVAL}'s twin for a `needs_review` pause (plan review / dry run). */
export declare const SUBAGENT_SUSPENDED_NEEDS_REVIEW = "suspended.needs_review";
/** ruled 2026-08-04 — a background child that paused durably but could NOT be parked, and whose
 *  checkpoint was therefore expired under the no-orphans rule: nothing is pending approval anywhere. */
export declare const SUBAGENT_SUSPENDED_CHECKPOINT_EXPIRED = "suspended.checkpoint_expired";
/**
 * Subagent steer verb (2026-07-03) — the handle emitted to `RunInternals.onSubagentSpawn`
 * when a deployment opts into steerable sub-agents. Mirrors {@link WorkflowAgentHandle}: `steer`
 * injects fenced operator guidance into the RUNNING child and returns a correlation marker; it
 * rejects with `steering.not_running` once the child ends. The handle never reaches the model.
 */
export interface SubagentSteerHandle {
    /** The child run's unified task id (`spec.taskId ?? sessionId` fallback resolves post-start, so this
     *  is the SPAWN-time identity: the parent's tool call id — stable, unique per delegation). */
    parentToolCallId: string;
    /** The child's display name (taskName / agent-type), when one was threaded. */
    agentName?: string;
    /** Inject fenced operator guidance into the running child; resolves to the correlation marker.
     *
     * `opts.inputId` (C6 — the workflow handle's twin, same form same seat): a PASS-THROUGH of the
     * underlying `TaskStream.steer` correlation/idempotency key (design/171 §6.3 — its whole
     * contract, value domain and typed refusals are that verb's; absent ⇒ byte-identical to every
     * pre-existing call). ⚠️ Same deliberate arm gap as the workflow twin: each call mints a FRESH
     * correlation marker into the framing, so a retry under the same id is a same-id-DIFFERENT-
     * instruction call and refuses typed `steering.duplicate_input_id` (`detail.differs` always names `text`
     * here) — the key buys AT-MOST-ONCE
     * (loud refusal), never silent idempotent replay. */
    steer: (content: string, opts?: {
        inputId?: string;
    }) => Promise<string>;
    /** Resolves when the child settles (the tool's own await — exposed so a registry can auto-evict). */
    settled: Promise<void>;
    /**
     * design/122 D1 — the retained child session id; present ONLY when the parent run enabled
     * {@link import("../core/types.js").TaskSpec.retainSubagentSessions} AND this child was actually
     * retained. ⚠️ Control-plane only: this is a continuation CAPABILITY — never expose it to
     * clients; target children by the opaque `parentToolCallId` instead.
     */
    childSessionId?: string;
    /**
     * design/122 D2 — REVIVE the settled child with a new operator prompt (CC `dfe` resumeAgentBackground
     * parity): the SAME session gains a fenced follow-up user turn and the child continues in the
     * background from its full prior context. ALWAYS ASYNC (even when the original spawn was a synchronous
     * delegation) — the caller is the operator/shell, not the parent model; the parent run's state is
     * untouched and completion is announced ONLY through the deployment-level background-notify sink
     * (never the parent-model `ctx.onTaskNotification` lane — r1-M5). Resolves to the correlation marker
     * (same contract as {@link steer}). Rejections carry an `Error.code`: `steering.still_running` (the
     * child — or a prior resume — is still in flight), `resume.retain_off` (the parent run did not retain
     * this child), `resume.evicted` (TTL/max/parent-end evicted the retained session), `resume.cap`
     * (per-child resume cap reached), `resume.session_not_found` (the session vanished from the store, or
     * is empty — the r1-m1 create-on-miss dark door). `handle.settled` keeps FIRST-RUN semantics (D4
     * ruling); each resume re-emits a FRESH handle (same `parentToolCallId`) to the spawn sink, whose own
     * `settled` tracks the revived run — a registry keeps the newest handle per id.
     */
    resume?: (content: string) => Promise<string>;
}
/** design/122 r1-m3 — UNPREDICTABLE correlation-marker fragment (uuid random tail, 12 hex chars). A marker
 *  must not be guessable by untrusted content — the old `steer-a<seq>` global counter let injected text
 *  pre-claim "[steer-aN]" and self-declare correlation; the same fix applies to the new resume markers. */
export declare function markerFragment(): string;
export declare function createSubagentResume(deps: {
    ledger: SubagentRetainLedger | undefined;
    parentToolCallId: string;
    runner: RunnerSelfSeat;
    notify?: (n: TaskNotificationPayload, opts?: {
        priority?: "now" | "next" | "later";
    }) => void;
    sink?: (handle: SubagentSteerHandle) => void;
    /** 1.284 C1 — the child gate's liveness source for RESUME cycles (design/144 §1.2: the 207 semantics
     *  apply to EVERY stop cycle, spawn and resume alike). Absent ⇒ honest ungated degradation (no
     *  registry to count against — the pre-C1 immediate send). */
    registry?: import("../core/task-registry.js").TaskRegistry;
    /** design/147 S1a — the RESUMING caller's live injection entry: overrides the retained
     *  snapshot's spawn-turn `parentNotify` (that lane is torn down with its turn — uplinks through it
     *  would PARK instead of reaching the currently active parent turn, behind a success receipt). */
    currentParentNotify?: (n: TaskNotificationPayload, opts?: {
        priority?: "now" | "next" | "later";
    }) => void;
    /** The RESUMING caller's live content-ask face (its trusted `ToolExecuteContext.onQuestion`), same
     *  reasoning as `currentParentNotify` one line up: the retained snapshot deliberately carries NO
     *  question face (the spawn turn's is stripped at freeze time — a per-request lane is torn down with
     *  its turn), so the human who can answer is the one attached to THIS turn. Absent ⇒ the resumed child
     *  has no live face and its `AskUserQuestion` mount falls back to the automatic criterion's other legs
     *  — never a call into a closed surface. */
    currentOnQuestion?: import("../core/ask-question.js").OnQuestion;
    /** The RESUMING caller's own tighten-only CLAMPS (its trusted `ToolExecuteContext` seats), folded ON TOP
     *  of the frozen snapshot so a wake can only ever narrow the child. Without this the snapshot alone
     *  decides: a child spawned by a write-capable, interactive, long-lived run could be woken BY a
     *  read-only / hard-headless / one-shot run and still act with the spawn turn's capabilities — the
     *  resumer driving actions it could not perform itself. The fold is a union of restrictions (either
     *  side saying "restricted" wins), never a relaxation: a snapshot clamp is not lifted by a resumer
     *  that lacks it. Absent ⇒ the snapshot governs alone (prior behavior). The seats are the clamp table's rows
     *  (`ClampValues`: only restricting values), plus the memory-off fact — a rewrite of the `memory` declaration,
     *  not a value seat. */
    currentClamps?: import("../core/child-clamp-seats.js").ClampValues & {
        memoryOff?: true;
    };
    /** The RESUMING caller's own handback-review seat (its trusted `ToolExecuteContext.autoModeReview`),
     *  same turn-bound reasoning as `currentOnQuestion` above: a resumed cycle is a completion like any
     *  other — its work reaches the durable row and the completion notification — and the decider that can
     *  review it belongs to the run doing the waking, not to the frozen spawn snapshot. Absent ⇒ the
     *  resumed cycle's output is carried through unreviewed (honest degradation: a caller with no review
     *  armed had none to apply). */
    currentAutoModeReview?: {
        decider: import("../core/auto-mode.js").AutoModeDecider;
    };
    /** design/176 — present iff this resume is PEER-DRIVEN (the SendMessage carrier): the resume
     *  prompt takes the peer-resume trust frame (teammate header + fence label + the per-message
     *  discipline block — never the operator-authority wording), and the woken cycle's inbound peer
     *  chain seeds from this value (a FRESH ref per cycle — never a mutation of the spawn ref).
     *  Absent = operator resume, byte-for-byte the pre-176 prompt. */
    peerOrigin?: {
        hopChain: string[];
    };
    /** design/176 — the WAKER's peer-identity ref, PAIR-MINTED with {@link currentParentNotify}: the
     *  replaced uplink callback points at the waking conversation, so the admission identity the
     *  woken child's "main" sends are judged against must travel with the same binding. */
    currentParentPeerRef?: PeerSelfRef;
    /** S2b RB-27② — the settled background-agent ROW this resume revives (SendMessage's completed
     *  branch supplies it). With registry+taskId present, the resume cycle re-enters the registry
     *  lifecycle: row flips running, a FRESH injector attaches (cycle-stamped), and this cycle's
     *  settle re-terminalizes the row — so a mid-resume SendMessage gets real running delivery
     *  instead of the still_running refusal. Absent (steer-handle resume path) ⇒ prior behavior. */
    taskId?: string;
    /** S2b RB-27② — the resuming caller's resolved registry access (pairs with taskId). */
    taskAccess?: import("../core/task-registry.js").TaskAccess;
    /** the process-level background-child observer (ctx.onBackgroundChildEvent, Runner-filled).
     *  When present TOGETHER with a revived registry row (taskId + successful revive), the resume cycle
     *  re-emits the SAME spawn→tick→terminal event family as a first spawn — a fleet view's row revives
     *  on the spawn frame (server treats spawn-after-tombstone as row revival). The spawn frame is
     *  emitted after `reviveBackgroundAgent` succeeds and BEFORE the stream starts (spawn precedes every
     *  tick — the observer's tombstone-period tick rejection depends on that order). No revived row ⇒
     *  no frames (the steer-handle resume path has no a*-domain row to project). */
    bgSink?: (event: import("../core/types.js").BackgroundChildEvent) => void;
    /** row lifetime class echoed onto the revive frames (mirrors the registry row). */
    sessionScoped?: boolean;
    /** the row's display description, echoed onto the revive spawn frame (resume-marked). */
    rowDescription?: string;
    /** the row's original observer metadata, reproduced on the revive frames so
     *  a consumer rebuilding a tombstoned row gets its TYPE/name/ancestry back (never a blank row). */
    rowName?: string;
    rowAgentType?: string;
    rowOwner?: string;
    rowScope?: string;
    rowParentTaskId?: string;
    rowParentSessionId?: string;
    rowRootSessionId?: string;
    /** RB-478 — the row's original spawn instant (AccessibleTaskRow.createdAt): the revive spawn
     *  frame's `startedAt` anchors the ROW's birth, never the wake call. */
    rowSpawnedAt?: number;
    /** RB-473 batch 2b — bounded disclosure when a deployment sink throws inside this resume closure's
     *  isolation scope (first failure per site; later ones counted only). */
    onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
}): (content: string) => Promise<string>;
/** Subagent transcript persistence — the RESOLVED delegation entry-caps pair (both members present;
 *  see {@link resolveDelegationEntryCaps}). */
export interface ResolvedDelegationEntryCaps {
    maxConcurrent: number;
    maxCumulativePerSession: number;
}
/**
 * Subagent transcript persistence — resolve + LOUDLY validate `RunnerDeps.delegationEntryCaps`
 * (#123 bad-value law: a garbage knob refuses with a coded error, it is never silently folded to
 * the defaults). Refusal shapes: a member that is not a positive integer (NaN, 0, negative,
 * fractional, non-number), and a RESOLVED pair where `maxConcurrent > maxCumulativePerSession`
 * (you cannot run more agents at once than the tree may ever create — a contradiction, not a
 * preference). Absent members take the exported CC-parity defaults.
 */
export declare function resolveDelegationEntryCaps(caps: {
    maxConcurrent?: number;
    maxCumulativePerSession?: number;
} | undefined): ResolvedDelegationEntryCaps;
/** @internal — observability seam for the lifecycle pins (tests only; never a public surface):
 *  the ledger footprint of one registry. */
export declare function delegationEntryLedgerFootprint(registry: object): {
    keys: number;
    handles: number;
};
/**
 * Options for {@link createSubagentTool}.
 *
 * Hot-update contract: the agent roster (`agents`/`builtinAgents`, including the shadow determination —
 * which deployment definition wins over a built-in of the same name) is a SNAPSHOT taken at
 * `createSubagentTool({...})` time: it is validated, baked into the tool's parameter enum/description,
 * and never re-read. A deployment that hot-reloads its agents directory (or otherwise mutates the
 * catalog) after mounting must REBUILD the ToolSpec — call `createSubagentTool` again with the fresh
 * roster and serve the new tool — for the change to take effect; already-running tasks keep the roster
 * they were prepared with.
 *
 * The MODEL roster ({@link SubagentToolOptions.models}) deliberately does NOT follow that rule when it
 * is left to the runner: a hot-swapped catalog is a first-class runner verb (`Runner.swapDeps`) with
 * no rebuild step, so the tool re-reads it at each judgement instead of requiring a remount. Only the
 * schema/announce projection of it stays a mount-time snapshot (cache-prefix stability). Passing
 * `models` explicitly opts back into snapshot semantics for that table — see the field.
 */
export interface SubagentToolOptions {
    /**
     * Runner used to execute child tasks.
     *
     * **This runner's session store decides where a child TRANSCRIPT lives — and therefore whether one
     * exists at all after the fact.** A SYNC or BACKGROUND child mints its session through
     * `runner.sessions`, never through the parent leg's store, so a deployment that mounts the Agent
     * tool on a DIFFERENT runner than the one serving root tasks has split the transcript lane away
     * from the session lane. (The FORK lane is the deliberate exception: a fork branches the CALLER's
     * session through `ctx.hostSessionFork` — the host Runner's own store — precisely because `runner`
     * may be a split child-execution runner whose store never held the source. A fork child follows the
     * HOST store's fate, an ordinary child this runner's, and one deployment can land one on disk and
     * lose the other.) Three shapes for the ordinary lanes, all reachable and all silent:
     *  - durable store on THIS runner whose `release` PRESERVES history (`evict:"forget"`, the
     *    `FileStorageBackend` default), no {@link SubagentToolOptions.background}.`agentStore` — the
     *    child transcript lands UNPLACED and survives the settle leg's eager release (a cache drop),
     *    so post-hoc forensics work even though nothing can ADDRESS it through an `a*` handle. The
     *    qualifier is load-bearing: durability and release semantics are INDEPENDENT declarations, and
     *    a store declaring itself durable while deleting on release destroys the transcript at settle —
     *    the settle leg calls `release`, and no placement makes the deletion refuse. "Durable" is a
     *    statement about restarts, never about release;
     *  - durable store + `agentStore` — the placed partition (see the mint gate in the background
     *    lane): addressable across a restart, list-excluded, joint-reaped with its row. This arm is
     *    also the one immune to the evict policy above (the settle leg keeps a confirmed row's
     *    transcript instead of releasing it);
     *  - transient/process-local store on THIS runner (e.g. a throwaway sub-task store, or a routing
     *    store that sends ordinary child acquires to a private cache) — the child transcript NEVER
     *    reaches disk whatever the root leg's store is. Nothing in the engine compensates: the
     *    completion notification's recent steps and the in-memory registry are the only readers, and
     *    both die with the process.
     * The asymmetry this produces reads as a bug from the outside: the `run_workflow` tool is mounted by
     * the ENGINE on the run's own runner (prepare-task's `runnerSelf`), so a workflow node's child
     * session always follows the ROOT store, while this tool is mounted by the DEPLOYMENT and follows
     * whatever runner it was handed. One data root can hold every workflow node's transcript and none
     * of the Agent tool's.
     * There is deliberately NO manifest field answering "does a child transcript reach disk here":
     * `WiringManifest.fleet.subagentTranscripts` is the ADDRESSABILITY tier and folds to `"none"` on
     * the absent-row-store arm before it looks at a session store, so it cannot separate the first
     * shape above from the third. What DOES answer it is this runner's store's own pair of
     * declarations — `sessions.durability` and its release/evict policy — read together. Upstream
     * (CC 2.1.250) writes a sub-agent transcript unconditionally to its own file beside the parent
     * session, so "the child conversation is readable afterwards" is never conditional on a store
     * tier there.
     */
    runner: RunnerSelfSeat;
    /**
     * design/115 P3 — background sub-agents (CC `run_in_background`). When set, the tool exposes the
     * `run_in_background` parameter: the call returns an `a*` task_id immediately, the child runs
     * asynchronously, and completion fires ONE task-notification ("next" priority — design/373 §3.7 census
     * re-seat, upstream-anchored: every background-task terminal is explicitly "next" there; the earlier
     * "later doesn't derail active work" reading was the flat era's — a completion must reach a busy model
     * at the boundary, not at the run's closing seat). Poll/stop via TaskOutput/TaskStop. Absent ⇒ synchronous only.
     */
    background?: {
        registry: import("../core/task-registry.js").TaskRegistry;
        /** A-2 (BREAKING 1.365.0): REQUIRED — the registry is default-deny on both axes, so a
         *  background registration without a declared owner would be unreachable by every caller. */
        owner: string;
        /** A-2 (BREAKING 1.365.0): REQUIRED — `"default"` is the single-tenant spelling (explicit,
         *  matching the engine chain's `principal ?? "default"`), never implied by omission. */
        scope: string;
        /**
         * Deployment-level completion-push FALLBACK — not an always-on tap. The settle notification is
         * delivered through `ctx.onTaskNotification ?? background.notify`: inside an engine-driven run
         * the tool-execution context carries the run's own notification injector (the completion lands
         * in the PARENT's live injection queue at its next turn boundary), and this sink is then never
         * called — one completion, one channel, no double-send. It fires only when the tool is executed
         * WITHOUT an engine notification channel (direct `execute()` harnesses, minimal mounts). A
         * deployment that wants an unconditional process-level completion tap should use
         * `RunnerDeps.onBackgroundChildEvent` (the BCE terminal frame) instead.
         */
        notify?: (n: import("../core/task-notification.js").TaskNotificationPayload, opts?: {
            priority?: "now" | "next" | "later";
        }) => void;
        /** design/151 S1a: the durable execution-row ledger. Present ⇒ every background registration
         *  writes a durable row (restart-surviving TaskOutput reads; the S1b lifecycle flip keys off the
         *  same wiring). Absent ⇒ pre-151 in-memory-only lifecycle, unchanged. */
        agentStore?: import("../core/background-agent-store.js").BackgroundAgentStore;
        /** design/153 §7.3 — the park lane's checkpoint store where no host seat rides (a tool invoked outside a Runner task, and the
         *  bare /decide drive of a parked child). Inside a task the child parks in the seat it was HANDED (`ToolExecuteContext.
         *  checkpointStoreForChildren`); an option naming a different store is refused at spawn (`config.invalid_checkpoint_store`).
         *  Absent with no host seat ⇒ a durably-suspending bg child keeps the pre-153 lifecycle. */
        checkpointStore?: import("../core/checkpoint-store.js").CheckpointStore;
        /**
         * design/153 §7.3 (r4 F-09) — the CAPABILITY protocol that attests the child's transcript
         * session is durable before a park commits (replaces any config-boolean form: a wrong `true`
         * would re-mint the stranded-park class this whole design closes). Called park-side with the
         * child's session id; resolving means "this session is now readable via the durable session
         * store from ANY process" (a transient-tier deployment migrates it here — the server's
         * park-前-迁移 hook; an all-durable deployment wires a no-op resolve). A REJECTION (or absence)
         * vetoes the park: the child settles failed exactly as pre-153, and the already-minted
         * checkpoint is expired (no orphans). Never called for sync children or non-suspending runs.
         *
         * ⚠️ Implementation obligation (field-proven): "migrates it here"
         * means ACTIVELY PROMOTE — a check-only implementation that merely LOOKS UP the host durable
         * store rejects every child whose session lives in a split/transient tier (a subRunner's
         * private TTL store), and the veto fires on EVERY park: the checkpoint expires within
         * milliseconds of minting and the redemption window never exists. The hook must cover the
         * store the child session ACTUALLY lives in — copy the session tree into the durable store,
         * then re-verify, and only then resolve.
         */
        ensureChildSessionDurable?: (sessionId: string) => Promise<void>;
    };
    /** Model the child runs on when neither the call nor the agent definition names one (the tool-level explicit seat).
     *  Omitted too ⇒ the deployment's stated `roles.subagent` tier, else the parent run's effective model (`childModelSeat`). */
    model?: ModelRef;
    /**
     * Declarative sub-agents the model can pick from (design/38 1B). When set, the tool exposes an `agent`
     * parameter (enum of these names) and the chosen definition's config builds the child task — its
     * `allowTools`/`denyTools` REPLACE the tool-level ones, `model` omitted = tool-level `model` > stated `roles.subagent` tier > the caller's model (`childModelSeat`).
     * design/141 件A: when OMITTED, defaults to the runner's deployment catalog (`RunnerDeps.agents`) —
     * one Runner, one registry across the Agent-tool and workflow lanes. Pass explicitly to narrow.
     */
    agents?: AgentDefinition[];
    /**
     * F1 (CC 2.1.198 parity): offer the BUILT-IN read-only `Explore`/`Plan` agent types (CC `HCe`
     * registry, GA default-on — pretty.js:487221-487240 + iAt :487127). Default `true` (CC posture);
     * `false` removes them (the config-driven analog of CC's `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS`).
     * A deployment definition named "Explore"/"Plan" SHADOWS the built-in of the same name (definition
     * wins — fork-shadowing precedent).
     */
    builtinAgents?: boolean;
    /**
     * Model catalog the tool validates model words against — an agent definition's string `model` ref at
     * assembly time (fail-fast), and the per-call `model` argument at call time.
     *
     * OMITTED is the normal deployment shape: the tool then reads the runner's CURRENT catalog
     * (`Runner.agentCatalog.models`) at each of those moments, so a catalog hot-swapped after mount is
     * the one a delegation is judged against. Passing this explicitly is a deliberate NARROWING and
     * pins the tool to exactly this table for the tool's whole life — the runner's generation changes
     * no longer reach it. And this table is AUTHORITATIVE for the per-call word, not merely advisory:
     * a word judged against it leaves the gate as this table's resolved `Model` object and the child
     * runs that binding — including a key the runner's own catalog does not carry, and including a
     * same-name key the runner binds differently. (The judged-then-re-resolved split this replaced
     * admitted such words and then killed the child inside its own prepare, or ran a binding the spawn
     * frame never named.) A definition's string `model` ref is different: it is validated here at
     * assembly time but still resolves at the child's own prepare through the runner's live table.
     */
    models?: Record<string, Model>;
    /** Tools available to the child: the deployment's EXPLICIT pool, used verbatim — it replaces {@link inheritedTools}
     *  and is the one channel that hands a child a tool its parent does not mount. `[]` = the child inherits nothing. */
    tools?: ToolSpec[];
    /**
     * The parent surface a child inherits when {@link tools} is absent (`assembleCodeTools` passes its bundle's own
     * instances, so the task-list family shares the leader's store). Narrowed per spawn by `inheritedChildPool`: only
     * the instances the spawning run mounts (`ToolExecuteContext.mountsCallerTool`; a host's copy of an entry is another
     * instance and is not inherited), no task-list family for an anonymous background child, and a same-named `extraTools`
     * product replaces the entry. Model-gate stamps travel with the entries; each child's prepare re-judges them.
     */
    inheritedTools?: ToolSpec[];
    /**
     * Tool-level allowlist (design/38 1A), used when NO `agent` is selected (a selected agent definition's
     * own `allowTools`/`denyTools` REPLACE these). When set, the child may use only what it names, out of everything its run would
     * mount (its pool, the hands, the engine's own tools, the MCP tools it inherits) — the child's own selector layer, under the
     * spawning run's selector (`["*"]` = no allowlist of its own). Omit for everything the parent could use. Allowlist is the
     * security-recommended shape. The nested delegation tool is one of the tools it governs: a child gets one only while under
     * `maxDepth` AND when these lists admit the delegation tool's name.
     */
    allowTools?: string[];
    /** Tool-level denylist (design/38 1A), used when no `agent` is selected: the denied half of the child's own selector layer (a deny wins). */
    denyTools?: string[];
    /** System prompt / persona for the child. Default (1.259.0, CC 198 parity): the LEAN
     *  {@link SUBAGENT_PROMPT} (agent persona + return contract), NOT the full default constitution role
     *  base — pass `DEFAULT_SYSTEM_PROMPT` here explicitly to restore the pre-1.259 behavior. */
    systemPrompt?: string;
    /** Tool name exposed to the parent. Default "Agent". */
    name?: string;
    /** What the child is for (shown to the parent in the tool description). */
    purpose?: string;
    /** Child run limits — forwarded verbatim onto each child's `TaskSpec.limits` (design/164 shape). */
    limits?: TaskLimits;
    /**
     * Max nesting depth of delegation. A child at depth `d` only receives a delegation tool when
     * `d + 1 < maxDepth`, so it cannot recurse past the limit.
     *
     * Default 3 (RB-292) — CC 2.1.220 parity: `bee()` @272064 returns `CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH`
     * when set, else a remote-config value, else the constant `aHu = 3` @272076 (cli250.js:37597). The old default of 5 was
     * anchored on CC 2.1.201's `Y4t` and upstream has since moved; 5 is a stale anchor, not a sema choice.
     * sema exposes the override as THIS option rather than an env var (deployments configure the tool, not
     * the process), so the env/remote-config half of `bee()` has no sema counterpart to keep in sync.
     *
     * Semantics pin (off-by-one checked): with the default, the main agent + children at depths 1-2 all
     * carry a delegation tool, the depth-3 child is the STRUCTURAL leaf (no delegation tool mounted —
     * sema's structural de-tooling, not a runtime throw), so "default behavior = CC default behavior":
     * 3 delegation levels under main are reachable and a 4th is impossible by construction.
     */
    maxDepth?: number;
    /**
     * Failure-evidence retain (default ON). A non-completed SYNC child (failed / timeout / blocked) used
     * to have its session released as eagerly as a successful one — for a tool-heavy child whose terminal
     * text is empty, that destroyed the only copy of its intermediate conclusions with zero retrieval
     * path. With this on, a non-completed sync child SKIPS the eager release and its session is briefly
     * retained (ledger-backed when available, timer-backed fallback; ~15 min), and the failure report
     * carries the transcript handle + a retention hint. `false` restores unconditional eager release.
     * Completed children are unaffected (still released eagerly).
     */
    retainFailedSessions?: boolean;
    /**
     * Observer-failure sink: called when an ARMED observer pairing dies (spawn/delivery failure, or an
     * unresponsive observer at the settle drain). Best-effort observability — a throwing sink is
     * swallowed. Regardless of this sink, the observed child's final report carries an
     * `observer: failed (…)` line so the degrade is never silent.
     */
    onObserverError?: (err: unknown, info: {
        observedAgent?: string;
        observerAgent?: string;
        site?: string;
    }) => void;
    /**
     * Dynamic tool-injection seam — evaluated ONCE PER SPAWN, with THIS delegation
     * call's parent coordinates, to produce tool instances that cannot sit in the mount-time static
     * `tools` pool (the deployment case: per-task tools issued by a task-env registry — the static pool
     * is fixed at `createSubagentTool({...})` time, before any task exists). The products are MERGED
     * INTO the `tools` pool and then flow through the EXACT SAME subset reading as the
     * static tools — an agent definition's `allowTools`/`denyTools` (or the tool-level ones when no
     * agent is selected) govern them verbatim: an explicit allowlist must NAME an injected tool for the
     * child to see it; `"*"`/absent allowlist = all. Without {@link extraToolNames} the pool cannot be listed before a spawn.
     * Nested delegation threads the SAME factory down
     * (`createSubagentToolNode(opts, depth+1)` carries it), so a grandchild spawn re-evaluates it — per-spawn,
     * at every level; each child gets its own instances bound to its own spawn moment (the
     * server-side factory is execute-time late-bound, so per-spawn evaluation is the confirmed shape).
     * A THROWING/rejecting factory degrades THAT spawn to zero injected tools (the child still runs)
     * with a FIXED generic `note:` disclosure on the report/async card (same lane as the model-override
     * notes; the ORIGINAL error goes to {@link onExtraToolsError} only — 快审 S3). On a NAME COLLISION
     * with the static pool the STATIC tool wins and the injected entry is skipped — mount-time
     * configuration outranks runtime injection; the reserved surface covers the static tools' declared
     * `aliases` too (快审 S2). Factory evaluations are capped per root spawn tree
     * ({@link EXTRA_TOOLS_MAX_FACTORY_CALLS_PER_TREE} — 快审 S4). Absent ⇒ byte-identical prior behavior.
     *
     * ⚠️ NOT a supported SPAWN seam (parent effective-policy inheritance): an injected tool that itself
     * calls `opts.runner.runTask(...)` internally is a SELF-MADE spawn OUTSIDE the delegation chain — it
     * does not (and will not) thread `RunInternals.inheritedGate`, so such a child does NOT inherit the
     * parent's evaluated policy chain. The supported spawn paths are the delegation tool itself, the
     * workflow `agent()`/`agentStream()` legs, and the `run_workflow` tool. The backstop for a self-made
     * spawn is the child task's OWN gate (deps/spec policy + its own session rules) — inheritance is a
     * tightening layer on the supported paths, never the only line of defense.
     */
    extraTools?: (ctx: SubagentSpawnContext) => ToolSpec[] | Promise<ToolSpec[]>;
    /**
     * The roster of {@link extraTools} — every tool it may inject: names, aliases, content class (absent = external). The declared
     * tools join the pool, so the pool is listable before a spawn; a product outside the roster refuses that spawn
     * (`config.extra_tool_names`, `detail.reason` `undeclared_name` / `undeclared_origin`, also to {@link onExtraToolsError});
     * a malformed roster, or one with no factory, throws where the tool is built (`shape`). Absent ⇒ as before. See extra-tool-names.ts.
     */
    extraToolNames?: ReadonlyArray<{
        name: string;
        aliases?: readonly string[];
        contentOrigin?: import("../core/types.js").ToolContentOrigin;
    }>;
    /**
     * extraTools 快审 S3 — host-side sink for the ORIGINAL `extraTools` factory error (and the S4
     * budget-exceeded signal). The model-facing report deliberately carries only the fixed
     * {@link EXTRA_TOOLS_FAILED_NOTE}: a registry/config error can embed paths/hostnames/credentials, and
     * the report lands in the model context AND the persisted transcript (four disclosure faces). Sink
     * choice rationale: this file's existing deployment-facing failure-sink posture is
     * {@link onObserverError} (best-effort, swallow-guarded, opts-level) — this mirrors it rather than
     * inventing a new trace channel. A throwing sink is swallowed; absent ⇒ the original error is dropped
     * after the generic disclosure.
     */
    onExtraToolsError?: (err: unknown, info: {
        toolCallId: string;
        agentType?: string;
        depth: number;
    }) => void;
}
/**
 * Read-only parent-spawn coordinates handed to {@link SubagentToolOptions.extraTools}. Deliberately a
 * NARROW projection of what the delegation tool's own `ToolExecuteContext` already carries (no new
 * information channel; the trusted sinks — retain ledger, worktree lane, notification sinks — stay
 * out of the factory's reach).
 */
export interface SubagentSpawnContext {
    /** THIS delegation tool-call's id — the spawn moment's stable coordinate. */
    toolCallId: string;
    /** The PARENT run's stable taskId (`spec.taskId ?? sessionId`, Runner-filled). Absent outside a Runner. */
    parentTaskId?: string;
    /** The PARENT run's session id (Runner-filled). Absent outside a Runner. */
    parentSessionId?: string;
    /** The parent task's authenticated end-user principal (design/62), inherited read-only. */
    principal?: string;
    /** The chosen subagent type for THIS spawn (agent-definition name, or "fork"); absent for a plain delegation. */
    agentType?: string;
    /** Delegation depth of the SPAWNING tool (0 = the main agent's Agent tool; a grandchild spawn sees 1, …). */
    depth: number;
}
/**
 * "general-purpose" alias fold (CC 2.1.207 parity — see {@link GENERAL_PURPOSE_SUBAGENT_TYPE}): rewrite
 * `subagent_type: "general-purpose"` to the OMITTED form so it takes the plain clean-context delegation
 * path (sema's built-in general-purpose equivalent: full tool pool + SUBAGENT_PROMPT persona). Wired both
 * as the tool's `prepareArguments` pre-validation shim (the declared `subagent_type` enum lists only
 * DEFINED agent names, so the literal must rewrite BEFORE the schema check) AND inside execute (direct
 * ToolSpec.execute callers). Idempotent; identity when `shadowed` (a deployment/builtin definition of the
 * same name wins — the literal then resolves as a normal agent lookup, never this fold). This is a LIVE
 * cross-vocabulary mapping, not a compatibility arm (RB-479-A③ retired the Fork `{directive}` adapter
 * that used to share this wiring).
 */
/**
 * RB-293 — subagent_type fold, CC 2.1.220 `ulr` @317317 verbatim:
 * `e.normalize("NFKC").toLowerCase().replace(/[\p{White_Space}\p{Pd}_]+/gu, "")`.
 *
 * Deliberately lossy and deliberately NOT a resolver: it exists only to decide whether two spellings
 * denote the same roster entry AFTER an exact match has already failed. `\p{Pd}` is the dash punctuation
 * class (so an en-dash paste folds like a hyphen), `\p{White_Space}` covers NBSP and friends, and NFKC
 * folds fullwidth/compatibility forms — the three ways a name gets mangled between a listing line and a
 * tool call. Note it can map DISTINCT roster names onto one key ("code-review" / "code_review"), which
 * is exactly why the caller must treat a multi-hit as ambiguous rather than picking a winner.
 */
export declare function normalizeSubagentType(value: string): string;
/**
 * A system-prompt note you can append to the PARENT task so the model knows delegation exists.
 * (The tool description below is the primary "explicit instruction"; this is an optional reinforcement.)
 */
export declare const SUBAGENT_SYSTEM_NOTE: string;
/**
 * G4 (CC 2.1.198 parity — 锚 pretty.js:225024 `Z4t` fork-boilerplate): the MESSAGE-LAYER instruction
 * frame prepended to a fork child's directive. 198 keeps the fork's SYSTEM prompt byte-identical to the
 * parent (prompt-cache) and carries the fork behavior contract in the messages instead — same posture
 * here: this frame wraps the `prompt` (the child's objective/user turn) and never touches systemPrompt.
 * 198-faithful verbatim (2026-07-10 realignment): the no-spawn line is now the full 198 text — the tool
 * name matches (198 `is`="Agent" = our DEFAULT_SUBAGENT_TOOL_NAME) and our tool card carries its own
 * fork guidance (:939) for the referent. An earlier adaptation dropped the "default to forking" clause
 * as CC-specific; a live identity-confusion incident (a fork reading the parent's delegation record as
 * "someone else is on it" and stopping) showed the parent/fork disambiguation clause is load-bearing —
 * keep the anchor whole. (NOT the 88-era frame — 88's "STOP. READ THIS
 * FIRST… Scope:/Result/Key files" format was rewritten shorter and softer in 198; parity follows 198.)
 * Frame TAIL = the literal directive prefix `Your directive: ` (198 `ZDt`, pretty.js:35621; the Z4t
 * assembly is `…</fork-boilerplate>\n\n${ZDt}${directive}`, :225038, and 198 strips the SAME prefix on
 * read-back, :415673). 1.259.0 合车复审修①: an earlier transcription invented a `<directive-prefix>`
 * tag here — that tag does not exist in 198; the anchor is the plain-text prefix.
 */
export declare const FORK_DIRECTIVE_FRAME = "<fork-boilerplate>\nYou are a worker fork. The transcript above is the parent's history \u2014 inherited reference, not your situation. You are NOT a continuation of that agent. Execute ONE directive, then stop.\n\nHard rules:\n- Do NOT spawn subagents with the Agent tool. The \"default to forking\" guidance is for the parent; you ARE the fork, execute directly.\n- One shot: report once and stop. No follow-up questions, no proposed next steps, no waiting for the user.\n\nGuidelines (your directive may override any of these):\n- Stay in scope. Other forks may be handling adjacent work; if you spot something outside your directive, note it in a sentence and move on.\n- Open with one line restating your task, so the parent can spot scope drift at a glance.\n- Be concise \u2014 as short as the answer allows, no shorter. Plain text, no preamble, no meta-commentary.\n- If you committed changes, list the paths and commit hashes in your report.\n</fork-boilerplate>\n\nYour directive: ";
/**
 * RB-295 (CC 2.1.220 `Kus` @273133 — verbatim modulo the two interpolations): the path-translation note
 * a FORK gets when it also runs under `isolation: "worktree"`. Upstream pushes it as its own message
 * appended AFTER the fork's directive messages (@466667: `if (L && Le) de.push(zr({content: Kus(…)}))`)
 * — it exists precisely because a fork INHERITS the parent's transcript, and every absolute path in that
 * transcript points at the parent's working copy while the fork's tools now resolve inside the worktree.
 * A non-fork worktree child has no inherited paths and gets no note, exactly as upstream.
 *
 * `parentCwd` is unknown on mounts that never threaded a parent working root; the sentence then names
 * the relationship instead of a path (the translation instruction is what carries the value, not the
 * literal directory), which is the only place this diverges from the anchor.
 */
export declare function forkWorktreeTranslationNote(parentCwd: string | undefined, worktreeDir: string): string;
/**
 * RB-291 — the `async_launched` tool-result card, rebuilt from the CC 2.1.220 anchor (@467305-467320)
 * and shared by both background lanes (Agent bg + fork bg) so the two copies cannot drift apart again.
 * Three drifts were repaired against the anchor:
 *  ① the closing instruction had inverted the anchor's meaning — sema said "end your response. Do not
 *     generate any other text", CC says "continue other work or respond to the user in the meantime".
 *     CC parks the model on the RESULTS (do not report/assume/predict), never on the turn itself; the
 *     old text told a model with unrelated work queued to stop working, which is a real capability loss;
 *  ② the internal-ID guard was absent — CC brackets both the whole result ("internal metadata — never
 *     quote or paste any part of it … into a user-facing reply") and the id line itself;
 *  ③ the SendMessage continuation pointer was absent — CC teaches the exact follow-up call, with the
 *     summary shape, right on the card.
 * Registered sema-side deltas that STAY: the id is the unified `task_id` (CC: `agentId`); sema has no
 * agent output file, so the anchor's no-outputFile variant is the one reproduced; `notify: false` is
 * sema's honest degrade arm (CC always has a notification lane); the caller's `notes` are sema's own
 * per-call truths (model override, injected tools, worktree, task- vs session-scoped lifetime).
 */
export declare function asyncLaunchedReceipt(p: {
    taskId: string;
    /** The lane's own "what is running" opener — the Agent lane and the fork lane say different things. */
    workingLine: string;
    /** Whether a real notification sink is wired (the receipt speaks the RUNTIME sink truth). */
    notify: boolean;
    /**
     * RB-220 — mirrors {@link import("../core/types.js").ToolExecuteContext.oneShot}: this run has no
     * later turn for an async background notification to land in (a headless `-p` process exits once
     * the turn ends). Checked AHEAD of `notify` below — a one-shot submission is wrong to promise a
     * notification regardless of whether a notify sink happens to be wired, the same failure form
     * `run_workflow`'s launch note was fixed for (RB-215/run-workflow-tool.ts): a model that follows
     * "end your turn and wait" advice in a one-shot process loses the result — the process exits with
     * nothing left alive to receive it. Default undefined ⇒ byte-compat (pre-RB-220 wording).
     */
    oneShot?: boolean;
    /** Per-call honest notes, rendered one per line between the status line and the closing instruction. */
    notes?: (string | undefined)[];
}): string;
/**
 * Build a tool that lets a parent task delegate a subtask to an **isolated** sub-agent run.
 *
 * The child runs synchronously in a fresh session (isolated context) with a narrowed tool set and
 * its own model, and returns a structured, machine-readable handoff (status / result / blockedReason / stats).
 */
export declare function createSubagentTool(opts: SubagentToolOptions): ToolSpec;
/**
 * Render an agent definition's tool boundary for the roster listing line tail — CC `gHm` semantics
 * (pretty.js:453014-453027): explicit allowlist ⇒ the list (minus denies; empty ⇒ "None"); only denies ⇒
 * "All tools except …"; neither ⇒ "All tools". sema delta: `allowTools: ["*"]` is the documented
 * allow-everything sentinel (AgentDefinition.allowTools) — treated as NO allowlist, not a literal list.
 *
 * deliberate: this renders the AUTHORED list raw — upstream's listing does too (gHm reads the
 * definition; the unknown-item split happens later, at spawn, in its resolveAgentTools). It is the
 * DECLARED boundary, NOT the effective child roster: an unknown entry may appear here while the
 * spawn-time filter (resolveToolSubset — the authority) drops it, and an alias-form divergence can
 * show a name the deny leg removes at spawn (allow:["HttpGet"] + deny:["Fetch"] over a Fetch/HttpGet
 * aliased tool displays HttpGet yet mounts nothing). Rendering a mount-time "resolved" face instead
 * would both diverge from the anchor and lie the other way (per-spawn extraTools products are not
 * knowable here).
 */
export declare function agentToolsNote(def: {
    allowTools?: string[];
    denyTools?: string[];
}): string;
/**
 * F4 (CC 2.1.198 `tIl` selection half — 锚 pretty.js:453029 `(t && e.whenToUseLean) || e.whenToUse`,
 * `t = yg(mainLoopModel)` :479504): pick the roster guidance text for one agent definition. CC keys
 * `lean` on its simple-system-prompt model predicate; sema runs the CC lean-prompt arm wholesale
 * (provider-neutral engine, web.ts lean-card precedent), so every call site passes the default
 * `lean = true` — a definition WITH `whenToUseLean` shows the lean text (exactly what modern CC main
 * models see), one without is unchanged. Empty-string lean falls back like CC (`||`, not `??`).
 */
export declare function agentWhenToUseText(def: {
    whenToUse?: string;
    whenToUseLean?: string;
}, lean?: boolean): string | undefined;