Generated by
pnpm docs:authoringfrom public declarations and TSDoc insrc/flow. Do not edit directly.
For the guided introduction, see Authoring workflows.
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<TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema = TSchema>(options: CreateAgentStepOptions<TInputSchema, TOutputSchema>): AgentStepCreates 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<TOutputSchema extends TSchema>(options: CreateQuestionnaireStepOptions<TOutputSchema>): QuestionnaireStepQuestionnaire 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<TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined>(options: CreateStepOptions<TInputSchema, TOutputSchema>): FunctionStepCreates 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(name?: string): ScopeFrame | undefinedThe 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<T = unknown>(stepName: string): T | undefinedLook 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<T = unknown>(): T | undefinedThe workflow's initial input, or undefined when the workflow has no input schema.
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(step: StepDefinition): WorkflowBuilderAppend a step node in sequence. Its input is the previous node's output and is validated against the step's input schema.
WorkflowBuilder.commit(): WorkflowDefinitionFinalize and validate the workflow definition.
createWorkflow<TInputSchema extends TSchema | undefined = undefined>(options: CreateWorkflowOptions<TInputSchema>): WorkflowBuilderStarts 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.
interface MapOptions { name?: string }Options for a .map() construct.
WorkflowBuilder.map(transform: MapFn, options?: MapOptions): WorkflowBuilderInsert 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.
type LoopCondition = (ctx: RunContext, lastOutput: unknown) => booleanA 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.
interface LoopOptions { name?: string maxIterations?: number }Options shared by .dowhile() and .dountil(); maxIterations defaults to 100.
WorkflowBuilder.dowhile(body: WorkflowDefinition, condition: LoopCondition, options?: LoopOptions): WorkflowBuilderLoop (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(body: WorkflowDefinition, condition: LoopCondition, options?: LoopOptions): WorkflowBuilderRun body, then repeat until condition holds. Feedback follows WorkflowBuilder.dowhile.
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.
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(body: WorkflowDefinition, selector: ForeachSelector, options?: ForeachOptions): WorkflowBuilderForeach (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.
interface ParallelOptions { name?: string }Options for a .parallel() construct.
WorkflowBuilder.parallel(arms: readonly StepDefinition[], options?: ParallelOptions): WorkflowBuilderParallel (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([...]).
type BranchCondition = (ctx: RunContext) => booleanA pure branch predicate over the run context; it must be side-effect-free to keep transitions deterministic.
interface BranchOptions { name?: string }Options for a .branch() construct.
type BranchArmSpec = readonly [BranchCondition, WorkflowDefinition]One .branch() arm: a pure condition paired with the committed sub-workflow to run when it holds.
WorkflowBuilder.branch(arms: readonly BranchArmSpec[], options?: BranchOptions): WorkflowBuilderMulti-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).
interface NestedWorkflowOptions { name?: string }Options for a .workflow() nested-workflow construct.
WorkflowBuilder.workflow(subWorkflow: WorkflowDefinition, options?: NestedWorkflowOptions): WorkflowBuilderNested 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.
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.
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.
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.
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<TInputSchema extends TSchema | undefined = undefined, TRequestSchema extends TSchema = TSchema, TOutputSchema extends TSchema = TSchema>(options: CreateInteractiveStepOptions<TInputSchema, TRequestSchema, TOutputSchema>): InteractiveStepCreate 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.