Overview
When workflows are enabled on an agent, the agent gets a set of tools for working with them from a conversation: starting and steering runs of the workflows it already has, putting a workflow on a schedule or giving it a goal, and - when composition is enabled for the agent - composing a new workflow from what the user asked for.
Every tool answers with a result, never with a run’s outcome.
startWorkflowRun returns a handle (runId and queued or gated), and the agent is instructed not to describe what a run produced until getWorkflowRun reports it as completed. A start that spends above the organisation’s threshold is gated on the user’s consent first (consent: 'ask'), and every start carries an idempotency key so a retried tool call returns the original run.
Runs started from chat are ordinary runs: they appear in
lua workflows runs with trigger: chat, stream the same events, and are approved, signalled and cancelled the same way. See Runs and events.When the agent composes a workflow
A request becomes a composed workflow when it needs steps that run unattended, wait on a person or an event, or outlive the reply - an approval in the middle, one job over many items in parallel, a wait for a signal or a delay, a recurring cadence, or a specialist-role review - whether or not the user says “workflow”. Requests the agent answers inline instead: a single answer, a short chain of tool calls it can finish in the same reply, or a clarifying question.
The sequence is compose, present, start: the agent calls
composeWorkflow, fixes every issue the result returns, tells the user the plan and the estimate, and only then calls startWorkflowRun({ composeId, idempotencyKey }). Nothing runs until startWorkflowRun is called. Passing name saves the composition as a reusable workflow on the agent (lua workflows list --all shows it as dynamic); dryRun validates and estimates without keeping a draft.
The node grammar
A composed workflow inform: 'graph' is the envelope
type and the members that type requires:
Inside a
promptTemplate two placeholders exist:
${initData.<field>}- a field of the run input${stepResults.<stepId>.text}- the text an earlier agent step produced
steps, input or output namespaces. A later node reads an earlier one by its id, so a chain of agent steps needs no mapping between them. The worked example the tool teaches:
step (a code step - composed workflows carry no code) and sleepUntil (not executed by the engine yet - use sleep with a duration). When the agent’s capabilities list ephemeralSpecialists: true, an agent node on '$self' may add role: { name, instructions, tools } to run the turn under a temporary role.
form: 'script' composes a JavaScript script instead - loops, budgets and judge panels; see Script form.
Repair hints
composeWorkflow validates the definition against the executors the agent may use and returns issues, each with a path (graph.0, graph.2.steps.1, graph.3.otherwise), a message and a repair the agent can apply. The codes a composer meets most:
A draft that validated before a grammar change is revalidated when it is started; a draft that no longer validates answers
startIssues and the agent recomposes it.
Grammar phases
The platform enables the workflow grammar in phases. A composed definition is accepted from phase 3, which is also where the approval and signal nodes exist; on an earlier phasecomposeWorkflow answers unsupported-node-version.
The
composeWorkflow tool description and the repair hints only advertise members the current phase allows.
Schedules from chat
scheduleWorkflow puts a saved workflow on a cadence - a cron expression or { cron, timezone } entries, at most 5 - after the agent has confirmed the exact cadence and timezone with the user. It returns the schedule’s id and the next fire time; nothing has run yet. One schedule per workflow: calling it again edits the schedule in place. describeWorkflow shows a saved workflow’s input schema, its schedules and its steps before the agent starts or schedules it. unscheduleWorkflow removes a schedule by id; runs already in flight continue.
A schedule that belongs to a goal (below) is tagged with its goalId in describeWorkflow and cannot be removed with unscheduleWorkflow.
Schedules are also managed over HTTP (/workflows/{agentId}/schedules, see Runs and events); lua workflows schedules is coming in the next CLI release.
Goals
A goal is a standing objective for a workflow: the workflow runs on a cadence, and after every run a judge decides whether the objective is met. The goal ends when the judge says done, when it has used itsmaxRuns, or when someone closes it.
Creating a goal
setWorkflowGoal creates the goal and its cadence schedule - nothing runs at that moment. Before calling it the agent confirms the objective and the cadence with its timezone, and asks once about maxRuns.
Judges
The agent prefers a predicate whenever done-ness is a field of the run output.
Reading and steering a goal
A goal’s status is one of
active, paused, done or closed; a paused goal carries a pauseReason of user, budget, max_runs or strikes. These tools are the only source of a goal’s state - the agent reports from them and never reconstructs a verdict from run outputs. Every change appends a goal.updated event to the run it concerns.
Close a goal, do not unschedule its job
A goal’s cadence is a schedule on the workflow, and the platform watches it. Deleting that schedule does not stop the goal: it is recorded as a failure of the goal, which is parkedpaused (strikes). unscheduleWorkflow therefore refuses a goal’s schedule with goal_schedule:
lua workflows goals is coming in the next CLI release.
Related
- Workflows - the concept
- Authoring - the static grammar these nodes correspond to
- Runs and events - what a run started from chat looks like
- Workflows Command -
runs,status,watchandapprovefor runs the agent started

