/** * Per-invocation accounting of the *non-step* portion of a workflow * handler run: deterministic event-log replay, workflow-VM execution * between step boundaries, suspension handling, queue round-trips, etc. * Inline step bodies (`"use step"` functions invoked via `executeStep`) * are intentionally excluded — they are bounded by the platform's * function `maxDuration` and the `NO_INLINE_REPLAY_AFTER_MS` early-return * guard. * * Usage: * * ```ts * const budget = new ReplayBudget(); * // …non-step work happens here, accumulates against the budget… * budget.pause(); * try { * await executeStep(...); // not charged * } finally { * budget.resume(); * } * // back to charging * if (budget.isExhausted()) { ... } * ``` * * Implementation notes: * * - `pause()` and `resume()` are idempotent: calling `pause()` while * already paused (or `resume()` while already resumed) is a no-op. * This protects against double-counting in future refactors that nest * step execution or take an early-return path between a `pause()` and * the matching `resume()`. * - `isExhausted()` is checked at loop boundaries by the caller — the * budget itself does not arm any timers. This means an in-flight * pathological `runWorkflow` call (e.g. a huge event-log replay) can * overshoot the budget by up to one iteration's worth of work before * the next check fires. In practice the 20s headroom built into * `MAX_REPLAY_TIMEOUT_MS` (and the function `maxDuration` ceiling) * gives us slack; the old `setTimeout`-based approach also ultimately * relied on the platform SIGTERM as the hard backstop. */ export declare class ReplayBudget { private readonly limitMs; private elapsedMs; private intervalStart; constructor(limitMs?: number); /** * The configured replay-timeout limit, in ms. Useful for log messages. */ get configuredLimitMs(): number; /** * Total non-step time accumulated so far, in ms. Includes the * currently-active interval if the budget is not paused. */ elapsed(): number; /** * Stop counting elapsed time toward the budget. Idempotent — safe to * call multiple times in a row; subsequent calls are no-ops until * `resume()` reopens an interval. */ pause(): void; /** * Resume counting elapsed time toward the budget. Idempotent — safe to * call multiple times in a row; subsequent calls re-anchor the * interval start to `now()`, which is fine because no time accrues * between back-to-back `resume()` calls. */ resume(): void; /** * True if the budget has been exhausted (`elapsed() >= limitMs`). * Callers should invoke `handleExhausted(...)` afterward and return * from the handler. */ isExhausted(): boolean; } /** * Fail the run (or retry, on early attempts) when the replay budget is * exhausted. The handling depends on whether the underlying World * supports `process.exit(1)` as a queue redelivery signal (see * `World.processExitTriggersQueueRedelivery`): * * - **Managed-platform Worlds** (`world-vercel`): on attempts <= * `REPLAY_TIMEOUT_MAX_RETRIES` exit the process so the platform fails * the invocation and the queue redelivers; on the next attempt write * `run_failed` with `RUN_ERROR_CODES.REPLAY_TIMEOUT` and exit. * * - **In-process Worlds** (`world-local`, dev servers): calling * `process.exit()` would terminate the host (e.g. `pnpm dev`), so * instead log a warning, write `run_failed` best-effort, and return. * The framework completes the request normally. */ export declare function handleReplayBudgetExhausted(args: { runId: string; workflowName: string; requestId: string | undefined; attempt: number; limitMs: number; }): Promise; //# sourceMappingURL=replay-budget.d.ts.map