# Workflow authoring API reference

> Generated by `pnpm docs:authoring` from public declarations and TSDoc in `src/flow`. Do not edit directly.

For the guided introduction, see [Authoring workflows](authoring.md).

## Steps

### `CreateAgentStepOptions`

```ts
interface CreateAgentStepOptions<TInputSchema extends TSchema | undefined, TOutputSchema extends TSchema> { name: string description?: string input?: TInputSchema output?: TOutputSchema model?: string prompt: (args: AgentPromptArgs<InferInput<TInputSchema>>) => string asks?: boolean retry?: RetryPolicy maxOutputRepairs?: number maxDurationMs?: number | ((args: { ctx: RunContext }) => number) optional?: boolean maxTokens?: number background?: boolean resumable?: boolean | string | ((args: { ctx: RunContext }) => string) }
```

Agent-step configuration, including model selection, output repair, budgets, isolation, and resumability.

Remarks: An acting agent omits `output`. A reporting agent declares `output`; an asking agent additionally sets
`asks: true`. Asking cannot be combined with `background`. Static resumable keys cannot be shared by
executions that may overlap.

### `createAgentStep`

```ts
createAgentStep<TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema = TSchema>(options: CreateAgentStepOptions<TInputSchema, TOutputSchema>): AgentStep
```

Creates an agent step that acts, reports structured output, or interactively asks for information.

Remarks: Omit `output` for an acting step whose result is its side effects. Declare `output` for a reporting
step; the agent must call `workflow_submit_result` with a matching value. Add `asks: true` when it may submit
questionnaire batches before its result. Never combine `asks: true` with `background: true`.

Example: ```ts
const review = createAgentStep({
  name: "review",
  input: changeSchema,
  output: reviewSchema,
  prompt: ({ input }) => `Review ${input.path}`,
})
```

### `CreateQuestionnaireStepOptions`

```ts
interface CreateQuestionnaireStepOptions<TOutputSchema extends TSchema> { name: string description?: string output: TOutputSchema questionnaire?: Questionnaire }
```

Configuration for deterministic, schema-driven user input collection.

### `createQuestionnaireStep`

```ts
createQuestionnaireStep<TOutputSchema extends TSchema>(options: CreateQuestionnaireStepOptions<TOutputSchema>): QuestionnaireStep
```

Questionnaire step (spec §2.4): collect structured input to satisfy an annotated target `output`
schema. Deterministic and LLM-free — the framework derives a questionnaire from `output` (or uses
the `questionnaire` override), blocks with it, and on answers reassembles + validates them into
`output`.

For elicitation — an agent that composes and re-batches questions until it can satisfy `output` —
use `createAgentStep({ asks: true })` instead.

Example: ```ts
const collectTarget = createQuestionnaireStep({
  name: "collect-target",
  output: Type.Object({ environment: Type.String({ title: "Environment" }) }),
})
```

### `CreateStepOptions`

```ts
interface CreateStepOptions<TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined> { name: string description?: string input?: TInputSchema output?: TOutputSchema retry?: RetryPolicy maxDurationMs?: number | ((args: { ctx: RunContext }) => number) optional?: boolean run: StepRunFn<InferInput<TInputSchema>, InferOutput<TOutputSchema>> }
```

Configuration shared by a function step, including retry, duration, and optional-failure controls.

Remarks: `retry.maxRetry` counts attempts after the first. `optional` should only be used when downstream
nodes do not require this step's output, because a final failure produces `undefined`.

### `createStep`

```ts
createStep<TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined>(options: CreateStepOptions<TInputSchema, TOutputSchema>): FunctionStep
```

Creates a TypeScript function step whose input and output types are inferred from its TypeBox schemas.

Remarks: The engine validates `input` before calling `run` and validates the returned value against `output`.
The callback may use `ctx` for earlier results, `abortSignal` for cancellation, and `logger` for
structured run-log messages.

Example: ```ts
const summarize = createStep({
  name: "summarize",
  input: Type.Array(Type.String()),
  output: Type.String(),
  run: ({ input }) => input.join("\n"),
})
```

## Data Flow

### `ScopeFrame`

```ts
interface ScopeFrame { readonly kind: "loop" | "foreach" | "branch-arm" | "parallel" | "workflow" readonly name: string readonly iteration?: number readonly itemIndex?: number readonly itemCount?: number readonly input: unknown }
```

One enclosing construct's position and input, exposed through RunContext.scope
(iteration-context spec, Feature 1). Values are pure data derived from the engine's walk state:
deterministic, identical on resume (rebuilt as re-entry descends), and identical regardless of
concurrency interleaving. Retry attempt is deliberately NOT here (spec 1.5): loop iteration and
retry attempt are different axes, and conflating them would invite resume keys that fork per retry.

### `RunContext.scope`

```ts
RunContext.scope(name?: string): ScopeFrame | undefined
```

The enclosing construct frames (iteration-context spec 1.2/1.6). Called with no argument it
returns the NEAREST enclosing frame; called with a construct name it walks outward to that
construct (a nested loop-in-foreach is addressable by name, not just the innermost). Returns
`undefined` at the top level, or when no enclosing construct carries the given name.

### `RunContext.getStepResult`

```ts
RunContext.getStepResult<T = unknown>(stepName: string): T | undefined
```

Look up a prior step's or construct's output by its BARE name (names-only addressing, spec 4.1) —
resolved lexically to the nearest enclosing scope that declares it, walking outward from the
calling step's own scope to the root. A declared name whose step has not been reached, or was
skipped, reads `undefined` — a structural fact, not an error. A name NO enclosing scope declares
THROWS (a provable wiring bug, spec 4.2), as does any argument carrying path syntax (`/`, `#`,
`@`) — the path form was removed outright. Paths remain identity in the event log and resume
addressing; they are no longer an authoring query language.

### `RunContext.getInitData`

```ts
RunContext.getInitData<T = unknown>(): T | undefined
```

The workflow's initial input, or `undefined` when the workflow has no input schema.

### `CreateWorkflowOptions`

```ts
interface CreateWorkflowOptions<TInputSchema extends TSchema | undefined = undefined> { name: string description?: string input?: TInputSchema defaultModel?: string maxConcurrency?: number }
```

Root workflow configuration. The optional TypeBox `input` schema validates initial run data and
infers its author-facing type; `maxConcurrency` defaults to 4.

### `WorkflowBuilder.then`

```ts
WorkflowBuilder.then(step: StepDefinition): WorkflowBuilder
```

Append a step node in sequence. Its input is the previous node's output and is validated against
the step's input schema.

### `WorkflowBuilder.commit`

```ts
WorkflowBuilder.commit(): WorkflowDefinition
```

Finalize and validate the workflow definition.

### `createWorkflow`

```ts
createWorkflow<TInputSchema extends TSchema | undefined = undefined>(options: CreateWorkflowOptions<TInputSchema>): WorkflowBuilder
```

Starts a fluent workflow definition and returns its builder. Append at least one node and call
`.commit()` to obtain a loadable WorkflowDefinition.

Remarks: Linear hand-off passes every node's output to the next node. Use `ctx.getStepResult()` for a
non-adjacent result, `ctx.getInitData()` for the root input, and `ctx.scope()` for enclosing
loop/foreach/branch/parallel input. Node names must be unique and cannot contain `/`, `#`, or `@`.

Example: ```ts
export default createWorkflow({ name: "review", input: requestSchema })
  .then(loadRequest)
  .then(reviewRequest)
  .commit()
```

Throws: If the workflow is empty, names are invalid or duplicated, concurrency exceeds its ceiling,
or incompatible execution options are combined.

## Map

### `MapFn`

```ts
type MapFn<TOutput = unknown> = (ctx: RunContext) => TOutput
```

A `.map()` transform (spec §3.7): derives the next step's input purely from the run context —
prior step outputs (`getStepResult`) and workflow init data (`getInitData`). Pure and
deterministic; it has no host, network, or LLM access beyond `ctx`. The returned value is
validated by the downstream step's input schema, so a map declares no schema of its own.

### `MapOptions`

```ts
interface MapOptions { name?: string }
```

Options for a `.map()` construct.

### `WorkflowBuilder.map`

```ts
WorkflowBuilder.map(transform: MapFn, options?: MapOptions): WorkflowBuilder
```

Insert a pure transform whose result becomes the next node's input via the linear hand-off
(spec §3.7). Reads earlier, non-adjacent outputs via `ctx.getStepResult` / `ctx.getInitData`.

## Loop

### `LoopCondition`

```ts
type LoopCondition = (ctx: RunContext, lastOutput: unknown) => boolean
```

A pure loop predicate (spec §3.3) over the run context and the body's most recent output.
`lastOutput` is the RAW body output (loop-feedback spec 2.4): it may be `undefined` when the round
produced nothing (a failed `optional` tail) even though the fed-back value passes through defined —
a predicate may legitimately need to know the round came up empty. The fed value itself is readable
as `ctx.scope(loopName)?.input`.

### `LoopOptions`

```ts
interface LoopOptions { name?: string maxIterations?: number }
```

Options shared by `.dowhile()` and `.dountil()`; `maxIterations` defaults to 100.

### `WorkflowBuilder.dowhile`

```ts
WorkflowBuilder.dowhile(body: WorkflowDefinition, condition: LoopCondition, options?: LoopOptions): WorkflowBuilder
```

Loop (spec §3.3): run `body`, then repeat while `condition` holds.

**Feedback** (loop-feedback spec, Feature 2): each iteration's body receives the PREVIOUS
iteration's body output as its input — the first iteration receives the value flowing into the
loop from upstream. It is ordinary, schema-validated step I/O, so the body's input and output
schemas must agree (checked at `.commit()` where both are declared). An iteration that produces
no output (a failed `optional` tail) passes its input through unchanged, and the loop's own
output is the final effective value — readable downstream by the loop's bare name.

### `WorkflowBuilder.dountil`

```ts
WorkflowBuilder.dountil(body: WorkflowDefinition, condition: LoopCondition, options?: LoopOptions): WorkflowBuilder
```

Run `body`, then repeat until `condition` holds. Feedback follows WorkflowBuilder.dowhile.

## Foreach

### `ForeachSelector`

```ts
type ForeachSelector = (ctx: RunContext) => readonly unknown[]
```

A pure item selector for a foreach (spec §3.4): derives the collection to iterate from the run
context. Must be side-effect-free and deterministic — a resume re-runs it and relies on it
yielding the same array so recorded per-item outputs line up by index.

### `ForeachOptions`

```ts
interface ForeachOptions { name?: string concurrency?: number feedback?: boolean }
```

Options for `.foreach()`; `concurrency` defaults to 1. Feedback threads each body's output into the
next item and therefore requires `concurrency: 1`; the current item remains available through
`ctx.scope(foreachName)?.input`.

### `WorkflowBuilder.foreach`

```ts
WorkflowBuilder.foreach(body: WorkflowDefinition, selector: ForeachSelector, options?: ForeachOptions): WorkflowBuilder
```

Foreach (spec §3.4): run `body` once per item selected by `selector` (pure), with the item as the
body's input. `options.concurrency` (default 1) bounds how many items run at once. Output is the
array of per-item outputs, in item order — independent of completion order.

**Author contract (spec §8.3, "non-overlapping side effects"):** at `concurrency > 1`, items run
genuinely concurrently — the engine does not, and cannot, know what a step or its subagent will
touch, so it enforces nothing here. Give each item's body its own files/branches/external
resources; anything shared across items (two agents editing the same file, say) must be sequenced
— either keep `concurrency` at 1, or restructure so the shared resource is touched outside the
fan-out.

## Parallel

### `ParallelOptions`

```ts
interface ParallelOptions { name?: string }
```

Options for a `.parallel()` construct.

### `WorkflowBuilder.parallel`

```ts
WorkflowBuilder.parallel(arms: readonly StepDefinition[], options?: ParallelOptions): WorkflowBuilder
```

Parallel (spec §3.5): structural fan-out over independent STEPS — every arm runs concurrently
against the same input, bounded only by the workflow ceiling (spec §3.6). Output is an object
keyed by each arm's own step name, independent of completion order.

**Author contract (spec §8.3, "non-overlapping side effects"):** every arm runs genuinely
concurrently — the same rule as `.foreach`'s doc above applies per arm here: no two arms may touch
the same file, branch, or external resource, since the engine has no way to detect or prevent two
concurrent agents rewriting the same working-tree state. Sequence anything that shares state with
`.then()` instead of putting it in the same `.parallel([...])`.

## Branch

### `BranchCondition`

```ts
type BranchCondition = (ctx: RunContext) => boolean
```

A pure branch predicate over the run context; it must be side-effect-free to keep transitions deterministic.

### `BranchOptions`

```ts
interface BranchOptions { name?: string }
```

Options for a `.branch()` construct.

### `BranchArmSpec`

```ts
type BranchArmSpec = readonly [BranchCondition, WorkflowDefinition]
```

One `.branch()` arm: a pure condition paired with the committed sub-workflow to run when it holds.

### `WorkflowBuilder.branch`

```ts
WorkflowBuilder.branch(arms: readonly BranchArmSpec[], options?: BranchOptions): WorkflowBuilder
```

Multi-match branch (spec §3.2): every arm whose condition holds runs sequentially; the node's
output is an object keyed by the executed arm names (each arm name is its body's workflow name).

## Workflow

### `NestedWorkflowOptions`

```ts
interface NestedWorkflowOptions { name?: string }
```

Options for a `.workflow()` nested-workflow construct.

### `WorkflowBuilder.workflow`

```ts
WorkflowBuilder.workflow(subWorkflow: WorkflowDefinition, options?: NestedWorkflowOptions): WorkflowBuilder
```

Nested workflow (spec §2.3/§11): run a committed sub-workflow's nodes here, transparently folding
into the parent run/log. Output is the sub-workflow's final output. Every step/node name must be
unique across the flattened tree, so nesting the *same* sub-workflow twice is a `commit()` error.

## Advanced Agent

### `RetryPolicy`

```ts
interface RetryPolicy { readonly maxRetry: number readonly backoffMs?: number }
```

Unified repeat policy for a step (spec §9.1). Covers thrown errors and invalid output uniformly;
an input-schema violation is a deterministic wiring failure and is never retried.

### `CreateAgentStepOptions`

```ts
interface CreateAgentStepOptions<TInputSchema extends TSchema | undefined, TOutputSchema extends TSchema> { name: string description?: string input?: TInputSchema output?: TOutputSchema model?: string prompt: (args: AgentPromptArgs<InferInput<TInputSchema>>) => string asks?: boolean retry?: RetryPolicy maxOutputRepairs?: number maxDurationMs?: number | ((args: { ctx: RunContext }) => number) optional?: boolean maxTokens?: number background?: boolean resumable?: boolean | string | ((args: { ctx: RunContext }) => string) }
```

Agent-step configuration, including model selection, output repair, budgets, isolation, and resumability.

Remarks: An acting agent omits `output`. A reporting agent declares `output`; an asking agent additionally sets
`asks: true`. Asking cannot be combined with `background`. Static resumable keys cannot be shared by
executions that may overlap.

### `CreateStepOptions`

```ts
interface CreateStepOptions<TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined> { name: string description?: string input?: TInputSchema output?: TOutputSchema retry?: RetryPolicy maxDurationMs?: number | ((args: { ctx: RunContext }) => number) optional?: boolean run: StepRunFn<InferInput<TInputSchema>, InferOutput<TOutputSchema>> }
```

Configuration shared by a function step, including retry, duration, and optional-failure controls.

Remarks: `retry.maxRetry` counts attempts after the first. `optional` should only be used when downstream
nodes do not require this step's output, because a final failure produces `undefined`.

## Interactive

### `CreateInteractiveStepOptions`

```ts
interface CreateInteractiveStepOptions<TInputSchema extends TSchema | undefined, TRequestSchema extends TSchema, TOutputSchema extends TSchema> { name: string description?: string input?: TInputSchema request: TRequestSchema output: TOutputSchema buildRequest: (args: InteractionRequestArgs<InferInput<TInputSchema>>) => Static<TRequestSchema> render: ( args: InteractionRenderArgs<Static<TRequestSchema>>, ) => Static<TOutputSchema> | undefined | Promise<Static<TOutputSchema> | undefined> }
```

Configuration for a workflow-defined interaction rendered by the attended PI host.

### `createInteractiveStep`

```ts
createInteractiveStep<TInputSchema extends TSchema | undefined = undefined, TRequestSchema extends TSchema = TSchema, TOutputSchema extends TSchema = TSchema>(options: CreateInteractiveStepOptions<TInputSchema, TRequestSchema, TOutputSchema>): InteractiveStep
```

Create a resumable interactive step whose request and response types are inferred from TypeBox.

The engine remains UI-free: it persists the request and returns `blocked`. Only an attended host
invokes `render`, after the engine call has ended. Offline callers can inspect the pending request
and submit a response directly without loading PI UI.
