Asking a human
Some decisions are not the agent’s to make. suspend stops the step and hands the question to a person, using the
workflow engine’s own suspension primitive — so the pause is exactly as durable as any other pause the engine takes.
const HITL_01 = Presets.requireHumanBefore(P.stepStart('applyRemediation'), { id: 'HITL-01', description: 'the applyRemediation step requires a completed human approval', mode: 'suspend',})
const workflow = guardWorkflow(createMonitor({ rules: [HITL_01] }), incidentWorkflow)requireHumanBefore is written against the run’s own lifecycle rather than against a tool: what counts as an approval is
a run.resume, which is something no tool call can fake.
The run comes back suspended
Section titled “The run comes back suspended”const started = await run.start({ inputData: { amount: 4_000 } })// started.status === 'suspended'It carries what blocked it. Show that to a person, and answer:
await run.resume({ step: 'applyRemediation', resumeData: { [VIOLATION_KEY]: detail, proceed: true },})proceed: true runs the step and commits its start event, so the trace records that a human authorised a violation rather
than hiding it. proceed: false bails the run naming the rules that stopped it — the step never starts, and nothing it
would have done happens.
Why this is not a callback
Section titled “Why this is not a callback”A callback would make the pause live in your process. The workflow engine’s suspension is a snapshot: the run can be
resumed by a different process, after a deploy, days later. That is what makes suspend usable for a decision that a
person has to actually think about, rather than one they have to answer before a timeout.
There is one honest caveat. The suspension is durable because the engine snapshots it; the monitor’s own state lives in
memory. A process that resumes a run it did not start is monitoring against an empty history. The human decision is still
honored — that path reads the resume payload, not the monitor — but a rule that something earlier in the run had already
satisfied is satisfied no longer. When that matters, replay the run’s recorded events through monitor.step before
resuming, which rebuilds exactly the state the old process held.
What guardWorkflow rewrites
Section titled “What guardWorkflow rewrites”Both the step map and the execution graph. A committed workflow holds direct step references in its graph, so replacing the map alone would leave the engine running the originals — which is the kind of guard that looks installed and is not.
The guarded step’s suspend and resume schemas are widened to admit one extra variant alongside whatever the step already declared. Everything else the step accepts is still validated.
A step that suspends for its own reasons is unaffected: the guard notices the body reached for suspend, records a pause
rather than a finish, and does not announce a second start when it resumes.