Step Configuration
Each step in a workflow is defined with the following properties:| Property | Purpose | Example |
|---|---|---|
id | Unique step identifier | "COLLECT_NAME" |
goal | Brief description (shown to the agent) | "Collect the user's name" |
instructions | Agent guidance (array of strings) | ["Ask for the user's full name."] |
inputs | Parameters to collect (JSON Schema) | [{name: "user_name", type: "string"}] |
on | Lifecycle hooks for actions | {start: [...], enter: [...], submit: [...]} |
next | Transition routing | ["NEXT_STEP"] or [{if: "...", id: "..."}] |
tools | Tool visibility control | {allow: [...], call: true} |
id
A unique string identifier for the step. Used in:- Transition routing (
nextentries) - Manual navigation (
go_to_step) - Debugging and logging
COLLECT_EMAIL, VERIFY_DOB).
goal
A brief description of what this step accomplishes. This text becomes the submit tool’s description, helping the agent understand the purpose of the current step.instructions
Guidance for the agent on how to execute this step. The current implementation expects an array of instruction strings, even for a single line:inputs
Defines the parameters the agent should collect before advancing. See Inputs Schema for full details.next
Controls workflow routing after successful submission. See Step Transitions for full details.tools
Controls which tools the agent can access during this step. See Tool Configuration for full details.Lifecycle Hooks
Lifecycle hooks execute actions at specific points during step execution. Define them in theon property:
Event Summary
| Event | When Triggered | Allowed Actions | Primary Use Cases |
|---|---|---|---|
on.start | First workflow turn, before the first on.enter | set, inc, say, call | One-time initialization, first-turn side effects |
on.enter | Entering a step (before input collection) | get, set, inc, say, call | Step welcome messages, pre-populate inputs |
on.presubmit | After agent submits, before validation | get, set, inc, save | Default missing values, data transformation |
on.submit | After validation passes | set, inc, say, save, call | Persist data, trigger side effects |
on.start
Executes once at workflow initialization, beforeon.enter on the first step.
Common uses:
- Initialize workflow-local state
- Queue first-turn
sayorcallactions - Seed values that the first step’s instructions depend on
on.start.
on.enter
Executes when the workflow enters this step, before the agent begins input collection. Common uses:- Display step-specific greetings or progress indicators
- Pre-populate inputs from existing data
- Initialize step-local counters
on.presubmit
Executes after the agent calls the submit tool, but before input validation runs. Restriction: Nosay or call actions (data mutation only). This prevents confusing UX where the agent says “Saved!” but validation then fails.
Common uses:
- Default missing optional fields
- Transform input values before validation
on.submit
Executes after validation passes, before evaluating transitions. Common uses:- Persist inputs to global variables
- Update counters
- Confirm submission to user
- Trigger external tool calls
Actions Reference
Actions are the operations executed during lifecycle events. All actions support an optionalif condition.
Action Summary
| Action | Description | Key Parameters | Available In |
|---|---|---|---|
say | Queue a verbatim message | text, role | on.enter, on.submit |
set | Set a variable value | name, value or valueFrom | all events |
inc | Increment a counter | name, by | all events |
get | Pre-populate inputs from variables | inputs, overwrite | on.enter, on.presubmit |
save | Persist inputs to global variables | name, inputs | on.presubmit, on.submit |
call | Queue a tool call | name, arguments | on.enter, on.submit |
say
Queues text that the agent must include verbatim in its response.| Parameter | Type | Default | Description |
|---|---|---|---|
text | string | required | Verbatim text to include |
role | string | "assistant" | Message role |
if | string | - | Optional condition |
say text is approximately 95%. The agent may occasionally add minor variations.
set
Sets a variable to a static value or computed expression.| Parameter | Type | Description |
|---|---|---|
name | string | Target variable path (e.g., local.x, user_name) |
value | any | Static value to set |
valueFrom | Expression | Dynamic value from expression (mutually exclusive with value) |
if | string | Optional condition |
inc
Increments a numeric variable. Creates the variable with value 0 if it doesn’t exist.| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | required | Variable to increment |
by | number | 1 | Amount to increment by |
if | string | - | Optional condition |
get
Copies values from existing variables into step inputs. Useful for pre-filling forms with known data.| Parameter | Type | Default | Description |
|---|---|---|---|
inputs | list[string] | all inputs | Which inputs to populate |
overwrite | boolean | false | If true, overwrite existing input values |
if | string | - | Optional condition |
save
Saves step inputs to global variables for use in later steps or after workflow completion.| Parameter | Type | Default | Description |
|---|---|---|---|
inputs | list[string] | all inputs | Which inputs to save |
name | string | input name | Target variable name (for single input) |
if | string | - | Optional condition |
call
Queues a pending tool call in the workflow state. The LLM is informed of the pending call on the next turn, but is not forced to execute it — it may respond to the user instead.| Parameter | Type | Description |
|---|---|---|
name | string | Tool name to call |
arguments | object | Template-rendered arguments |
autoPopulate | boolean or "required" | Planned call-prefill strategy hint (not yet enforced — see note below) |
if | string | Optional condition |
tools.call: true on the step. For the most deterministic behaviour, also restrict tools.allow to just the target tool name.
autoPopulate status: This field is accepted by the runtime but has no effect yet. Intended future semantics: true = prefer queuing the call when required arguments can be rendered; false = always let the LLM decide via tool_choice; "required" = queue the call only when all required arguments are available, otherwise fall back to tool_choice. This field does not automatically copy tool results into inputs.* or workflow variables — that is never done automatically.
Important: The workflow engine does not automatically copy the tool result into inputs.* or local.*. If later branching depends on the tool result, either:
- have the endpoint persist known values into
vars.*, or - have the agent resubmit normalized result fields through the submit tool.
Conditional Actions
All actions support an optionalif field for conditional execution:
if condition is evaluated as a JMESPath expression by default. See Expressions for syntax details.
Expressions
Expressions are used in conditions (if fields), computed values (valueFrom), and step transitions (next[].if).
JMESPath (Default)
JMESPath is the default expression language. When you write a string condition, it’s evaluated as JMESPath. Common Patterns:| Issue | Wrong | Correct |
|---|---|---|
| Boolean literals need backticks | flag == true | flag == \true“ |
| Number literals need backticks | count > 3 | count >= \3“ |
| String quotes | name == morning | name == 'morning' |
CEL (Common Expression Language)
CEL is useful when you need features JMESPath doesn’t support: arithmetic, string concatenation, or ternary operators. Syntax:inputs.address.city). Use JMESPath for nested structures.
When to Use Which
| Use Case | Recommended | Why |
|---|---|---|
| Simple condition checks | JMESPath | Default, no syntax overhead |
| String comparisons | JMESPath | Cleaner syntax |
| Nested property access | JMESPath | CEL doesn’t support it |
| Arithmetic | CEL | JMESPath can’t compute |
| String building | CEL | JMESPath can’t concatenate |
| Ternary logic | CEL | Clean conditional values |
Inputs Schema
Each step can defineinputs—parameters that the agent should collect before advancing.
Input Parameters
Theinputs array defines parameters that become the submit tool’s function schema:
Available Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Parameter name |
type | string | No | JSON Schema type: string, number, integer, boolean, object, array. Defaults to string |
description | string | No | Human-readable description shown to agent |
required | boolean | No | Whether parameter must be collected. Defaults to true |
enum | list[str] | No | Allowed values (agent constrained to these options) |
format | string | No | Format hint: date, time, date-time, email, uri, etc. |
pattern | string | No | Regex pattern for validation |
Accumulation Behavior
Inputs accumulate across multiple submissions until all required fields are collected:- New values overwrite: If the agent provides a new value for an existing input, it replaces the old value
- Missing values retained: Inputs not included in a call keep their previous values
- Empty string = missing: For string types,
""and whitespace-only strings are treated as empty
Reset on Transition
Important: Input variables (inputs.*) are cleared when transitioning to a new step. To preserve values across steps, use the save or set actions:
Templates
Template substitution lets you inject dynamic values into text fields using variable placeholders.Supported Syntax
| Syntax | Behavior | Example |
|---|---|---|
{{var}} | Replace with value, or empty string if missing | {{user_name}} → "Alice" or "" |
${var} | Replace with value, or empty string if missing | ${user_name} → "Alice" or "" |
${var=default} | Replace with value, or default if missing | ${name=Guest} → "Alice" or "Guest" |
{{var}} (handlebars style) for most cases.
Where Templates Are Expanded
| Field | Template Expansion | Notes |
|---|---|---|
instructions[] | Yes | Step instructions are rendered with current context |
say action text | Yes | Message text is rendered before queuing |
set action value | Yes (if string) | Static string values are rendered |
call action arguments | Yes (recursive) | All string values in arguments are rendered |
goal | No | Used as-is |
if conditions | No | Use expression evaluation instead |
valueFrom | No | Use expression evaluation (JMESPath/CEL) |
Available Variables
| Scope | Access Pattern | Example |
|---|---|---|
| Global variables | {{variable_name}} | {{user_name}} |
| Task-local state | {{local.key}} | {{local.retry_count}} |
| Step inputs | {{inputs.field}} | {{inputs.provided_dob}} |
| Nested values | {{scope.path.to.value}} | {{inputs.address.city}} |
Examples
In instructions:Step Transitions
Thenext field controls workflow routing after a step is successfully submitted.
Transition Basics
Evaluation Order
Transitions are evaluated in order. The first matching entry wins:- Iterate through
nextarray from first to last - For each entry: if it has an
ifcondition, evaluate it - If condition is true (or no condition), transition to that step
- If no entries match, the submission is treated as terminal and the workflow completes in place
if as the last item:
Terminal Steps
A step is terminal if:- It has no
nextfield, OR - It has
"next": [](empty array)
- The workflow is marked as completed
- The submit tool is removed from the agent’s available tools
- The workflow state is preserved for reference
tools.call: true still need to drive that final submit.
Loop-Back Transitions
Steps can transition back to themselves or earlier steps for retry patterns:Tool Configuration
Thetools field on a step controls which tools the agent can access and whether to force specific tool behavior.
Configuration Options
| Field | Type | Default | Description |
|---|---|---|---|
tools.allow | list[string] | null (all) | Whitelist of allowed tool names |
tools.call | boolean | false | Force immediate tool call |
tools.allowGoToStep | boolean | false | Expose manual step navigation |
tools.allow
Restricts which tools the agent can see during this step. The submit tool is always available regardless of this setting.- Prevent agent from calling irrelevant tools during sensitive steps
- Progressive disclosure: unlock tools as workflow progresses
- Security: restrict access to sensitive operations
tools.allow or using an empty list means “no restriction.” If you want “submit tool only,” list the submit tool name explicitly.
tools.call
Whentrue, forces the agent to make a tool call before responding to the user. Typically used with on.enter call actions.
- Forces a tool call on the next model turn
- If a queued
callaction is pending and allowed, that pending call is prioritized - If no pending external call is available, the runtime may force the submit tool instead
- Useful for fetching data, triggering mandatory side effects, or making no-input steps deterministic
inputs and a next transition, the LLM has nothing to collect and will not automatically submit. This causes the workflow to stall. You must add "tools": {"call": true} to force submission:
tools.call: true to ensure submission happens.
tools.allowGoToStep
Whentrue, adds a go_to_step parameter to the submit tool schema, allowing the agent to manually jump to a specific step.
- The agent chooses the step by name
- Invalid step IDs return a validation error
- Bypasses normal
nextevaluation when used - The current implementation validates the target at submit time; it does not enumerate valid step IDs in the generated schema
- Use with caution: letting the LLM model choose the next step makes the workflow more flexible, but also less predictable
Variables
Step Workflows use multiple variable scopes for different purposes.Variable Scopes
| Scope | Prefix | Lifetime | Use Case |
|---|---|---|---|
| Global | (none) or vars.* | Conversation | Shared data, final outputs |
| Task-local | local.* | Workflow | Counters, flags, intermediate state |
| Step inputs | inputs.* | Current step | Collected input parameters |
Global Variables
Global variables persist for the entire conversation and are accessible to all tools and workflows.- Data needed after the workflow completes
- Sharing data between concurrent workflows
- Final outputs that other systems will use
Task-Local Variables
Task-local variables (local.*) are scoped to a single workflow instance.
- Retry counters within the workflow
- Flags and intermediate state
- Data that shouldn’t persist after workflow completion
Step Inputs
Step inputs (inputs.*) contain the current step’s collected parameters.
Key behaviors:
- Cleared when transitioning to the next step
- Accumulated across multiple submissions
- Read-only in expressions; use
setto modify
| Context | Available Inputs |
|---|---|
on.enter | Empty (step just started) |
on.presubmit | Values from current submit call |
on.submit | Validated values (all required present) |
if conditions | Current accumulated values |
| Templates | Current accumulated values |
When to Use Which Scope
| Scenario | Scope | Why |
|---|---|---|
| Data needed after workflow completes | Global | Persists beyond workflow |
| Retry counter within workflow | local.* | Workflow-specific state |
| Sharing between concurrent workflows | Global | Task-local is isolated |
| Sensitive intermediate data | local.* | More contained scope |

