Summary
Add a step type that pauses the workflow for a fixed duration (seconds, minutes, or hours) before continuing. Pure in-process sleep — no durability requirement.
Motivation
A handful of small but recurring needs:
- Rate-limit cooldown — after a burst of API calls, wait 60s before the next batch instead of hitting
429s.
- Polling — call an endpoint, check status, wait 30s, call again (combined with routing loop-backs).
- External system catch-up — "I just published the doc; wait 10s for the CDN to invalidate before fetching it back to verify."
- Demos and reproductions — pace a workflow so a human can follow along in the dashboard.
Today these all require type: script with command: sleep (which doesn't work on Windows without a shim) or sticking a time.sleep() inside a tool. Native support is cleaner and cross-platform.
Proposed shape
agents:
- name: cooldown
type: wait
duration: 60s # or: "5m", "1h", or a plain number (seconds)
reason: "Cooling down after rate-limited burst" # optional, shown in dashboard
- name: poll_loop
type: wait
duration: "{{ workflow.input.poll_interval_seconds }}s" # templated
routes:
- to: check_status # loop back
Duration accepts:
- Plain int/float — seconds
- String suffix:
s, m, h (e.g. "500ms", "2.5m", "1h")
- Jinja2 template that evaluates to one of the above
Behavior
- Pure
asyncio.sleep in the engine dispatch loop. No worker pool, no persistence.
- The workflow-level
limits.timeout_seconds continues to count — a wait step inside a tight wall-clock limit will still trigger WorkflowTimeoutError.
- Interrupt key (Esc/Ctrl+G) cancels the wait immediately, same as it interrupts an agent.
- Emits
agent_started / agent_completed events with type: wait so the dashboard can render a "sleeping…" pill with a countdown.
- The step's output is
{ "waited_seconds": <actual elapsed> } so downstream routes can branch on it if needed (rarely useful, but free).
Why now
- Tiny implementation, big convenience win for workflows that interact with external systems.
- Cross-platform (no shell
sleep dependency).
- Composes with our existing routing loop-back pattern to build polling workflows without writing any Python.
Open questions
- Maximum allowed duration? Suggest 24h cap — anything longer probably wants
limits.timeout_seconds reconsidered first. Reject at schema validation with a clear message.
- Should wait counts against agent iteration counters (
max_agent_iterations)? No — it's not an agent execution. Counts as a step for routing purposes only.
- Visibility: dashboard should show a live countdown? Nice-to-have, not required for v1.
Acceptance criteria
Summary
Add a step type that pauses the workflow for a fixed duration (seconds, minutes, or hours) before continuing. Pure in-process sleep — no durability requirement.
Motivation
A handful of small but recurring needs:
429s.Today these all require
type: scriptwithcommand: sleep(which doesn't work on Windows without a shim) or sticking atime.sleep()inside a tool. Native support is cleaner and cross-platform.Proposed shape
Duration accepts:
s,m,h(e.g."500ms","2.5m","1h")Behavior
asyncio.sleepin the engine dispatch loop. No worker pool, no persistence.limits.timeout_secondscontinues to count — a wait step inside a tight wall-clock limit will still triggerWorkflowTimeoutError.agent_started/agent_completedevents withtype: waitso the dashboard can render a "sleeping…" pill with a countdown.{ "waited_seconds": <actual elapsed> }so downstream routes can branch on it if needed (rarely useful, but free).Why now
sleepdependency).Open questions
limits.timeout_secondsreconsidered first. Reject at schema validation with a clear message.max_agent_iterations)? No — it's not an agent execution. Counts as a step for routing purposes only.Acceptance criteria
type: waitaccepted by the YAML schema withduration(templated) and optionalreasons/m/hsuffixes and plain numbersasyncio.sleep{ "waited_seconds": float }and available to routes / downstream stepsexamples/(polling pattern with route loop-back)