UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

397 lines (396 loc) • 29.6 kB
/** * design/98 §3.1 (S8c) — the `run_workflow` TOOL an LLM calls to author + run its own workflow. Mounted ONLY * when self-orchestration is active (design/98 §1.2: opt-in + a HARD `WorkflowScriptRunner` + a governance * baseline). Params are ONLY `{ script, args }` — it NEVER accepts `depth` (the nesting depth is a trusted * internal channel, design/98 §0.1). `effect: "write"` (spawning agents is an irreversible side effect → it * passes the design/37 policy gate; each spawned agent then passes its OWN governed gate). * * Flow: size-cap the script → statically validate `meta` (no eval) → `startWorkflow` with the GOVERNED * primitives + hard caps → return `{ runId }` IMMEDIATELY → on completion, notify the originator via the * {@link WorkflowCompletionNotifier} seam. */ import type { AgentTool } from "../internal/harness-types.js"; import type { Model } from "../internal/llm.js"; import type { RunnerSelfSeat } from "../core/runner/contracts.js"; import type { WorkflowGovernanceBaseline } from "../core/types.js"; import type { WorkflowRunStore } from "../core/workflow-run-store.js"; import type { WorkflowJournalStore } from "../core/workflow-journal-store.js"; import type { TaskRegistry } from "../core/task-registry.js"; import type { TaskNotificationPayload } from "../core/task-notification.js"; import type { WorkflowAgentHandle } from "./workflow.js"; import type { WorkflowScriptRunner } from "./workflow-script-runner.js"; import { type NamedWorkflowListing, type WorkflowScriptStore } from "./workflow-script-store.js"; import { type WorkflowSizeGuideline } from "./workflow-size-guideline.js"; /** Reserved name of the injected self-orchestration tool. Audit 2026-07-07: CC's primary * name is `Workflow` with `RunWorkflow` as the alias — CC-trained prompts address "Workflow", so the * primary flipped to match. #181-F2 订正: since RB-476-A retired alias resolution, `RunWorkflow`/ * `run_workflow` are NOT callable — they live in RETIRED_TOOL_NAMES as validation data only (a call * under either gets a loud roster miss), so every model-visible mention must use THIS constant. */ export declare const RUN_WORKFLOW_TOOL_NAME = "Workflow"; /** * design/98 §D.5 — the seam the deployment implements to RE-INVOKE the originating LLM when its workflow * finishes. Core calls `notify` once per run (at most once — `runId` dedup) with a REDACTED, BOUNDED summary; * the service routes it back through its task-notification path (cross-replica via the WorkflowRunStore). */ export interface WorkflowCompletionNotifier { notify(input: { runId: string; task_id?: string; task_type?: "workflow"; toolUseId?: string; sourceTaskId?: string; /** The ORIGINATING session id (the run that spawned this workflow), threaded from the * Runner so a deployment's completion inbox resolves the target session lookup-free. */ originatingSessionId?: string; principal?: string; status: "completed" | "failed"; /** Redacted + length-bounded — never the host's internal paths/tokens. */ summary: string; result?: string; usage?: unknown; /** The diagnosis route (per-agent rows / journal locator / resumeFromRunId guidance). * Added 1.353 on the payload but silently dropped at this hand-written subset seam until 1.404 — * keep this interface in lockstep with the payload fields the settle legs mint. */ diagnostics?: string; /** See {@link "../core/task-notification.js".TaskNotificationPayload.completionId}. * This CROSS-REPLICA seam is exactly one of the read faces the cross-channel correlation contract * promises the same value on — added here in the same car specifically so this interface does not * repeat the diagnostics field's own drift history (the comment right above this one). */ completionId?: string; }): Promise<void> | void; /** * Poll-then-also-notify dedup: the ORIGINATING session was served this run's TERMINAL * state IN-BAND (a `TaskOutput` poll through the task registry returned a non-running snapshot) — * the deployment should ack/drop any still-pending completion-inbox entry for (originatingSessionId, * runId), so a later stream-open doesn't re-deliver a `workflow_complete` the model already * consumed. Fired at most once per run, AFTER `notify` may already have enqueued (the whole point: * the poll usually wins the race in-turn). Optional; errors are swallowed. The shell-side same-process * seed dedup remains the belt — this is the durable/cross-restart half. */ ackServed?(input: { runId: string; originatingSessionId?: string; sourceTaskId?: string; principal?: string; }): Promise<void> | void; } /** design/98 §D.6 — the deployment's hard CEILINGS for an LLM-authored workflow (the tool forces them; a * script may only tighten). All optional; sensible defaults bound a runaway script. */ export interface WorkflowLimits { /** Max script source length (chars). Default 100_000. */ maxScriptChars?: number; /** Max cumulative agents. Default 50. */ maxAgents?: number; /** Whole-workflow wall-clock cap (ms). Default 600_000 (10 min). */ totalTimeoutMs?: number; /** design/164 — per-child wall-clock ceiling (ms). No default: unset means the children are not * wall-clock bounded (state a ceiling to get one). */ perAgentMaxWalltimeMs?: number; /** Per-child cost ceiling (USD). Forced onto every child. */ childMaxCostUsd?: number; /** Per-child token ceiling. Forced onto every child. */ childMaxTokens?: number; /** Per-child turn ceiling. */ childMaxTurns?: number; /** Workflow token budget (`ctx.agent` throws once reached). */ budget?: number; /** Max returned-result size (chars). Default 100_000. */ maxResultChars?: number; /** Max single log-line length (chars). Default 10_000. */ maxLogChars?: number; /** * CC 2.1.202+ config key `workflowSizeGuideline`: ADVISORY workflow size guidance * (NOT a cap — the hard ceiling stays `maxAgents`). small/medium/large append CC's verbatim * "keep workflows under N agents … guideline, not a hard limit" section to the Workflow tool card * (matching observed CC behavior); an EXPLICIT "unrestricted" injects nothing, while unset resolves * to the medium DEFAULT arm (RB-339 rebaseline — see workflow-size-guideline.ts). * Lives on the limits object so it rides the EXISTING `RunnerDeps.workflowLimits` threading — * deployments configure it as `workflowLimits: { sizeGuideline }` with no new prepare-task seam. * `RunWorkflowToolDeps.sizeGuideline` (direct construction) takes precedence when both are set. */ sizeGuideline?: WorkflowSizeGuideline; } /** * design/140 §6 1b — pick the LLM-facing guidance text for one named workflow: `whenToUse` when declared, * else the description (the agents-side `agentWhenToUseText`/CC `tIl` selection shape, workflows arm — * workflows have no lean variant, so the fallback chain is just whenToUse → description). */ export declare function workflowWhenToUseText(m: { description?: string; whenToUse?: string; }): string | undefined; /** * design/140 §6 1b — render the named-workflow roster block appended to the Workflow tool card (the * consumption face `meta.whenToUse` previously lacked). Line shape mirrors the agent roster (CC `tIl`, * subagent.ts precedent): `- name: whenToUse-or-description`. Returns undefined when nothing is registered * (the card stays byte-identical to the pre-140 form). */ export declare function renderNamedWorkflowListing(entries: ReadonlyArray<NamedWorkflowListing>): string | undefined; export interface RunWorkflowToolDeps { /** The Runner that executes child tasks (the workflow's `ctx.agent` → `runner.runTask`). */ runner: RunnerSelfSeat; /** The HARD sandbox (asserted `safeForUntrustedScripts:true`). */ scriptRunner: WorkflowScriptRunner; /** Deployment-trusted governance every spawned agent inherits (design/98 §2.5). */ governanceBaseline: WorkflowGovernanceBaseline; /** (tool-face round 5): the HOST task's tool-face controls (prepare-time snapshot) — a * scenario roster must survive the workflow delegation path exactly like the Agent path. Merged * (union — tighten-only) into the baseline base for every child spec this workflow spawns. */ parentExcludeTools?: readonly string[]; parentDeferTools?: readonly string[]; /** DD-3 (RB-422): the defer face's subtract valve rides the same snapshot — a pin the operator * declared for the tree must survive this direct-mount lane exactly like the deferral it exempts * from. Union (widen-the-exemption never tightens the child beyond the parent's own face). */ parentAlwaysLoadTools?: readonly string[]; /** design/277: the HOST task's model-gate restore selector * ({@link import("../core/types.js").TaskSpec.restoreGatedTools}, prepare-time frozen snapshot) — * the workflow lane is a delegation lane too, so the selector inherits here exactly like the * three tool-face controls above (union with the baseline's own; `true` on either side wins). * Without this seat, a restored parent's workflow child on the same strong model lost the * scaffold nobody chose to drop — the exact tree inconsistency design/277 §3.4 forbids. Only a * loosening toward tools the DEPLOYMENT's own baseline composes (the script cannot name this * field; excludeTools still wins downstream). */ parentRestoreGatedTools?: readonly string[] | true; /** R2 双形轴 — the HOST task's resolved prompt profile, inherited by every workflow-spawned child * (base spec seat; the child's own explicit promptProfile would win in prepare, but workflow * scripts cannot name this field, so in practice the tree speaks the host's profile). */ parentPromptProfile?: "simple" | "classic"; /** Model catalog (for the name→Model allowlist resolution). */ models?: Record<string, Model>; /** F4 agentType registry: deployment agent definitions `agent(…, {agentType})` resolves (SHADOW over * built-in Explore/Plan; `builtinAgents:false` removes the built-ins). Same registry the Agent tool uses. */ agents?: import("../core/types.js").AgentDefinition[]; builtinAgents?: boolean; /** Optional run store for cross-replica `/workflows` observability (opt-in). */ store?: WorkflowRunStore; /** design/97 CORE-9 (Part A) — optional LOAD-BEARING resume journal: records each agent result so a * `resumeFromRunId` re-run replays the unchanged prefix (durable cross-replica resume). Opt-in. */ journalStore?: WorkflowJournalStore; /** design/149: the process-level BackgroundChildEvent sink — workflow-spawned agents * mint synthetic `wa*` rows (spawn/tick/terminal) on the SAME observer bg subagents use * (RunnerDeps.onBackgroundChildEvent), immune to any run-leg forwarding freeze. Opt-in. */ onBackgroundChildEvent?: (event: import("../core/types.js").BackgroundChildEvent) => void; /** design/97 CORE-9 (Part B) — optional steerable-handle sink. When set, the script's `agent()` primitive runs * each agent STEERABLE and emits its {@link WorkflowAgentHandle} here (the script never sees the handle); the * deployment registers it by runId+label to route a human/cross-replica steer. Unset ⇒ non-steerable. Opt-in. */ onAgentSpawn?: (handle: WorkflowAgentHandle) => void; /** Tenant/grouping key for the run. */ scope?: string; /** Completion re-invoke seam. */ notifier?: WorkflowCompletionNotifier; /** The originating session id, threaded into every completion notify. */ originatingSessionId?: string; /** The ROOT host session of the delegation tree (prepare-task passes * `internals?.rootSessionId ?? sessionId` per task). The AUTO-MOUNTED tool's execute ctx is the * harness-native one (no internals enrichment — that wrapper covers spec.tools only), so the * root MUST ride deps; `ctx.rootSessionId` stays first for a deployment-composed mount that * does get the enriched ctx. */ rootSessionId?: string; /** design/380 O1② (C12) — the host run's EXPLICIT placement fixed point * (`RunInternals.placementRoot`), riding deps for the same auto-mounted-ctx reason as * `rootSessionId` above; threaded into the workflow run so every workflow-spawned agent keeps * the ladder/gate placement. Absent ⇒ nothing extra travels (the rootSessionId chain is the * placement root through prepare's mint middle segment). */ placementRoot?: string; /** Process-local unified task registry. When present, RunWorkflow returns `task_id === runId` with a `w*` id. */ taskRegistry?: TaskRegistry; /** design/115 P2 core slice: run-local task-notification sink for SDK event + live model XML injection. * design/373 §3.7: the sink accepts the injection tier — workflow terminals declare `"next"` * explicitly (a completion must reach a busy model at the boundary; the parameterless funnel * default is the EXTERNAL lane's `"later"` and no internal lane may lean on it). */ taskNotification?: (notification: TaskNotificationPayload, opts?: { priority?: "now" | "next" | "later"; }) => void; /** Runner-owned owner fallback for registry access when the execute context is unavailable. */ taskOwner?: string; /** Hard ceilings. */ limits?: WorkflowLimits; /** B5/F2 (CC scriptPath/name surface): optional script-persistence seam. When wired, EVERY invocation's * resolved script is persisted (best-effort) and its path returned in the tool result; `scriptPath` * re-runs a persisted file and `name` resolves a saved workflow. Absent ⇒ inline `script` only. */ scriptStore?: WorkflowScriptStore; /** design/140 §6 1c — `false` removes the BUILT-IN named workflows (`discussion`, …) wholesale (the * `builtinAgents:false` analog). Default ON: `{name}` resolves a built-in even with NO deployment script * store; a deployment `scriptStore.resolveName` hit for the same name always SHADOWS the built-in. */ builtinWorkflows?: boolean; /** CC `workflowSizeGuideline` — advisory size guideline appended to the tool card. * Direct-construction face; wins over `limits.sizeGuideline` (the RunnerDeps-reachable channel). * See {@link WorkflowLimits.sizeGuideline}. */ sizeGuideline?: WorkflowSizeGuideline; /** The originating task id + principal, threaded to the notifier. */ sourceTaskId?: string; /** The HOST run's parent SPEC seat ({@link import("../core/parent-spec-seat.js").ParentSpecSeat}: principal / oneShot / * clientContext / the frozen question face / the fs-fence widening / envFacts / the declared persistence statement), * minted once by the host's prepare and threaded as a MOUNT dep because this auto-mount's execute ctx is minimal * (`ctx.parentSpecSeat` wins when a wrapping path provides it). Applied onto every spawned agent at the launch site * (`RunWorkflowOptions.parentSpecSeat` → `applyParentSpecSeat`, the Task lane's one law); the tool also reads its * `principal` for the registry/notify scope and its `oneShot` for the launch receipt's guidance branch (a one-shot * caller has no later turn for a notification to land in, so "end your turn and wait" is wrong for it). */ parentSpecSeat?: import("../core/parent-spec-seat.js").ParentSpecSeat; /** The HOST run's own per-request question face was already stripped by the engine (`RunInternals.questionFaceStripped` * of the host leg): the fact inherits onto every agent this workflow spawns (`ctx.questionFaceStripped` wins on a rich-ctx * mount; the dep covers the auto-mount), beside the strip this tool performs on the seat itself. */ parentQuestionFaceStripped?: true; /** design/173 §8.3 (r3-F1) — the HOST run's RESOLVED interaction posture, threaded as a MOUNT * dep because prepare-task's auto-mount seals this tool without the rich-ctx wrapper (the same * reason `forwardEvent`/`inheritedGateForChildren` ride deps); `ctx.interactionPosture` still * wins when a wrapping path provides it. */ parentInteractionPosture?: "interactive" | "headless"; /** * design/383 §2.5 (rescan post-6.0.0-RC) — the HOST session's live capture opt-out state, * threaded as a MOUNT dep for the same unenriched-auto-mount reason as the posture seat above. * A call-time GETTER on purpose (`ctx.memoryCaptureOptedOut` is live — a mid-run flip must * floor agents the workflow spawns after it); the enriched-ctx getters win when a wrapping * mount provides them. Rides `RunWorkflowOptions.parentMemoryCaptureState` into both spawn legs. */ parentMemoryCaptureState?: () => { optedOut: boolean; indeterminate: boolean; controlDir?: string; ancestors?: ReadonlyArray<{ sessionId: string; controlDir?: string; }>; } | Promise<{ optedOut: boolean; indeterminate: boolean; controlDir?: string; ancestors?: ReadonlyArray<{ sessionId: string; controlDir?: string; }>; }>; /** TRUSTED nesting depth from the run's internals (NOT a tool param) — passed to `startWorkflow` so a * cross-process child workflow is rejected by the one-level guard. */ workflowDepth?: number; /** The HOST task's effective working root — threaded into every spawned agent's trusted internals so a * TOC env factory can root the child at the parent's cwd (CC parity, 2026-07-03). */ parentCwd?: string; /** Call-time getter for the level a spawned agent runs at when neither the script, the agentType * definition nor the governance baseline set `thinking` (the mount folds the deployment's STATED * `roles.subagent.thinking` over the HOST task's current level — the child thinking seat's lower rungs). * Threaded to the workflow as `defaultThinking` and applied at the launch site AFTER the agentType fold, * the `parentModel` → `defaultModel` companion. */ parentThinking?: () => import("../core/types.js").TaskSpec["thinking"]; /** 5.30 merge-rescan (design/199 parity gap) — call-time getter for the HOST task's RESOLVED * read-face containment; folds stricter-wins into every spawned workflow child (see * `buildWorkflowPrimitives`'s injection block for the full rationale). */ parentReadFace?: () => import("../core/types.js").TaskSpec["readFace"]; /** The WRITE seat's twin of the above: the HOST task's RESOLVED write-face containment, folded * stricter-wins into every spawned workflow child on its own seat. */ parentWriteFace?: () => import("../core/types.js").TaskSpec["writeFace"]; /** Twin of the above for the deny-set additions (union, not replace). */ parentReadDenyPatterns?: () => import("../core/types.js").TaskSpec["readDenyPatterns"]; /** #345 ③ — the HOST task's write-hands clamp, the read-face clamp's sibling on this lane (the * subagent delegation lane has carried it as `ToolExecuteContext.handsReadOnly` since its own * tighten-only ruling; the workflow lane was the remaining gap — a read-only host's workflow * children spawned with full write hands). Filled ONLY when the clamp is ON (`true`), exactly * like the ctx seat, so an unclamped deployment's mount gains no key. Known at prepare-time * (frozen TaskSpec snapshot — no lazy getter needed, the `parentCheckpointStore` shape). */ parentHandsReadOnly?: true; /** #345 ③ — the HOST task's hard-headless clamp (`interactiveTools: false`), same carriage rules * as `parentHandsReadOnly` above: only the DISABLING value travels, mirroring the subagent lane's * `ToolExecuteContext.interactiveTools` seat — without it a headless-clamped host's workflow * children could re-mount the interactive tool the host run disabled. */ parentInteractiveTools?: false; /** Call-time getter for the HOST task's RESOLVED model AND the word that chose it: a spawned agent whose * fold chain produced no model inherits the object on its spec and the word on its trusted channel * (the subagent lane's `ctx.model` / `ctx.modelRef` pair). Word-less ⇒ the object alone, i.e. a pin. */ parentModel?: () => import("../agents/child-model-seat.js").InheritedModelPick | undefined; /** The HOST task's per-model auth hook: the CREDENTIAL half of model * inheritance. The Model object fixed the URL routing but the child brain still authenticated * with the boot env token (rerun: 7/7 kimi-route 401s with the qwen key). The subagent lane * has inherited this hook since; the workflow lane never did — the governance whitelist * rightly blocks SCRIPTS from setting it, but host inheritance is a different lane. */ parentGetApiKeyAndHeaders?: import("../core/types.js").TaskSpec["getApiKeyAndHeaders"]; /** backlog #342 — the HOST task's frozen effective approver (`spec.onAsk ?? deps.onAsk`, the exact * value the host's own gate resolves an `ask` at — see * {@link import("../core/types.js").ToolExecuteContext.onAsk}). The workflow lane is a delegation * lane too (the subagent lane has forwarded this seat since the 2026-08-04 ruling), and it was the * one lane that never carried it: a deployment that wires its approver per TASK had every workflow * child's own ask resolve at the headless auto-deny while a live operator sat attached to the host. * A dep for the same reason as `forwardEvent`/`parentGetApiKeyAndHeaders`: the auto-mounted tool's * execute ctx is minimal `{toolCallId, signal}`, so a ctx-only read was dead on the one lane every * Runner deployment uses; `ctx.onAsk` still wins when a wrapping path provides it. The governance * whitelist rightly blocks SCRIPTS from naming `onAsk` — host inheritance is a different, TRUSTED * lane (the `parentGetApiKeyAndHeaders` precedent): it rides the governed baseline's BASE * slot at execute time (see the fold in `execute`), and only when the deployment's own baseline * does not pin the seat — a deployment-pinned `base.onAsk` (function or blanket) always wins. * Absent on both seats ⇒ byte-identical baseline: a child's asks keep the pre-#342 resolution * (`RunnerDeps.onAsk`, else the fail-closed headless auto-deny). */ parentOnAsk?: import("../core/tool-policy.js").OnAsk; /** #642 — the HOST run's `durableApproval` opt-in (value copy; the Runner mints it from `spec.durableApproval` * beside `parentOnAsk`, the same seat the delegation tool reads as `ctx.durableApprovalForChildren`). Folded * into `RunWorkflowOptions.defaultDurableApproval` at execute — ONLY when a `store` is wired (the wa* row's * durable home; without it the child keeps the pre-#642 lifecycle: its unavailable ask denies fail-closed * and the `delegation.ask_unresolvable` notice says so). `ctx.durableApprovalForChildren` wins when a * wrapping path provides it; this dep covers the auto-mounted tool's minimal execute ctx, like `parentOnAsk`. * Absent on both seats ⇒ byte-identical: a workflow child's ask keeps the host-seat resolution alone. */ parentDurableApproval?: { scope: string; ttlMs?: number; }; /** #642 — the decisions a host holds for PARKED wa* rows of a prior run, read at execute when the model * re-invokes with `resumeFromRunId` (a call-time getter over the run id: the decisions arrive on the host's * trusted run channel, `RunInternals.workflowParkedResume`, and only the ones naming THIS resume's source run * apply). Threaded into `RunWorkflowOptions.parkedResume`: the resume drives the parked child on with the * decision instead of re-parking. Never a tool argument — a model cannot decide an approval. */ parkedResume?: (resumeFromRunId: string) => ReadonlyArray<import("./workflow.js").WorkflowParkedResume> | undefined; /** The HOST run's display sink (its `RunInternals.onForwardEvent` behind the runner's ctx wrapper: * `task_progress` always, plus the children's content events — `text_delta`/`text_end`/ * `reasoning_delta`/`tool_start`/`tool_end`, UNTRUSTED-RAW: the consumer must redact — when the HOST spec set * `forwardSubagentEvents: true`) — threaded via `startWorkflow` into every spawned agent's trusted * internals so a workflow child's events bubble to the deployment's one sink, the same * channel a `createSubagentTool` delegation threads. Display-only; absent ⇒ ticks stay in each * child's own stream. (A dep, not read off the execute ctx: the mounted tool's `AgentTool.execute` * wrapper builds a minimal `{toolCallId, signal}` ctx — the rich-ctx injection only wraps * `spec.tools`. `ctx.forwardEvent` still wins when a wrapping path provides it.) */ forwardEvent?: (event: import("../core/types.js").TaskEvent) => void; /** Parent effective-policy inheritance: the HOST task's `inheritedGateForChildren` chain accessor, * called at LAUNCH time and threaded via `RunWorkflowOptions.inheritedGate` into every spawned agent's * trusted internals — workflow children inherit the host's evaluated gate exactly like a * `createSubagentTool` delegation. (A dep for the same reason as `forwardEvent`: the direct mount's * execute ctx is minimal; `ctx.inheritedGateForChildren` still wins when a wrapping path provides it.) */ inheritedGateForChildren?: () => import("../core/runner/contracts.js").InheritedGate; /** RB-201 FO-3 — the HOST session's auto-mode classifier, threaded via `RunWorkflowOptions.autoModeReview` * so every child this workflow spawns is reviewed before it starts, exactly like a `createSubagentTool` * delegation's child. (A dep for the same reason as `forwardEvent`/`inheritedGateForChildren`: the direct * mount's execute ctx is minimal; `ctx.autoModeReview` still wins when a wrapping path provides it.) * A call-time getter, not a value — the decider is built during prepare and this dep is read at launch. * Absent on both seats ⇒ auto-mode is not armed and the review legs are a complete no-op. */ autoModeReview?: () => { decider: import("../core/auto-mode.js").AutoModeDecider; } | undefined; /** The HOST run's RESOLVED checkpoint seat — the store object the host parks in, else the word `"disabled"` * (the same two-state value the delegation ctx carries as `ToolExecuteContext.checkpointStoreForChildren`; * `ctx` wins on a rich-ctx mount). Every agent this workflow spawns inherits it verbatim unless its own * spec (baseline / trusted script) pinned a store — and `"disabled"` lands unconditionally (a removal the * baseline must not undo); see `buildWorkflowPrimitives`. A dep for the same reason as `forwardEvent`/ * `inheritedGateForChildren`: the auto-mounted tool's execute ctx is minimal `{toolCallId, signal}`. */ parentCheckpointStore?: import("../core/checkpoint-store.js").CheckpointStore | "disabled"; /** #235 — the deployment's structured notice sink (`RunnerDeps.onNotice`), forwarded into the governed * build so the fields a script's agent spec wrote that did NOT reach the child are announced instead of * vanishing (`workflow.governance_key_stripped`). #505 ① — the same seat now also carries the OPTIONS * membrane's twin announcement (`workflow.agent_option_ignored`: keys of `agent(spec, opts)`'s second * argument that did not apply). A dep for the same reason as the seats above: this auto-mount's execute * ctx is minimal. Absent ⇒ the historic `console.warn` loudness. */ onNotice?: (n: import("../core/types.js").EngineNotice) => void; /** Twin of the above for design/148 S1's center-artifact inheritance (see the `ctx.centerArtifactDigest`/ * `ctx.centerSourceRevision` reads at the `startWorkflow` options site) — `centerAdoption` resolves * LATE in prepare-task.ts (well after this tool's mount point), so this is a call-time getter, not * a value, mirroring `parentReadFace`. `ctx.centerArtifactDigest` still wins when a wrapping path * provides it. */ parentCenterArtifactDigest?: () => string | undefined; /** Publish-provenance companion of the above. */ parentCenterSourceRevision?: () => string | undefined; } /** * Build the `run_workflow` tool. ASSERTS the runner is a hard sandbox (defense in depth — prepare-task only * mounts it under the same gate). Returns a structured error (not a throw) for an LLM-correctable problem * (oversized / malformed-meta / nesting), so the model can fix and retry. * * Async since: the card's named-workflow roster is derived from the SAME sources the execute path * resolves against (built-ins probed through `resolveName` shadowing + awaited `list()`), so the card can * never advertise a workflow the call would resolve differently. */ export declare function createRunWorkflowTool(d: RunWorkflowToolDeps): Promise<AgentTool>;