step() records local non-determinism once so it replays identically on resume.
Use it for anything that decides which effects run next but isn’t a tool or LLM
call, like the current time, a random choice, or a fresh id.
Date.now() or
Math.random() directly gives a different value the second time, so the effects
that follow carry different arguments. Different arguments mean different step
ids, and the kernel runs those effects again for real instead of replaying them.
See determinism and step ids.
Signature
nameis the step’starget, the same field a policy rule matches on. The step id itself is derived by the kernel.fnis the work to run. It’s called asfn(args), with the wholeargsobject as the single argument.argsis the payload used for step identity, and is passed tofn. Omit it (default{}) whenfntakes no arguments.idempotencymirrorsdefineTool:safe_to_retry(default) for reads and non-determinism,at_most_oncefor local side effects that must not re-run on resume.
What it records
Astep() call is recorded with kind local and travels the same submission
path as a tool call. Replay, policy, and idempotency all work the same way.
Policy treats local as a separate kind. A local step that matches no rule is
allowed whatever default_action says, so a deny-by-default bundle won’t block
one.
Calling step() outside an active execution throws an Error.