Skip to main content
The workflow builder turns a chain of method calls into a static, serializable workflow graph. createStep() declares a code step; createWorkflow() returns a builder whose chain methods add steps and containers and whose .commit() validates the graph and returns a LuaWorkflow. Predicates and data bindings are built with the helpers on this page, never with closures: a function where a predicate, template or mapping is expected fails the build. Workflows live anywhere under src/ and may be listed on LuaAgent under workflows. Starting and steering runs from code is Workflows; writing a workflow end to end is the authoring guide. Requires lua-cli 3.36.0 or later.

Quick example

Two code steps, an agent step, and a conditional agent step placed inside the switch by its id:
src/workflows/ticket-triage.ts

Workflow definition

createWorkflow()

Returns a LuaWorkflowBuilder for the given config. Validation of the config happens here; validation of the graph happens in .commit().
string
required
Server identifier, and what lua workflows and Workflows.start() address. Must match ^[a-z][a-z0-9-_]*$.
string
Shown in lua workflows list.
ZodType
required
Validated on every start; a failing input is refused.
ZodType
Validates the run output.
ZodType
Types ctx.state. The run-scoped store holds at most 64 KB.
{ maxCredits?: number; maxSteps?: number; maxDurationSeconds?: number }
Per-run caps. What maxCredits counts depends on the organization’s plan, and the name is historical — on a seat plan the unit is actions, not credits, and lua workflows status says which.A tier: 'job' agent step is charged per model reply on the first two rows, so a long coding turn can be worth many units — budget it by replies, not by steps (What a Job step costs). Before a Job step is dispatched the platform checks that one heavy call on that step’s model still fits, and parks the run rather than spawning a container it cannot pay for.maxDurationSeconds defaults to 604 800 (7 days), or 2 592 000 (30 days) when the graph contains an approval, a signal wait, or a step with suspendSchema. A run that exhausts a dimension stops and parks on a budget gate; raise it with lua workflows raise-budget. A run whose last step finished past the cap completes instead, marked budget.exceeded.
'allow' | 'forbid'
'forbid' refuses another start while a run of this workflow is in flight (RUNS_IN_FLIGHT, with the blocking run id). As of lua-cli 3.33.0 and the current platform that refusal is not applied when a run is started from the CLI, the SDK, the REST API, or a trigger, so treat overlapping runs as possible until the fix ships; a scheduled fire is still skipped while a run is in flight.
{ roles: string[]; users?: string[]; ownerBypass?: boolean }
Who may read run outputs. roles must be non-empty; at most 20 roles and 50 user ids.
JobSchedule & { runAs?: 'installer' | 'system' }
The same { type: 'cron' | 'interval' | 'once', … } shape as a LuaJob. runAs only affects the copy an agent template installs: 'installer' (default) runs as the person who installed, 'system' runs with no person attached.
Record<string, unknown>
Literal run input for every scheduled fire. Required when inputSchema has required keys (schedule-input-required, a push blocker) and validated against it (schedule-input-invalid).
{ maxOccurrences?: number }
Missed occurrences to replay when a paused schedule is re-enabled; integer 1–200.
{ maxSilenceMinutes?: number; alertAfterConsecutiveFailures?: number; notify?: 'app' | 'email' | 'emailApp' }
Alert the schedule’s owner when no scheduled run has succeeded for maxSilenceMinutes (5–43 200), after alertAfterConsecutiveFailures failed scheduled runs in a row (1–3 for a workflow), or while the platform has turned the schedule off. notify defaults to emailApp. Needs schedule and at least one threshold. Compiling it needs the next lua-cli release; an older CLI drops it. See Watch schedule health.
WorkflowGoalEnvelope
{ objective, judge: { agentId, role?, schema }, cadence: JobSchedule[], maxRuns, budget?, maxTotalCredits?, initialState? }. See goals and schedules.
WorkspaceSpec
A volume for Job-tier steps: { kind: 'git', repo, ref?, credentialsRef?, sizeGb?, ttlHours?, verify?, keepArtefacts?, backend? } or { kind: 'empty', sizeGb?, ttlHours?, keepArtefacts?, backend? }. repo and ref accept env.template(). See Job tier.
Array<{ key: string; integrationType: string; required?: boolean; description?: string }>
Connection keys the workflow acts through, resolved against the agent that runs it. key matches ^[a-z][a-z0-9_-]{0,63}$ and is what workspace.credentialsRef and a step’s requiredConnections name. See connections.
form is set by the compiler; never write it. Errors — invalid-workflow-name; invalid-envelope for an empty outputVisibility.roles or a backfillOnEnable.maxOccurrences outside 1–200; cap-exceeded for more than 20 roles or 50 users; env-template-secret-key when schedule or workspace carries env.template() on a key ending in SECRET, TOKEN, KEY or PASSWORD.

defineWorkflow()

Sugar for createWorkflow followed by a build callback that must end in .commit().
Errors — invalid-envelope when the callback returns anything other than the committed LuaWorkflow.

Code steps

createStep()

Declares a code step and returns it unchanged, typed by its schemas. The object is placed in a chain with .then(step) or as a container arm.
src/workflows/steps/sendEmails.ts
string
required
Unique within the workflow; ^[a-z][a-zA-Z0-9_-]{0,63}$. The characters [, #, . and : are reserved.
string
Shown in run listings.
ZodType
required
The step input after bindings are applied.
ZodType
required
The engine validates the returned output; a mismatch fails the attempt.
(ctx: WorkflowStepContext) => Promise<output>
required
The step body. After a resume it runs again from the top with ctx.resumeData set.
number
default:300
Integer ≥ 1. Worker tier allows up to 600; tier: 'job' allows up to 14 400 and defaults to 3600. An attempt that runs over is failed with TIMEOUT and counts toward retry.maxAttempts.
RetryPolicy
default:"{ maxAttempts: 1 }"
{ maxAttempts: 1–20; backoffSeconds?: number; backoff?: 'fixed' | 'exponential'; maxBackoffSeconds?: number }. Backoff is an engine-side wait, not a sleep in your code; 'exponential' doubles backoffSeconds per attempt up to maxBackoffSeconds (default 3600), which is only accepted with 'exponential'. A timed-out attempt is retried under this policy like a thrown error.
'none' | 'external'
default:"'none'"
'external' parks the step for a person instead of retrying it when a platform fault interrupts it.
'fail' | 'continue' | 'park'
default:"'fail'"
What the final failure does to the run: fail it, continue with null as this step’s output, or park the run on an exception gate for a person to retry, skip, complete or fail the step.
string[]
Declared connection keys or connection ids the step needs.
ZodType
Types the payload passed to ctx.suspend().
ZodType
Types ctx.resumeData, the data a person supplies to resume the step.
number
default:168
Deadline for a ctx.suspend() park; at most 720.
WorkflowBusinessHours
{ tz, calendar?: 'mon-fri' | { days, start, end, holidays? } }. Deadlines count business time.
'fail' | 'cancel-run'
What a missed resumeTimeoutHours does.
'job'
Run the step as a Job-tier pod. Implied by workspace.
{ mount: 'rw' | 'ro'; isolation?: 'shared' | 'worktree' }
Mount the workflow’s workspace. Requires tier: 'job' or no tier.
'small' | 'medium' | 'large'
Job-tier pod size; default 'small'.
WorkflowJobToolId[]
'shell' | 'read' | 'write' | 'edit' | 'glob' | 'grep' | 'git' | 'gh' | 'fetch' | 'ripwire'. Job tier only.
Errors — invalid-step when the argument is not an object or has no execute; invalid-step-id when id is outside the grammar. When the step is placed in a chain: duplicate-step-id for a second object with the same id; workspace-requires-job-tier; cap-exceeded for jobTools without the Job tier or retry.maxAttempts over 20; invalid-envelope for a missing, non-integer or sub-1 retry.maxAttempts; backoff-invalid; timeout-out-of-range; timeout-exceeds-tier over 600 s on the worker tier; job-timeout-exceeds-cap over 14 400 s.

Step context

execute receives one object.

WorkflowStepResultError

Thrown by ctx.getStepResult(). Check err.code, not instanceof: a step runs in its own realm.
'STEP_RESULT_NOT_ANCESTOR' | 'STEP_RESULT_TOO_LARGE' | 'STEP_RESULT_OFFLOADED'
Not an upstream step; the output could not travel with the step (4 MiB budget per step); or the output is stored offloaded (over 256 KB) and could not be hydrated. For the last two, bind the value through the step’s input or read it through artefacts.
string
The requested step.
number
The output’s serialized size, for the size codes.
string
Why hydration failed, for STEP_RESULT_OFFLOADED.

Chain methods

Every method returns the builder. A StepRef is a createStep object or a string naming an entry declared elsewhere in the chain; a ContainerArm is a StepRef or a [mapConfig, stepRef] pair whose step runs with the map as its input.

then()

Appends a code step, or places a declared entry sequentially by id.

parallel()

Runs 2 to 16 arms concurrently. The output is { [stepId]: output }. An arm may be a [mapConfig, stepRef] pair, or the id of an approval or waitForSignal, which then parks beside its siblings.
merge applies to Job-tier worktree arms. Errors — cap-exceeded outside 2–16 arms; container-arm-empty for a pair without a step; mapping-placement for a map arm.

switch()

Runs the first arm whose predicate is true; otherwise runs when none is. Without otherwise, a run with no true arm continues past the switch. An arm target may be a map placed by id. When the switch is the last entry, the taken arm’s output is the run output.
Errors — invalid-envelope with no arms; closure-predicate for a function in the predicate slot.

branch()

Runs every arm whose predicate is true. exclusive: true behaves like switch without otherwise.

foreach()

Runs the body once per item. The body receives each item as its input; the output is an array. An approval or waitForSignal body means one approval or wait per item.
TypedRef<unknown[]> | MapDescriptor | { initData: true; path?: string }
Where the items come from: step(x).path('list'), init('rows'), fromInit('rows'), { initData: true, path: 'rows' }, or a single-step fromStep(x, 'list'). Omitted, the body iterates the previous entry’s output. value, template, fromRequest, rows, fromKnowledge and a fan-in fromStep([…]) are refused (invalid-envelope); put a .map({ '': … }, { id }) before the foreach instead.
number
default:4
1–16.
number
default:256
1–20 000. A longer list fails the run; it is never truncated.
{ size: number }
Process items in chunks; size is an integer from 1 to maxItems. A chunked body cannot be an approval or signal wait.
{ perSecond?: number } | { perMinute?: number }
Exactly one of the two; perSecond at most 50, perMinute at most 3000.
Errors — cap-exceeded, chunk-size-invalid, rate-limit-invalid, invalid-envelope; mapping-placement for a [map, step] body.

dowhile() and dountil()

Repeats the body while (or until) the predicate holds. The body receives the previous iteration’s output. A loop body cannot be an approval, a signal wait, or a [map, step] pair.
number
default:100
Iteration cap.
number
Engine-side wait between iterations; integer 1–86 400.
Errors — loop-interval-out-of-range; node-type-unsupported-in-container; mapping-placement.

map()

Reshapes data at the top level of the chain. Each key is a mapping descriptor or a literal; the single key '' makes the output the descriptor’s value itself rather than an object.
The default id is map_<n>. Once a workflow has two or more maps every map needs an explicit id: .commit() warns map-id-required and lua push refuses. A map can be a switch arm or otherwise by id; it can never be a parallel, foreach or loop arm. Errors — closure-binding for a function in a descriptor slot.

sleep()

An engine-side wait of ms milliseconds; a literal, non-negative number. The default id is sleep_<n>.

sleepUntil()

Builds a wait until an ISO timestamp or a template() that resolves to one. The engine does not execute this step in 3.33.0: lua compile and lua push refuse it with node-type-unsupported-by-engine. Use sleep().

agentStep()

Declares an agent turn. The step’s output carries the reply as text; with outputSchema, the reply is validated and a mismatch fails the attempt.
string | EnvRefBinding
required
A member agent id, '$self' for the owning agent, or env.template('KEY').
string | TemplateBinding | EnvRefBinding
required
A literal, template('…'), or env.template('KEY').
ZodType
Validates the reply.
string
Model code for this turn, overriding the agent’s, or a model class: 'class/fast', 'class/balanced' or 'class/strong', resolved to one approved model when the step runs.
'classify' | 'extract' | 'transform' | 'draft' | 'research' | 'reason' | 'code' | 'judge'
What the step does, mapped to a model class. On a pushed definition it requires a model beside it (task-class-without-model); the map is applied when the agent composes a workflow. A step with neither taskClass nor model runs on the agent’s own model.
('vision' | 'structured' | 'largeContext' | 'codeExecution')[]
Traits the chosen model must have. Preferred over pinning a code when a capability is what the step needs.
'low' | 'medium' | 'high'
How hard a model would think on this step. Validated, recorded on the step and counted in the run estimate; a plain push never sends it to the model, lua push workflow --apply-effort marks the version so that it is sent where the platform forwards it, and a model whose reasoning is a plain on/off toggle ignores it in any case. The step row’s effortApplied records what reached the model.
string
Why this step asks for what it asks for. At most 160 characters of plain text, shown on the run card, never parsed. A composed step that carries none is given the platform’s own line.
{ connectionIds?: string[]; skillIds?: string[]; toolIds?: string[]; jobTools?: WorkflowJobToolId[] }
Tools offered on this turn; {} means none. jobTools needs tier: 'job'.
string
A static persona override for the turn.
number
Worker tier up to 600 (default 600 for an agent step); Job tier up to 86 400, and over 14 400 only with a workspace.
RetryPolicy
As for code steps.
'fail' | 'continue' | 'park'
As for code steps.
string[]
As for code steps.
'job'
Run the turn in a Job-tier pod. Implied by workspace.
{ mount: 'rw' | 'ro'; isolation?: 'shared' | 'worktree' }
Mount the workflow’s workspace.
'small' | 'medium' | 'large'
Pod size.
'claude-code' | 'generic'
Coding harness; Job tier only.
number
Coding turns per harness query; integer 1–500. Job tier only.
number
Harness messages per attempt; integer 1–5000, default 400. Job tier only.
number
Input tokens per attempt; integer 1 000 000–500 000 000, default 4 000 000. Job tier only.
'lua-agent-builder'[]
Claude Code plugins the coding turn loads; today only lua-agent-builder, at most 4, no repeats. Needs tier: 'job', the claude-code harness and shell among the job tools. The turn can stage changes to its agent but never make them live. Compiling it needs the next lua-cli release; an older CLI drops it. See Stage agent changes from a coding step.
{ agentId: string }
Beside plugins only: the agent the plugin works on instead of the step’s own. Not generally available; refused with target_not_granted unless switched on for your environment.
taskClass, requires, model: 'class/…', effort and modelReason are optional and additive, and nothing fills a default for any of them. A workflow that sets none behaves exactly as it did before they existed, and no already-pushed definition, published version or in-flight run is changed by them.
Errors — invalid-envelope without agentId; workspace-requires-job-tier; harness-requires-job-tier; max-turns-requires-job-tier; max-turns-invalid; cap-exceeded for toolScope.jobTools off the Job tier; timeout-exceeds-tier; job-timeout-exceeds-cap; long-job-requires-workspace; plugins-invalid for plugins or target that break the rules above; the retry errors; invalid-step-id.

specialistStep()

Declares a turn of the owning agent under an additive role: a reviewer, verifier or planner without a second agent. Ephemeral roles must be enabled for the organization or agent; otherwise lua push refuses with ephemeral-specialists-disabled.
{ name: string; instructions: string; tools: string[] } | { ref: string }
required
An inline role (instructions at most 4000 characters, tools at most 64 names from the agent’s own tools; [] means none) or a reference to a role in the organization’s library (role-ref-unknown at push when it does not exist).
string | TemplateBinding | EnvRefBinding
required
As for agentStep.
ZodType
As for agentStep.
string
As for agentStep, model classes included. taskClass, requires, effort and modelReason are not accepted here.
{ connectionIds?; skillIds?; toolIds? }
As for agentStep.
—
As for code steps; worker tier only.
Errors — invalid-envelope without a role or with a non-array tools; role-ref-and-inline when ref is combined with inline fields; ephemeral-role-too-long; cap-exceeded over 64 tools.

toolStep()

Calls a LuaTool directly, with an input built from mapping descriptors.
LuaMapConfig
The tool input; each key a descriptor or literal.
—
As for code steps; worker tier only.
Errors — invalid-envelope when tool is not a LuaTool value; closure-binding for a function inside input.

approval()

Declares a human decision. The approver sees the previous step’s output as the payload; the approval’s own output is what the next step reads.
src/workflows/refund.ts
string
required
Plain text shown on the approval card.
TemplateBinding
template('…') rendered on the card. A function is closure-binding.
WorkflowApproverSpec
default:"'creator'"
'creator', 'org-admins', { users: string[] }, { role }, { group }, or { governance: { policyId } }. users, role and group also accept a template() binding.
boolean
The person who started the run can never approve it. Not allowed with approver: 'creator', the default (approver-excludes-only-candidate at build and at push). Your organization’s approval policy can exclude the initiator on every approval; when that meets a creator approver, the platform asks the organization’s admins instead, minus the initiator, and records the escalation on the step as suspend.approverFallback.
number | TemplateBinding
default:168
1–720; a binding is resolved when the step parks. The range is not checked at compile, push, or deploy: a literal above 720 is accepted, and the step then parks with the 168-hour default rather than the value you wrote.
WorkflowSuspendTimeoutChain
default:"'deny'"
'deny', 'cancel-run', 'fail', 'continue', or an array of at most 3 { escalateTo, timeoutHours } hops ending in one of those.
'continue' | 'fail'
default:"'continue'"
By default a denial is data the next step reads.
WorkflowBusinessHours
Deadlines count business time.
boolean
Let the approver change the payload. Inferred true from a non-empty editablePaths; an explicit false beside paths is refused.
string[]
Paths the approver may edit: drafts, drafts[*], drafts[*].body, drafts[3].body, summary.title.
ZodType
Validates the edited payload. Requires editable.
{ edit: WorkflowApproverSpec; approve: WorkflowApproverSpec }
Whoever edits cannot be the one who approves. Requires editable.
—
Per-item approvals over an array in the payload. itemApprover may be { fromItem: '<field>' }; itemTimeout is { timeoutHours }.
Output — read by the next step; the payload the approver saw is not echoed back, so read it with getInitData() or getStepResult(), or project both into one input with a .map() before the step. Give that step’s inputSchema a .passthrough().
boolean
false on a denial and on a timeout under 'deny'.
'approved' | 'denied' | 'timed_out'
The decision word.
string
The approver’s note, else the decision word.
string
The note verbatim, when one was left (at most 2000 characters).
unknown
The payload as edited, when it was edited.
number
How many times the payload was edited before the decision.
{ id?: string; kind?: string; … }
The deciding principal.
—
Set when the deadline chain decided, how many hops fired, the evidence artifact ids, and the per-item rows.
Errors — invalid-envelope without title; approver-excludes-only-candidate; four-eyes-requires-editable; editable-path-invalid; escalation-chain-too-long; escalation-chain-not-terminal; closure-binding.

waitForSignal()

Declares a wait for a named signal delivered from outside the run with Workflows.signal, the CLI, or the REST API. A signal that arrives before the wait begins is parked and consumed when it does.
src/workflows/pr-review.ts
string
required
The signal name a sender must use. The builder only requires a non-empty string; the platform accepts a delivery only when the name is 1–64 characters of [a-zA-Z0-9_.-], so a name outside that grammar can never be satisfied; lua compile, lua push, and lua workflows deploy all accept it, so check the name yourself.
ZodType
Validates the payload; a mismatch is rejected at delivery.
number | TemplateBinding
Deadline for the wait. 1–720, checked when the step parks and on each escalation hop, not at build; a literal outside the range passes compile, push, and deploy.
'fail' | 'continue'
default:"'fail'"
'continue' completes the step with { received: false, timedOut: true }.
WorkflowBusinessHours
Deadlines count business time.
Array<'webhook' | 'api' | 'user' | 'agent'>
default:"['webhook', 'api', 'user']"
Which callers may deliver it.
Output — { payload, source, signalId, receivedAt } (receivedAt in epoch milliseconds), or { received: false, timedOut: true } after a timeout under 'continue'. Errors — invalid-envelope without signal.
Local runs only. Under lua test workflow, --signal completes the wait with the raw payload instead of the { payload, source, signalId, receivedAt } envelope. Read inputData.payload in the next step and expect it to be undefined offline.

workflow()

Declares a child run of another workflow, by LuaWorkflow value or by name on the same agent. The child runs with trigger: 'workflow'; nesting is at most 3 deep.
src/workflows/ticket-to-pr.ts
LuaMapConfig
The child run’s input, built from descriptors.
'inherit'
Mount the parent’s workspace in the child. The parent must declare one and the child must not.
RetryPolicy
default:"{ maxAttempts: 1 }"
Re-arms the row with a fresh child run when the child ends failed or timed_out on its own.
Errors — invalid-envelope without a workflow or name; workspace-inherit-without-parent-workspace; workspace-inherit-conflict; closure-binding for a function inside input.

commit()

Resolves placements, validates the graph, and returns the LuaWorkflow. The compiler observes every commit, so a workflow that is never committed is not compiled.
Errors — empty-graph when nothing was added; unknown-step-ref, duplicate-step-id, mapping-placement, container-arm-empty, node-type-unsupported-in-container from placement; invalid-envelope on a second commit(). Warnings (read with getBuildWarnings(), printed by lua compile) — map-id-required, hitl-duration-defaulted, schedule-input-required, schedule-input-invalid.

Placement rule

Every chain call appends one entry where it is called; nothing is moved afterwards. agentStep, specialistStep, toolStep, map(…, { id }), workflow, approval and waitForSignal are declarations: a container (parallel, switch, branch, foreach, a loop) that names the id as a string claims the declaration and places it inside itself, whether the declaration comes before or after the container in the chain. A declaration no container claims is placed where it is called, and .then('<id>') places one sequentially. A createStep object declares no id, so a string arm cannot name it; pass the object.
src/workflows/brief.ts
Here the two angle steps run inside the parallel, the map runs inside the switch arm, and lowConfidence is the switch’s otherwise; none of them appears a second time at the top level. The same id placed by two containers is duplicate-step-id; a string that names nothing is unknown-step-ref. An approval or signal wait may be claimed by parallel, switch, branch or foreach, never by a loop, a [map, step] pair, or a chunked foreach (node-type-unsupported-in-container).

References and predicates

References name a value in the run; predicates compare references and literals. Both are plain data, so a function in their place fails the build.
src/workflows/predicates.ts
A predicate that reads a step which is not upstream of the container resolves to an unresolved binding at run time and fails the step before dispatch.

Mapping descriptors

Descriptors bind step inputs, map() keys, tool-step inputs and child-run inputs to values in the run. A member that is not a descriptor is passed through as the literal it is.
src/workflows/mapping-demo.ts

Template placeholders

template('…') strings are rendered by the engine when the step is dispatched. Write them in single quotes so TypeScript never interpolates them. Objects and arrays render as JSON; null and a missing leaf render as an empty string. An unknown namespace (${inputData.*} is not one), a step that is not upstream, or a path through a non-object fails the step with binding_unresolved before it is dispatched. In an agent prompt each rendered value is wrapped in a data fence marked untrusted, so upstream output reaches the model as data rather than instructions.

Environment templates

env.template('KEY') returns an EnvRefBinding placeholder and is accepted for agentId, prompts, workspace.repo and ref, schedule fields and mapping values. lua push workflow resolves every key from the target environment; a missing key aborts the push. Keys ending in SECRET, TOKEN, KEY or PASSWORD are refused with env-template-secret-key; read those with env('KEY') inside execute. getEnvTemplateKeys() lists the keys a workflow carries.

LuaWorkflow

The value .commit() returns. Never construct it yourself.

Build errors

createWorkflow, createStep, the chain methods and .commit() throw LuaWorkflowBuildError, a subclass of Error with name: 'LuaWorkflowBuildError'.
LuaWorkflowBuildCode
One of the codes in the following table.
string
The message, with the hint appended after an em dash when one exists.
string
What to change, when the builder knows.
workspace-not-declared and approval-inside-container remain in the LuaWorkflowBuildCode union but are not thrown by the builder. lua compile and lua push run a second validation over the serialized graph and can refuse what the builder accepted: node-type-unsupported-by-engine for sleepUntil, template-reference-unresolved for a ${stepResults.<id>…} placeholder that names a step which is not upstream, connection-key-undeclared and connection-declaration-invalid for connection keys, ephemeral-specialists-disabled and role-ref-unknown for specialist roles, task-class-without-model for an agent step that names a taskClass without a model, and map-member-malformed (a warning) for a map member that carries descriptor keys without being an exact descriptor.

The workflow-builder subpath

lua-cli/workflow-builder exports the same builder, helpers, LuaWorkflow and LuaWorkflowBuildError without the runtime client behind them. It exists so a compiled workflow artifact is self-contained; your own files import from 'lua-cli'. Two constants are exported only there:

Script form

A workflow can also be a deterministic JavaScript module at src/workflows/<name>.workflow.script.js, driven by host helpers instead of this builder. Its shape and limits are described in the authoring guide; a workflow’s form is fixed by its first version.

Types

Exported from 'lua-cli': LuaWorkflow, LuaWorkflowBuilder, LuaWorkflowConfig, LuaWorkflowStep, WorkflowStepContext, WorkflowStepResultError, WorkflowArtefactMeta, WorkflowRunTrigger, WorkflowGoalEnvelope, RetryPolicy, StepRef, ContainerArm, AgentStepOptions, AgentToolScope, SpecialistStepOptions, ToolStepOptions, NestedWorkflowOptions, ApprovalOptions, WaitForSignalOptions, ForeachOptions, LoopOptions, TemplateLike, StepPathRef, DotPath, PathValue, WorkflowApproverSpec, WorkflowSuspendTimeoutChain, WorkflowSuspendTimeoutChainMember, WorkflowFourEyes, WorkflowBusinessHours, WorkflowStepWorkspace, WorkflowMergePolicy, WorkflowSuspendOnTimeout, WorkflowOutputVisibility, WorkspaceSpec, WorkflowWorkspaceBackend, WorkflowSpecialistRole, WorkflowJobToolId, WorkflowJobHarness, ReplyChannel, LuaWorkflowBuildError, LuaWorkflowBuildCode, LuaWorkflowBuildWarning, and the wire shapes LuaPredicate, MapDescriptor, LuaMapConfig, TemplateBinding, EnvRefBinding, TypedRef, Literal, PathOrLiteral, JsonSchema, KnowledgeBindingSpec, ArtefactRef, DatasetRef, SerializedWorkflowGraph.

See also