Dynamic Workflow runtime for CodeWhale.
This crate is the imperative half of Workflow: a sandboxed QuickJS
(rquickjs) runtime that executes a model-authored JS program which
dispatches fleet-routed subagents via task(), fans out with
parallel()/pipeline(), reports progress with log()/phase(), and
scales itself to a token pool via the budget global. The static,
declarative IR (record/replay, model policy) stays in codewhale-workflow;
this crate only speaks to the outside world through the
[WorkflowDriver] seam, so it is fully testable without spawning a real
subagent (see [testing::FakeDriver]).
Script surface
Every script runs inside an async function with these globals:
args— the invocation input, verbatim.await task(opts)— dispatch one subagent; resolves to the full result text, or to a parsed + schema-validated object whenopts.responseSchemais set. Throws on rejection, failure, cancellation, budget exhaustion, or once [WORKFLOW_LIFETIME_CAP] spawn attempts have been made.parallel(thunks)— all-settled fan-out; an ordinary failed slot becomesnull; schema-contract failures and run cancellation still fail the run; at most [PARALLEL_MAX_ITEMS] items.pipeline(items, ...stages)— per-item stage chains with no barrier between stages; an ordinary stage error drops that item tonull, while schema-contract failures and cancellation still fail the run; same cap.log(msg)/phase(title)— progress events forwarded to the driver.budget.total/budget.spent()/budget.remaining()— live driver snapshots (totalisnullandremaining()isInfinitywhen no ceiling is configured).
Date.now(), new Date(), Date.parse/UTC, and Math.random() throw:
runs must be deterministic so recorded traces can be replayed.
Ownership boundaries
Token accounting and admission belong to the driver; the VM only reads
snapshots and fast-fails a spawn when the shared pool is already exhausted.
Already-running parallel children can reconcile above the hint because
provider usage arrives at response boundaries, not token-by-token. Fleet
roster resolution for profile also happens driver-side; this crate
normalizes and token-validates the profile string, nothing more.