eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
97 lines • 3.74 kB
TypeScript
import { type SlotSnapshotParams } from './helpers.js';
/**
* 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 since 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;
}
export declare class ReplayTimeoutRetryError extends Error {
readonly name = "ReplayTimeoutRetryError";
}
/**
* Reject the current delivery while replay-timeout retries remain. Once
* exhausted, fail the run and let the queue acknowledge the delivery.
*/
export declare function handleReplayBudgetExhausted(args: {
runId: string;
workflowName: string;
requestId: string | undefined;
attempt: number;
limitMs: number;
/**
* How much of the log the replay that ran out of budget had loaded. Carried
* onto the terminal write for the same reason every other write from that
* replay carries it: the run is being failed on a view of the log, and the
* World should be told which view.
*/
slotSnapshot?: SlotSnapshotParams;
}): Promise<void>;
//# sourceMappingURL=replay-budget.d.ts.map