import type { TSchema } from "typebox"; import type { Runner } from "../core/runner/runtask.js"; import type { AgentDefinition, TaskResult, TaskSpec, TaskStatus, ToolActivity } from "../core/types.js"; import type { WorkflowRunStore } from "../core/workflow-run-store.js"; import type { WorkflowJournalStore } from "../core/workflow-journal-store.js"; /** 198 `P0m` (pretty.js:447248) — prose state: the child's final text is the script-facing return value. */ export declare const WORKFLOW_SUBAGENT_PROMPT = "You are a subagent spawned by a workflow orchestration script. Use the tools available to complete the task.\n\nCRITICAL: Your final text response is returned **verbatim** as a string to the calling script \u2014 it is your return value, not a message to a human.\n- Output the literal result (data, JSON, text). Do NOT output confirmations like \"Done.\" or \"Sent.\"\n- If asked for JSON, return ONLY the raw JSON \u2014 no code fences, no prose, no markdown.\n- Do NOT address a human user \u2014 there is no user on the other end. Put your answer in your final text response.\n- Do NOT delegate to further sub-agents or start nested workflows; complete the task yourself.\n- Be concise. The script will parse your output."; /** 198 `M0m` (pretty.js:447333) — schema state: the answer goes through the StructuredOutput tool, once. */ export declare const WORKFLOW_SUBAGENT_PROMPT_SCHEMA = "You are a subagent spawned by a workflow orchestration script. Use the tools available to complete the task.\n\nCRITICAL: You MUST call the StructuredOutput tool exactly once to return your final answer. The tool's input schema defines the required shape.\n- Do your work (Read files, run commands, etc.), then call StructuredOutput with your answer.\n- Do NOT put your answer in a text response. The script reads ONLY the StructuredOutput tool call.\n- If the schema validation fails, read the error and call StructuredOutput again with a corrected shape.\n- After calling StructuredOutput successfully, end your turn. No acknowledgment needed.\n- Do NOT delegate to further sub-agents or start nested workflows; complete the task yourself."; /** 198 composite prose-append `O0m` (pretty.js:447255) — a script-supplied CUSTOM persona gets the return * contract APPENDED (never replaced), mirroring 198's `opts.agentType` + schema composition. VERBATIM * (PARITY-SPOT-WORKFLOW F6/B6): 198's constant opens with a `---` separator line and closes with the * "Output the literal result; do not output confirmations…" tail — the previous sema paraphrase drifted. */ export declare const WORKFLOW_SUBAGENT_APPEND = "---\n\nNOTE: You are running inside a workflow script. Your final text response is returned verbatim as a string to the calling script \u2014 it is your return value, not a message to a human. Output the literal result; do not output confirmations like \"Done.\" Be concise \u2014 the script will parse your output."; /** 198 composite schema-append `L0m` (pretty.js:447328) — VERBATIM incl. the `---` opener (F6/B6; the old * sema text compressed the "Do your work, then call X" instruction out). */ export declare const WORKFLOW_SUBAGENT_APPEND_SCHEMA = "---\n\nNOTE: You are running inside a workflow script. You MUST return your final answer by calling the StructuredOutput tool exactly once \u2014 the tool's input schema defines the required shape. Do your work, then call StructuredOutput; do NOT put your answer in a text response (the script reads ONLY the tool call). If validation fails, read the error and call StructuredOutput again with a corrected shape."; /** * Apply the G5 default persona to a `ctx.agent`/`ctx.agentStream` child spec. Two-state on the EFFECTIVE * schema (`agentOpts.schema` wins over a spec-carried `outputSchema`, same as the runSpec injection): * • no `spec.systemPrompt` → the dedicated workflow-subagent persona REPLACES the role base (198 * default `workflow-subagent` agent-type semantics); * • a script-supplied `spec.systemPrompt` (custom persona) → the matching NOTE is APPENDED via * `appendSystemPrompt` (198 composite semantics — append, never replace), after any existing append. * Called AFTER the call-key is computed (the journal identity keys the AUTHORED spec, so a resume across * core versions replays cleanly; the persona is an execution detail, not call identity). */ /** * F4 agentType (CC 198 锚 pretty.js:446608-446627): resolve `opts.agentType` against the registry * (deployment SHADOW over built-ins) and fold the definition into the child spec — persona as * `systemPrompt` (so {@link withWorkflowChildPersona} composes the return-contract NOTE via the * custom-persona APPEND arm = CC `O0m` semantics), model/thinking/maxTurns/skills/memory when the spec * didn't pin them, and allow/denyTools as a ToolPolicy (combined deny-wins with any spec policy). * The definition is DEPLOYMENT-TRUSTED (registry-declared, not script-authored), so its model bypasses * the script-facing modelName allowlist by design — same trust tier as the Agent tool's registry. */ export declare function applyWorkflowAgentType(spec: TaskSpec, agentType: string | undefined, registry: AgentDefinition[]): TaskSpec; /** The agentType registry for one workflow run: built-ins (unless opted out) with deployment SHADOW. */ export declare function workflowAgentRegistry(opts: { agents?: AgentDefinition[]; builtinAgents?: boolean; }): AgentDefinition[]; /** * design/97 CORE-1 (foundation) — the STABLE, deterministic identity of one `ctx.agent` call, so the resume * journal (CORE-7) can key a cached result and the observe tree (CORE-3/5) can hang per-agent data off it. * `ordinal` is the synchronous call-order index (`run.agents.length` at RECORD time — captured BEFORE the * `sem.acquire` await, so for `parallel(direct thunks)`/sequential it is deterministic run-to-run. `pipeline`'s * stage-2+ ordinals are LATENCY-dependent (NOT deterministic) — so `pipeline` conservatively forces replay * divergence and runs live on resume; deterministic stage-scoped keys are a CORE-7.1 follow-on. The hash is over * the WHOLE behaviour-defining spec (audit fix — a cherry-pick missed images/thinking/limits/maxTokens/model * runtime fields), with non-behavioural identity (taskId/sessionId/signal) stripped and tools/mcp reduced to * NAMES (their closures aren't serializable). SHARED pure fn (anti-drift): the journal MUST key through this. */ export declare function workflowAgentCallKey(ordinal: number, spec: TaskSpec, opts: { schema?: TSchema; isolation?: "worktree"; }): string; /** * Workflow mode (design/97 S1a) — deterministic JS orchestration primitives over `runner.runTask`, the * core counterpart of Claude Code's Workflow tool. A workflow is a **script the caller writes** * (`parallel`/`pipeline`/`phase`/`budget` + `ctx.agent`), NOT an LLM dynamically planning a DAG (the * value-judgment-rejected LLM-planner). Thin composition, zero Runner-core changes — same posture as the 5 * orchestrators (team/cascade/teacher/verify/repair-loop). * * `ctx.agent(spec)` runs `runner.runTask(spec)` directly (script-driven, deterministic) — distinct from the * subagent tool, where the LLM decides to delegate (non-deterministic). * * This module owns the run model + event types (S1a builds an in-memory {@link WorkflowRun} and emits * {@link WorkflowEvent}s). Persistence (`WorkflowRunStore`, S1b) and the `/workflows` query/subscribe API * (S1c) consume these types; with no store wired a workflow still runs and emits — observability is * opt-in, never required (design/97 §D.4). */ export type WorkflowRunStatus = "running" | "completed" | "failed"; /** Status of one phase / agent-run within a workflow. */ export type WorkflowItemStatus = "running" | "completed" | "failed"; export interface WorkflowPhase { title: string; /** F7/B7 (CC pretty.js:446478): a phase PRE-REGISTERED from `meta.phases` starts as `"pending"` — the plan * is visible before execution. The first `phase()` call with the SAME title adopts it (→ running). A phase * still pending at the run's terminal was planned but never reached (left pending, honestly). */ status: WorkflowItemStatus | "pending"; /** meta.phases[].detail, when pre-registered. */ detail?: string; /** meta.phases[].model (CC card :448042) — display-only phase model note. */ model?: string; /** For a pre-registered phase this is the ADOPTION time (0 while pending — it hasn't started). */ startedAt: number; endedAt?: number; } /** design/97 CORE-3: one nested `ctx.workflow` sub-group. The tree is reconstructed from `parentGroupId` * (undefined = a child of the root) + the agents/phases that carry this `groupId`. */ export interface WorkflowGroup { groupId: string; parentGroupId?: string; status: WorkflowItemStatus; startedAt: number; endedAt?: number; } export interface WorkflowAgentRun { /** Display label (caller-supplied via opts.label, else an auto `agent-N`). */ label: string; /** design/97 CORE-1: the STABLE deterministic identity of this `ctx.agent` call ({@link workflowAgentCallKey} * = `ordinal:specIdentityHash`). The resume journal (CORE-7) keys a cached result on it. */ callKey: string; /** design/97 CORE-1: the nesting GROUP this agent ran under — `undefined` = top-level (root); a nested * `ctx.workflow` (CORE-3) sets a sub-group id so the observe/UI can render the agent tree. */ groupId?: string; /** The phase this agent ran under, if any (opts.phase, else the enclosing `phase()` title). */ phase?: string; /** design/97 CORE-8 (①): the model display label (`spec.model` id/name) — for a CC-style "Opus 4.8" per-agent row. */ model?: string; /** design/97 CORE-4 (#5 observability): what the worker was ASKED — its objective (+ systemPrompt), redacted * + size-bounded. NOT fenced at rest; a consumer feeding it to an LLM must fence (untrustedEgressForHuman). */ prompt?: string; /** design/97 CORE-4 (#5 observability): the worker's final OUTPUT (structuredOutput or result text), redacted * + size-bounded. Same fencing caveat as `prompt`. Set on a completed/returned run (absent on a thrown one). */ output?: string; /** design/97 CORE-8 (②): total tool calls the worker made (from `TaskResult.stats.toolCalls`). */ toolCalls?: number; /** design/97 CORE-8 (③): the worker's tool-call ACTIVITY — bounded to the LAST {@link MAX_ACTIVITY} beats (a * CC-style "last N of M tool calls"; M is `toolCalls`). Structural only (name/phase/ids), no args/output. */ activity?: ToolActivity[]; status: WorkflowItemStatus; /** The underlying task's terminal {@link TaskStatus} (a non-`completed` status maps to `failed` above). */ taskStatus?: TaskStatus; /** design/99 MF-W (design-review DoR ⑥): when this agent was ENQUEUED (the `ctx.agent`/`ctx.agentStream` call, * before it waited on the concurrency semaphore). Always set. A record with `queuedAt` set but `startedAt` * ABSENT is QUEUED (waiting for a slot) — `deriveAgentDisplayStatus` projects that to `"queued"`. */ queuedAt: number; /** When the agent ACTUALLY started running — set AFTER it acquired a concurrency slot (post-queue). ABSENT * while queued, or if it was aborted/the-run-finalized before it ever ran. So `durationMs` (`endedAt - * startedAt`) excludes the queue wait (the DoR fix: a queued agent no longer reports a wrong running duration). */ startedAt?: number; endedAt?: number; /** This agent's OWN (root) usage — nested/delegated usage rolls into the run's {@link WorkflowRunStats.nested}. */ stats?: { tokens: number; turns: number; costMicroUsd?: number; }; /** design/97 CORE-7: set when this agent's result was REPLAYED from a resume journal (cached, no live runTask) * rather than freshly run. Its stats count toward `run.stats` (total work) but not `ctx.budget.spent()`. */ replayed?: boolean; /** F5/B4: total attempts this call made (set only when > 1 — stall/throttle retries happened). The record's * `stats` are the FINAL attempt's; burned retry spend rolls into `run.stats`/budget (CC semantics). */ attempts?: number; /** F5/B4: why the LAST retry happened — "stalled" (progress watchdog) or "throttled" (degraded response). */ lastAttemptReason?: string; /** design/114 — the session id of this agent's run (= its `TaskResult.sessionId`). Surfaces the conversation * handle to the observation layer so an external initiator can WARM-resume a failed/timed-out agent * (`fork(sessionId)→runTask`, or `runTask({sessionId})`) instead of a fresh re-run. An opaque id (not * content) → scope-gated via `getWorkflowRun`, no redaction needed. Absent for a stub run that minted none. */ sessionId?: string; } /** * Cumulative workflow usage. `own` (top-level) and `nested` (each agent's delegated sub-agents) are kept * SEPARATE — never folded — mirroring `TaskResult.stats` (R-5): a consumer adds them. Total spend = * `tokens + nested.tokens`. */ export interface WorkflowRunStats { tokens: number; turns: number; costMicroUsd: number; nested: { tokens: number; turns: number; tasks: number; costMicroUsd: number; }; } export interface WorkflowRun { id: string; scope: string; /** design/99 MF-W (workflow monitor header): the workflow's display name + one-line description, from the * script's `export const meta = {...}` (the run_workflow tool passes them in). Absent for a direct * `runWorkflow` call that supplies no name. */ name?: string; description?: string; /** design/114 #3 (service) — the HOST task id that started this workflow (the `RunWorkflow` tool's source * task). Lets a crash-recovered consumer rebuilding from `getWorkflowRun` re-associate the run with its * initiator: the live notify path carries it, but the recovery path can't otherwise recover it (it rebuilds * from the persisted `WorkflowRun`, which until now dropped it). Absent for a direct `runWorkflow` call. */ sourceTaskId?: string; /** service [371]②: the ORIGINATING session id, recorded like {@link sourceTaskId} so crash-recovery * rebuilds a completion payload symmetric with the live notify (which carries it since 1.208). */ originatingSessionId?: string; /** design/140 §6 1a — the MERGED effective args the script actually received (call-time args over the * registration's defaultArgs), snapshotted at RESOLVE time (parse-time discipline, design/140 §2). * OBSERVATION-ONLY (a /workflows viewer / recovery consumer reads what the run got) — never a gate input, * never re-read by the engine; a resume's identity is carried by the journal callKeys, not this field. * Absent for a direct `runWorkflow` call or an argless invocation. */ effectiveArgs?: unknown; status: WorkflowRunStatus; phases: WorkflowPhase[]; agents: WorkflowAgentRun[]; /** design/97 CORE-3: nested `ctx.workflow` sub-groups (the persisted group tree). Empty when the script * used no nesting. Agents/phases reference a group via their `groupId`. */ groups: WorkflowGroup[]; stats: WorkflowRunStats; startedAt: number; endedAt?: number; createdAt: number; /** Optimistic-concurrency revision for the store (S1b); unset for a pure in-memory run. */ rev?: number; /** Set when `status === "failed"`: the error the script threw. */ error?: string; /** 黑板 [409](飞轮): the script's RETURN VALUE, bounded + redacted at completion time (same egress * discipline as the notifier `result`), so a terminal `TaskOutput` poll can hand the * model the result instead of sending it in poll circles. Absent on failed runs and runs recorded by * pre-[409] engine versions. */ result?: string; /** 黑板 [413] (NH-1): the FULL return value (redacted, capped at {@link WORKFLOW_RESULT_FULL_MAX}), set ONLY * when `result` above was truncated by its display bound. Terminal poll replies inline this instead of the * truncated `result`, so the generic large-tool-result offload (design/30) persists it under a REAL * `read_tool_result` ref — previously the `…[+N chars]` tail dead-ended (no retrieval path; models tried * the runId as a ref and hit the store-miss message). Display/notify surfaces keep using `result`. */ resultFull?: string; } export type WorkflowEvent = { type: "run_start"; runId: string; scope: string; ts: number; } | { type: "phase_start"; runId: string; title: string; ts: number; } | { type: "phase_end"; runId: string; title: string; status: WorkflowItemStatus; ts: number; } | { type: "agent_start"; runId: string; label: string; phase?: string; groupId?: string; callKey?: string; prompt?: string; model?: string; queuedAt?: number; replayed?: boolean; ts: number; } | { type: "agent_end"; runId: string; label: string; phase?: string; groupId?: string; status: WorkflowItemStatus; output?: string; toolCalls?: number; replayed?: boolean; ts: number; } | { type: "agent_activity"; runId: string; callKey: string; label: string; groupId?: string; phase: "start" | "end"; toolCallId: string; toolName: string; arg?: string; isError?: boolean; ts: number; } | { type: "subgroup_start"; runId: string; groupId: string; parentGroupId?: string; ts: number; } | { type: "subgroup_end"; runId: string; groupId: string; status: WorkflowItemStatus; ts: number; } | { type: "log"; runId: string; message: string; ts: number; } | { type: "run_end"; runId: string; status: WorkflowRunStatus; ts: number; }; /** Thrown by `ctx.agent` once the workflow's cumulative token spend reaches the budget (design/96 G#4: a * hard ceiling, typed error in v1; durable suspend is left to a future slice). */ export declare class WorkflowBudgetExceededError extends Error { readonly spent: number; readonly total: number; readonly code = "workflow.budget_exceeded"; constructor(spent: number, total: number); } /** * Thrown by `runWorkflow`/`startWorkflow` when a workflow is started INSIDE another workflow — nesting is * capped at ONE level (design/97 §H.1, the S8 prerequisite). A workflow's agent (a `runner.runTask` child) * cannot itself start a workflow: the LLM-facing `run_workflow` tool hits this guard, and so does a * trusted-dev nested `runWorkflow(...)` call. The depth is read from the TRUSTED * {@link WorkflowInternals.workflowDepth} first (the cross-process channel — a worker/script cannot forge it, * it is not a `TaskSpec` field) then the in-process {@link workflowDepthStore} (AsyncLocalStorage), so neither * a spec field nor a tool param can defeat it (design/98 §0.1 BLOCKER3). */ export declare class WorkflowNestingError extends Error { readonly code = "workflow.nesting"; constructor(); } /** * Thrown by `ctx.agent(spec, { schema })` (design/98 §0.2, strict mode) when the sub-agent **completed but * produced no `structuredOutput`** — i.e. the model answered in PROSE, which core's `outputSchema` path * permits by default (types.ts: "may still answer in prose"). A workflow script that asked for a structured * result must not silently receive `undefined`; it fails loud here instead. A task that ended NON-`completed` * (failed / blocked / timeout) is returned as-is (the script checks `result.status`) and never turned into * this error — that path already carries its own signal. */ export declare class WorkflowAgentSchemaError extends Error { readonly label: string; readonly code = "workflow.agent_schema"; constructor(label: string); } /** design/98 §D.6 hard cap: thrown by `ctx.agent` once the workflow has spawned `max` agents (a runaway * LLM-authored script is bounded, not trusted — budget alone is checked only pre-spawn, so a concurrent * fan-out can overshoot it; this counts cumulative spawns). */ export declare class WorkflowMaxAgentsError extends Error { readonly max: number; readonly code = "workflow.max_agents"; constructor(max: number); } /** design/98 §D.6 hard cap: thrown when the script's returned result exceeds `maxResultChars` (a runaway * script must not return an unbounded payload to the originator). */ export declare class WorkflowResultTooLargeError extends Error { readonly size: number; readonly max: number; readonly code = "workflow.result_too_large"; constructor(size: number, max: number); } /** * A TRUSTED, run-scoped internal channel carrying the workflow **nesting depth** across a process boundary * (design/98 §0.1 BLOCKER3). It is NOT a `TaskSpec` field and NOT a `run_workflow` tool param — a worker or * an LLM-authored script can never set it. When a deployment (e.g. the service) initiates a workflow on * behalf of a parent run that is itself inside a workflow, it passes `workflowDepth = parentDepth + 1` here * so the entry guard fires cross-process. In-process nesting needs no internals — the * {@link workflowDepthStore} AsyncLocalStorage propagates depth into every `runner.runTask` child * automatically. `startWorkflow` takes depth = `internals?.workflowDepth ?? ALS.depth ?? 0`. */ export interface WorkflowInternals { workflowDepth?: number; } export interface WorkflowBudget { /** The token ceiling, or null when none was set (then `remaining()` is Infinity). */ readonly total: number | null; /** Cumulative tokens spent so far (own + nested across all agents). */ spent(): number; /** `max(0, total - spent())`, or Infinity when no budget was set. */ remaining(): number; } export interface WorkflowAgentOptions { /** Display label for this agent-run (else `agent-N`). */ label?: string; /** * F4 (CC 198 parity, 锚 pretty.js:446608-446627; DEFER 解除 2026-07-11 — 前提「无 agent 注册表」被 * 1.262.0 agents F1 推翻): run this agent AS a named agent type from the deployment's registry * (built-in Explore/Plan + `RunWorkflowOptions.agents`, deployment SHADOW wins on a name collision — * the same registry the Agent tool's `subagent_type` resolves). The definition supplies persona * (composed with the workflow return-contract NOTE — CC `O0m` append semantics), model (tier words OK, * the catalog is tier-expanded), thinking, maxTurns, skills, memory and the allow/deny tool policy. * An unknown name throws a teaching error listing the known types. Slot-tools carrier (design/140 * ②-3): a collab member slot's tool surface = its agent type's allow/denyTools — no new mechanism. */ agentType?: string; /** Explicitly group this agent under a phase title (use this inside `parallel`/`pipeline` where the * global enclosing-phase state races — design/97 §D race note). */ phase?: string; /** Per-agent cancellation; falls back to the workflow signal. */ signal?: AbortSignal; /** * Force a STRUCTURED result for this sub-agent (design/98 §0.2). Sets the child task's `outputSchema` to * this typebox schema; the validated object is surfaced as `TaskResult.structuredOutput`. **Strict**: if * the child COMPLETES without a `structuredOutput` (the model answered in prose — core permits that), * `ctx.agent` throws {@link WorkflowAgentSchemaError} rather than silently returning `undefined`. A child * that ends non-`completed` is returned as-is (the script checks `status`). If the script's `spec` already * carried an `outputSchema`, this option wins. */ schema?: TSchema; /** * design/97 CORE-6: run this agent in an ISOLATED git WORKTREE so parallel agents editing the same repo * don't conflict. A SCRIPT-FACING OPTION (NOT a TaskSpec field — an untrusted spec can't self-select an env, * design/44 §7 Q4): threaded via the TRUSTED RunInternals.isolation to the control-plane executionEnvFactory, * which mints a worktree-rooted env (e.g. {@link addWorktree}). The worktree is INSIDE the one configured env * (local OR a single E2B) — not a per-agent container. ISOLATE-ONLY: the runtime never merges; the * orchestrator script reads each worktree's result and decides verify/merge in userland. Requires a * worktree-capable `executionEnvFactory`; durable-suspend is incompatible (the factory env is non-remote). */ isolation?: "worktree"; } /** * design/97 CORE-5 (#6 steer) — the handle returned by {@link WorkflowRunContext.agentStream}: a STILL-RUNNING * agent the launcher can STEER mid-flight and then await. The recording (callKey/groupId/prompt + stats/output + * `agent_end`) and the concurrency slot are tied to the underlying stream settling (an eager completion), NOT to * `result()` being called — so a script that defers `result()` still records and releases. ⚠️ EXCEPTION (audit * MAJOR): if the script returns and the run FINALIZES before the stream settles, the eager completion hits the * finalized-guard and skips the trailing record — so a fire-and-forget stream the body never awaits may be left * as `running`. A leader that wants the outcome (the normal case) awaits `result()`, which is fully recorded. */ export interface WorkflowAgentHandle { /** The enclosing workflow run id (NOT a per-agent id — agentStream does not mint a sub-run). */ runId: string; /** This agent's display label. */ label: string; /** This agent's deterministic {@link workflowAgentCallKey}. */ callKey: string; /** * Inject an operator/leader STEER into the running worker (design/47 `TaskStream.steer`). The content is * FENCED (untrusted data — it can't pose as authority) inside a TRUSTED framing that asks the worker to TAG * its reply with the returned marker. Returns that MARKER so the launcher can correlate the worker's tagged * reply via the #5 transcript (the worker self-stamps; reply is best-effort). One-directional + leader-driven * (the worker can't address the leader except by the marker). Rejects with `steering.not_running` if the task * hasn't started or already finished. */ steer(content: string): Promise; /** Await the agent's {@link TaskResult} (the same value the eager recording used; idempotent). */ result(): Promise; } export interface WorkflowRunContext { readonly runId: string; readonly budget: WorkflowBudget; /** The composed workflow abort signal (caller signal + `cancel()` + `totalTimeoutMs` deadline). A script * runner (the hard sandbox) should thread THIS into its execution so a cancel/timeout actually aborts the * script body — not just the agents it spawns (workflow review, caps). */ readonly signal: AbortSignal; /** Run one `runner.runTask(spec)` as a recorded agent-run (script-driven, NOT an LLM delegation). Returns * the TaskResult (any terminal status — it does not throw on a task-level failure). Throws on workflow * abort or budget exhaustion before spawning. * * F5 terminal semantics vs the CC anchor (206-pretty.js:17488900-17489600), verified 2026-07-12: * - stall-retry EXHAUSTION **throws** `agent stalled on all N attempts…` — CC throws here too (null is * NOT the stall terminal in CC). * - CC's `agent() → null` legs are user-skip and a terminal API error (post inner query-layer retries). * This seam has no user-skip, and a terminal API death surfaces as a **`status:"failed"` TaskResult** * (the script checks `status`) — the seam adaptation of CC's null; recorded divergence, not a gap. * - inside `parallel`/`pipeline` a THROWN leg folds to `null` (F11/B8), so the CC-taught * `.filter(Boolean)` idiom still holds at the fan-out level. */ agent(spec: TaskSpec, opts?: WorkflowAgentOptions): Promise; /** design/97 CORE-5 (#6): spawn a STEERABLE agent — same recording/caps/budget as {@link agent}, but returns a * {@link WorkflowAgentHandle} the launcher can `steer()` mid-flight and then `await result()`. Resolves once * a concurrency slot is acquired and the underlying stream has started (so `steer` won't hit a not-running * task). Use for long-running workers a leader wants to redirect; use `agent` for fire-and-await. */ agentStream(spec: TaskSpec, opts?: WorkflowAgentOptions): Promise; /** Run thunks concurrently (capped by the workflow concurrency limit via `agent`). A thunk that throws * resolves to `null` — filter before use. BARRIER: awaits all. */ parallel(thunks: Array<() => Promise>): Promise>; /** Run each item through all stages independently, NO barrier between stages. Each stage receives * `(prevResult, originalItem, index)`. A stage that throws drops that item to `null`. */ pipeline(items: I[], ...stages: Array<(prev: any, item: I, index: number) => Promise>): Promise; /** Group work under a named phase (observability). TWO forms (黑板 [406] CC-心智兼容): * - scoped `phase(title, body)` — `body`'s agents default to this phase; closes when body settles. * - bare `phase(title)` (CC marker style) — subsequent agents group under `title` until the NEXT * `phase()` call, the end of the enclosing scoped `phase()` body (T2A-10: a marker never leaks its * lexical scope), or the run's end — whichever comes first. Original contract line follows: * `phase()` call (which closes it) or the run's end. Returns `undefined`. */ phase(title: string, body?: () => Promise): Promise; /** * design/97 CORE-3: run `body` as a nested SUB-WORKFLOW — IN-PROCESS over the SAME ctx (shares budget / caps * / abort / the cumulative agent counter / the run object), under a fresh sub-`groupId` so the observe tree * can render it. It does NOT mint a new runId (it is NOT `startWorkflow`, so it never trips the run_workflow * nesting guard). Agents/phases spawned inside carry the sub-group's id. Group nesting is depth-capped. */ workflow(body: (ctx: WorkflowRunContext) => Promise): Promise; /** Emit a narrator log line (observability only — not stored on the run). */ log(message: string): void; } export interface RunWorkflowOptions { /** Trusted caller-supplied run id. Used by the unified task registry so tool-launched workflows expose * `task_id === runId` (`w*`) without making the model choose an id. Omit for the legacy UUID mint path. */ runId?: string; /** design/97 S1b/S1c — persist the {@link WorkflowRun} to a {@link WorkflowRunStore} for `/workflows` * history + cross-replica visibility. **Opt-in (default none)**: with no store the workflow still runs * + emits + is subscribable in-process (S1a behavior, unchanged). Persistence is **best-effort**: a store * throw is swallowed and NEVER breaks the workflow (the run/script is the source of truth; the store is an * observation layer). The owner is the sole writer (rev CAS). */ store?: WorkflowRunStore; /** Tenant/grouping key for the run (default `"default"`). */ scope?: string; /** design/99 MF-W: the workflow's display name + description (the run_workflow tool passes the script's * `meta.name`/`meta.description`) — recorded on the {@link WorkflowRun} for the /workflows monitor header. */ name?: string; description?: string; /** design/114 #3 — the host task id that started this workflow → recorded on the {@link WorkflowRun} so a * crash-recovered consumer can re-associate the run with its initiator (the run_workflow tool passes it). */ sourceTaskId?: string; /** service [371]②: the originating session id — recorded on the {@link WorkflowRun}(same rationale as * sourceTaskId: live notify carries it, recovery otherwise can't). */ originatingSessionId?: string; /** design/140 §6 1a — the merged effective args snapshot recorded on the {@link WorkflowRun} (the * run_workflow tool passes the value it resolved at parse time; a JSON value, observation-only). */ effectiveArgs?: unknown; /** Cancels the whole workflow: propagated to each agent's runTask; new agents short-circuit once aborted. */ signal?: AbortSignal; /** * F4 agentType registry (2026-07-11): deployment agent definitions resolvable by `agent(…, {agentType})`. * Same SHADOW rule as the Agent tool: a deployment definition whose name collides with a built-in * (Explore/Plan) wins. The run_workflow tool threads the deployment's `agents` here. */ agents?: AgentDefinition[]; /** `false` removes the built-in Explore/Plan from the agentType registry (mirror of the Agent tool's * `builtinAgents:false`, CC `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS` analog). Default true. */ builtinAgents?: boolean; /** Token ceiling — `ctx.agent` throws {@link WorkflowBudgetExceededError} once spend reaches it. */ budget?: number; /** Max concurrent agents (default `min(16, cpus-2)`, floor 1). */ concurrency?: number; /** design/98 §D.6 hard cap — max CUMULATIVE agents the workflow may spawn (LLM-authored bound). `ctx.agent` * throws {@link WorkflowMaxAgentsError} once reached. Undefined ⇒ only `budget`/`concurrency` bound it. */ maxAgents?: number; /** design/98 §D.6 hard cap — whole-workflow wall-clock deadline (ms). An internal AbortController cancels * the run at the deadline (agents short-circuit as aborted; the run finalizes `failed`). */ totalTimeoutMs?: number; /** design/98 §D.6 hard cap — cap on a single `log()` message length (chars); longer messages are truncated * with an honest marker (a runaway script must not flood the log). */ maxLogChars?: number; /** design/98 §D.6 hard cap — cap on the script's RETURNED result size (chars, JSON-serialized). Exceeding * finalizes the run `failed` with {@link WorkflowResultTooLargeError}. */ maxResultChars?: number; /** Progress sink (run/phase/agent/log/run_end events). */ onEvent?: (e: WorkflowEvent) => void; /** Injected clock (design/87 §4.3 — testable without the wall clock). Default `Date.now`. */ now?: () => number; /** * design/97 CORE-7 — the LOAD-BEARING resume journal. With a `journalStore`, every `ctx.agent` result (NOT * `ctx.agentStream` — see below) is recorded keyed by its deterministic callKey. Supplying `resumeFromRunId` * (a PRIOR run id, requires `journalStore`) replays the longest UNCHANGED PREFIX of those results instantly and * runs only the first changed/new call + everything after it live (same script + args → 100% cache hit). A * `journalStore` WITHOUT `resumeFromRunId` just records (so a future run can resume from THIS run). Replayed * agents accumulate into `run.stats` (total work) but NOT into `ctx.budget.spent()` (live spend only — a resume * is not re-charged for cached work). NOT-REPLAYED (they force divergence so the suffix runs live): `ctx.agentStream` * (a finished result can't be steered) and `ctx.pipeline` (latency-dependent ordinals aren't deterministic — * a CORE-7.1 follow-on adds stage-scoped deterministic keys). Sequential + `parallel(direct thunks)` replay. */ journalStore?: WorkflowJournalStore; /** design/97 CORE-7 — resume by replaying a prior run's journal (requires `journalStore`). See `journalStore`. */ resumeFromRunId?: string; /** The HOST task's effective working root — threaded into every spawned agent's trusted internals * (`RunInternals.parentCwd`) so a TOC env factory roots children at the parent's cwd (CC parity; * `isolation: "worktree"` on an individual agent wins over it). */ parentCwd?: string; /** F7/B7 (CC pretty.js:446478): PRE-REGISTER the script's `meta.phases` as pending progress groups at run * start — the plan is visible before execution; `phase(title)` adopts the matching pending entry. */ phases?: ReadonlyArray<{ title: string; detail?: string; model?: string; }>; /** F5/B4: override the per-agent stall watchdog (ms). Default {@link WORKFLOW_AGENT_STALL_MS}. A deployment * may tune it; tests set it small to exercise the retry path without waiting 180s. */ stallMs?: number; /** F5/B4: override the max stalled-attempt retries. Default {@link WORKFLOW_AGENT_MAX_RETRIES}. */ agentMaxRetries?: number; /** F5/B4: override the throttle-degradation backoff (ms). Default {@link WORKFLOW_AGENT_THROTTLE_BACKOFF_MS}. */ throttleBackoffMs?: number; /** F5/B4 (design/87 §4.3): injectable timers for the resilience layer (stall watchdog + throttle backoff * sleep) — tests advance a virtual clock instead of really waiting {@link WORKFLOW_AGENT_STALL_MS}. * Default: real unref'd `setTimeout`/`clearTimeout`. Pair with `now` for a fully virtual clock. */ timers?: WorkflowTimers; } export interface RunWorkflowResult { result: T; runId: string; run: WorkflowRun; } /** * design/98 §D.4 (S8c) — the handle {@link startWorkflow} returns: the `runId` is available SYNCHRONOUSLY * (the run row + live subscription exist before the body runs, so a caller can subscribe + return the id to an * LLM immediately), `done` resolves/rejects exactly like {@link runWorkflow}, and `cancel` aborts the whole * run (every agent's signal fires; new agents short-circuit). */ export interface WorkflowHandle { readonly runId: string; readonly done: Promise>; cancel(reason?: string): void; } /** Cap on items a single `parallel`/`pipeline` call accepts (matches the CC Workflow tool). */ export declare const MAX_WORKFLOW_ITEMS = 4096; /** CC `MTy` (206-pretty.js:17501680; 198 `F0m`): no tool activity for this long marks the attempt STALLED * (progress-based, unlike the governance perAgentTimeoutSec hard cap, which still bounds each attempt). * `<= 0` DISABLES the watchdog (CC arms only `if (ae > 0)`, :17477830). */ export declare const WORKFLOW_AGENT_STALL_MS = 180000; /** CC `j_d` (206-pretty.js:17501680; 198 `Mxl`): max stalled-attempt retries per `ctx.agent` call * (initial + 5 = 6 attempts). */ export declare const WORKFLOW_AGENT_MAX_RETRIES = 5; /** CC throttle backoff (206-pretty.js:17488618): ONE fixed 45s sleep-then-retry for a throttle-shaped * degraded response (a flat sleep, NOT exponential backoff — and only on the FIRST attempt's result). */ export declare const WORKFLOW_AGENT_THROTTLE_BACKOFF_MS = 45000; /** F5/B4 + design/87 §4.3 — the INJECTABLE timer seam the per-agent resilience layer schedules on (the * stall watchdog + the throttle backoff sleep). Tests drive VIRTUAL time instead of really waiting * 180s/45s. Default: the real `setTimeout`/`clearTimeout`, unref'd (a pending watchdog must never hold * the process open). */ export interface WorkflowTimers { setTimeout(fn: () => void, ms: number): unknown; clearTimeout(handle: unknown): void; } /** * Start a workflow script and return its {@link WorkflowHandle} SYNCHRONOUSLY (design/98 §D.4): the `runId`, * run row, and live subscription exist before the body runs — so a caller (e.g. the `run_workflow` tool) can * subscribe + hand the id to an LLM immediately, then await `done`. Establishes a {@link WorkflowRunContext}, * runs `fn(ctx)` asynchronously, and resolves `done` with the script's value + the assembled * {@link WorkflowRun} (status `completed`). If `fn` throws, the run finalizes `failed` (events emitted) and * `done` rejects. `cancel(reason)` aborts the whole run. A nesting/validation violation throws SYNCHRONOUSLY * from this call (before any handle exists). Thin composition over `runner.runTask` — no Runner changes. */ export declare function startWorkflow(runner: Runner, fn: (ctx: WorkflowRunContext) => Promise, opts?: RunWorkflowOptions, internals?: WorkflowInternals): WorkflowHandle; /** * Run a workflow to completion (design/97 S1a) — the thin await over {@link startWorkflow}: returns the * script's value + the assembled {@link WorkflowRun}. A synchronous nesting/validation throw from * `startWorkflow` becomes a rejected promise here (this function is `async`), preserving the original * `runWorkflow` contract (`await runWorkflow(...)` rejects rather than throwing synchronously). */ export declare function runWorkflow(runner: Runner, fn: (ctx: WorkflowRunContext) => Promise, opts?: RunWorkflowOptions, internals?: WorkflowInternals): Promise>; //# sourceMappingURL=workflow.d.ts.map