Bound how long the engine WAITS on one step, and let Stop() cut that wait
short.
timeBudgetExpiryReason is consulted only at the top of the step loop
— it gates step entry, it does not bound a step's duration. A step that
blocks internally therefore never returns to that check and the run never
self-expires. Stop() cannot rescue it either: the abort signal is wired to
the LLM calls, not to browser work, so a Stop() raised while execution sits
inside a page call only lands at the next cooperative checkpoint — which a
blocked step never reaches. Both gaps are the same missing capability, so
both are supplied here.
Whichever way this settles, the losing step keeps running: a page call cannot
be cancelled. That is the point — the engine stops waiting and scores the
run, rather than holding its worker (and, in a parallel suite, that worker's
whole remaining queue) on a promise that will never settle.
Bound how long the engine WAITS on one step, and let
Stop()cut that wait short.timeBudgetExpiryReason is consulted only at the top of the step loop — it gates step entry, it does not bound a step's duration. A step that blocks internally therefore never returns to that check and the run never self-expires.
Stop()cannot rescue it either: the abort signal is wired to the LLM calls, not to browser work, so aStop()raised while execution sits inside a page call only lands at the next cooperative checkpoint — which a blocked step never reaches. Both gaps are the same missing capability, so both are supplied here.Whichever way this settles, the losing step keeps running: a page call cannot be cancelled. That is the point — the engine stops waiting and scores the run, rather than holding its worker (and, in a parallel suite, that worker's whole remaining queue) on a promise that will never settle.