import { AIError } from "../../errors/ai-error.mjs"; import { AgentContract } from "../agent/agent.contract.mjs"; import { NextStepResult } from "./next-step-result.type.mjs"; import { RetryConfig } from "./retry-config.type.mjs"; import { WorkflowContext } from "./workflow-context.type.mjs"; import { AgentExecuteOptions } from "../agent/agent-options.type.mjs"; import { StandardSchemaV1 } from "@standard-schema/spec"; //#region ../ai/src/contracts/workflow/step.contract.d.ts /** * Step-local event payloads. Fired on per-step `on: { ... }` handlers * in addition to the workflow-level `workflow.step.*` events — useful * when you want a handler scoped to one step only. */ type StepLocalEvents = { /** Before the step's lifecycle starts (after `skip` evaluates false). */starting: { step: string; }; /** Fired before every retry attempt (not the first). */ retrying: { step: string; attempt: number; totalAttempts: number; lastError: unknown; }; /** Fired once after all retry attempts are exhausted. */ failed: { step: string; error: unknown; attempts: number; }; /** Fired when the step completes successfully. */ completed: { step: string; output: unknown; duration: number; }; }; /** * Input built by `input(ctx)` when the step has an `agent`. Carries * the prompt string plus any per-call agent options (history, * attachments, placeholders, per-call output schema, etc.). */ type StepAgentInput = AgentExecuteOptions & { /** Prompt text passed to `agent.execute(prompt, options)`. */prompt: string; }; /** * Output extraction spec. `extract(ctx)` reads from `ctx.agentResult` * (for agent steps), the step's `run()` return value, or `ctx.state` * and produces the value stored at `step.output` in the snapshot. * * If `schema` is supplied, the extracted value is validated against it; * validation failures throw `SchemaValidationError` and trip the * step's retry logic (a retryable failure). */ type StepOutputSpec, TContext = unknown> = { extract: (ctx: WorkflowContext) => TOutput | Promise; schema?: StandardSchemaV1; }; /** * Static declaration of a single workflow step. * * **Step kind.** Exactly one of `run`, `agent`, or `parallel` must be * set. `input` is required when `agent` is set. The step factory * (`ai.step`) throws a `WorkflowError` at authoring time when these * invariants are broken. * * **Lifecycle order (happy path).** * ``` * skip? → before? → (run | agent | parallel) → output?.extract (+ schema) → after? * ↓ * nextStep? * ``` * Retry wraps the middle block (`before → run|agent → output → after`); * `skip` / `nextStep` / `onCancel` sit outside the retry loop. */ type StepDefinition, TContext = unknown> = { /** * Unique step name. Must be unique within the workflow (including * across parallel children). Used as the key in `ctx.steps` and * `report.steps`, as the routing target for `{ goto: "..." }`, and * as the identifier in every event payload. */ name: string; /** * Skip guard. Evaluated BEFORE `before`. Returns `true` to bypass * the step entirely — `before` / `run` / `agent` / `output` / `after` * are never invoked, the snapshot is marked `{ skipped: true, * status: "skipped" }`, and `nextStep` still fires so routing can * react to the skip. */ skip?: (ctx: WorkflowContext) => boolean | Promise; /** * Pre-work hook. Runs inside the retry loop before `run` / `agent`. * Mutate `ctx.state` here for setup (fetching prerequisites, setting * derived values, validating preconditions). A throw fails the * attempt and triggers retry (if configured). */ before?: (ctx: WorkflowContext) => void | Promise; /** * Core work for non-agent steps. Receives the context and may return * any value; the return value is exposed to `output.extract` via * `ctx.agentResult`. Mutually exclusive with `agent` and `parallel`. */ run?: (ctx: WorkflowContext) => unknown; /** * Agent to execute for this step. Mutually exclusive with `run` and * `parallel`. The agent receives `input(ctx)`'s prompt (and any * options from the same call); its `AgentResult` is stored on * `ctx.agentResult` for downstream `output.extract` / `after` use * and aggregated into `report.steps[name].executionResult`. */ agent?: AgentContract; /** * Builds the prompt + per-call options for `agent`. Required when * `agent` is set. Runs once per retry attempt so the prompt can * incorporate fresh context (e.g., loop-back feedback on * `ctx.state.qaFeedback`). */ input?: (ctx: WorkflowContext) => StepAgentInput | Promise; /** * Output extraction spec. Runs after `run` / `agent`. Without this * the step's `output` in the snapshot is `undefined`. A throw (or * schema validation failure) fails the attempt and triggers retry. */ output?: StepOutputSpec; /** * Post-work hook. Runs inside the retry loop AFTER `output.extract`. * Intended for side effects (save to DB, send notification, fire * webhook). A throw fails the attempt and triggers retry — so keep * `after` work idempotent. */ after?: (ctx: WorkflowContext) => void | Promise; /** * Step-level routing. Runs OUTSIDE the retry loop, once per step * completion — but ONLY for `completed` and `skipped` outcomes. A * step whose retries are exhausted (`failed`) bypasses `nextStep` * and goes through `onFailure` (if defined) or halts the workflow. * Return `{ goto, end, void }` — step-level wins over workflow-level; * `void` falls through. Errors here are NOT retried: routing is * authoritative, so a throw terminates the workflow with * `RoutingError`. */ nextStep?: (ctx: WorkflowContext) => NextStepResult | Promise; /** * Failure-routing hook. Runs OUTSIDE the retry loop, once after all * retry attempts are exhausted, before the workflow halts. Return: * - `{ goto: "name" }` to redirect to a recovery step (workflow * continues; final status becomes `completed` if recovery succeeds) * - `{ end: true }` to terminate cleanly without an error * - `void` (or omit) to halt the workflow with the original * `StepFailedError` — same as if no `onFailure` were defined * * The step's snapshot retains `status: "failed"` and `error` for * forensic trace even when `onFailure` recovers the run. Errors * thrown from `onFailure` are NOT retried — they terminate the * workflow with `RoutingError` (mirrors `nextStep`). */ onFailure?: (ctx: WorkflowContext, error: AIError) => NextStepResult | Promise; /** * Cancellation cleanup. Runs best-effort when the workflow is * aborted while this step is in-flight. Typical use: release a * reservation, flush a partial write, cancel an external job. Errors * thrown from `onCancel` are swallowed + logged — never rethrown. */ onCancel?: (ctx: WorkflowContext) => void | Promise; /** * Retry configuration. Overrides workflow-level `defaultRetry`. * `false` disables retry entirely. See `RetryConfig` for backoff * options. Retry wraps `before → run|agent → output → after`; * `AbortError` short-circuits the loop. */ retry?: RetryConfig | false; /** * Parallel children — run concurrently, each starting from a snapshot * of the parent `ctx.state`. After every child settles, their * resulting states are merged back into the parent in **declaration * order** (the order children appear in this array), so a key written * by more than one child deterministically resolves to the * **last-declared** child's value — regardless of which child finished * first. Supply {@link mergeState} to customize conflict resolution * (e.g. sum counters, concat arrays). Mutually exclusive with `run` * and `agent`. The parent's snapshot is atomic: written only after * every child settles. Children are addressable at both * `ctx.steps.` and `ctx.steps..steps.`. */ parallel?: StepDefinition[]; /** * Conflict resolver for {@link parallel} children that write the same * `ctx.state` keys. Called once per child, in **declaration order**, * with the accumulator (the parent state being built up), that child's * resolved state, and the child's name. Mutate `acc` in place. * * Omit it to keep the deterministic default (last-declared child wins * per key — a plain `Object.assign` in declaration order). Supply it * for advanced merges: * * ```ts * mergeState: (acc, childState) => { * acc.total = (acc.total ?? 0) + (childState.total ?? 0); // sum * } * ``` * * Only consulted for parallel steps; ignored otherwise. */ mergeState?: (acc: TState, childState: TState, childName: string) => void; /** * Per-step event handlers — fires alongside the workflow-level * `workflow.step.*` events. Scope is step-local (no `workflow.` * prefix); payload includes the step name for symmetry with the * workflow-level events. */ on?: Partial<{ [K in keyof StepLocalEvents]: (payload: StepLocalEvents[K]) => void }>; }; //#endregion export { StepAgentInput, StepDefinition, StepLocalEvents, StepOutputSpec }; //# sourceMappingURL=step.contract.d.mts.map