workflow
Some jobs are one thing an agent does. Others have a shape: a decision, then several pieces built at once because they do not touch each other, then somebody looking at the result and saying whether it holds. Written as instructions, that shape becomes a list an agent has to carry in its head, and by the last step the first is gone. Written as a workflow, the shape is code — it runs in the engine, it decides its own phases and branches and fan-out, and nothing that answers it is told what comes next.
The shape on disk
<name>.workflow/
workflow.js the program — the primary file, and where all the decisions live
workflow.yaml what it is, when to reach for it, the phases it moves through
README.md
.metadata
The program
workflow.js is JavaScript, run in the engine. Six things are given to it:
phase(name) name the stage the run is in
log(text) say something as it goes
agent(prompt, opts) ask for one agent's whole job; parks until answered
parallel(thunks) several asks that do not depend on each other
pipeline(items, ...) each item through every stage, independently
args whatever the run was started with
agent() is the only call that leaves the engine. It parks the run, puts the
request where whoever holds credentials can see it, and resumes with the answer.
Everything else — the loop that built those prompts, the branch on what came
back, the decision to fan out — is the program's, and stays in the engine.
opts.schema states the shape the answer must have:
'string' 'number' 'boolean' 'any' a value of that type
'string[]' or [ shape ] a list, every item of that shape
{ field: shape, 'note?': shape } a table with those fields; a name
ending in ? may be left out
workflow.next hands the schema over with the prompt. workflow.answer
refuses a value of any other shape, naming the field and the shape expected
there, and the request stays open to be answered again. workflow.correct
replaces an answer the program has not read yet, held to the same schema; the
program reads an answer within a moment of it arriving, and a correction after
that is refused as already consumed. Every list the schema
names reaches the program as an array, an empty one included. A schema written
outside this notation fails the run at the agent() call that carries it.
Running one
local run = asset.resolve("<name>", "workflow"):start({ ... })
or through the workflow toolbox, which is what an agent uses:
workflow.start, then workflow.next / workflow.answer until it is finished.
Each request a run asks is announced on tool responses once, when it can be
taken, and once more if a claim on it runs out; workflow.status lists every
request still waiting.
From the engine shell, the same run is driven with zero workflow:
zero workflow start --name [name]
zero workflow status
zero workflow next --runner <who you are> (claim the request it is waiting on)
zero workflow renew --request <id> (keep the claim while its work goes on)
zero workflow answer --request <id> --value <result>
zero workflow fail --request <id> --reason "<why>"
next names who is taking the request, and answer, renew and fail name
the request they act on by the id next returned.
What a run is
A run lives in the engine, not in a session. Whoever started it can close the tab, lose the connection or hand over to somebody else; the run stays parked exactly where it was, and the next agent to ask gets the request that was waiting. Each request is one subagent's whole job — the point is that no single context has to hold the entire run.
In the Inspector
Workflow: what the workflow is for and when to reach for it, the args it takes, its phases, its step budget when it declares one, and the size of workflow.js, with Open for workflow.yaml and workflow.js. Nothing here runs it.