Skip to content

Rulvar API reference


Rulvar API reference / @rulvar/core / BudgetHooks

Interface: BudgetHooks

Defined in: packages/core/src/runtime/agent-loop.ts:336

Budget hooks bound by the three-layer budget.

Properties

PropertyTypeDescriptionDefined in
admitTurnExposure?(servedBy, estimatedInputTokens, plannedOutputTokens) => (() => void) | undefined-packages/core/src/runtime/agent-loop.ts:391
assertPricedDispatch?(servedBy) => voidThe strict pre-egress pricing gate (RV1508): wired only when RunOptions.strictPricing armed it; throws typed BEFORE the wire call for a model whose price row is missing, malformed, or stale.packages/core/src/runtime/agent-loop.ts:390
awaitExposureRelease?(signal?) => Promise<"aborted" | "released" | "drained">Parks until the next in-flight exposure hold releases (RV1902): 'released' on that wake, 'drained' immediately when no hold is live, 'aborted' when the signal fires first. Wired beside admitTurnExposure when the cap is configured; consumed only by invocations that opted into the exposure wait.packages/core/src/runtime/agent-loop.ts:403
liveExposureUsd?() => numberLive in-flight exposure currently held by open dispatches (RV1902).packages/core/src/runtime/agent-loop.ts:405
maxAffordableOutputTokens?(servedBy, estimatedInputTokens) => number | undefinedLayer 2b, the pre-dispatch output bound: the output tokens the remaining budget still affords from servedBy for a prompt of estimatedInputTokens. The dispatch clamps the request's maxOutputTokens to it and denies the turn entirely when not even one output token fits. Undefined = unbounded (no ceiling, no price row, or free output).packages/core/src/runtime/agent-loop.ts:347
maxExposureOutputTokens?(servedBy, estimatedInputTokens) => number | undefinedLayer 2b asked of the IN-FLIGHT EXPOSURE ceiling (RV2503), wired only when the cap is configured: the output tokens the exposure room still affords for this prompt. The dispatch clamps to it too, so a turn whose full plan overshoots the exposure line is SHORTENED rather than refused while the budget can still pay for it. An answer below the serving model's output floor is ignored, so a genuine exposure exhaustion still refuses through admitTurnExposure with its own typed reason.packages/core/src/runtime/agent-loop.ts:368
openCallMeter?(servedBy) => (delta) => voidOpens the per-call marginal meter (RV1101): one meter per provider call, fed every mid-stream delta and the settle remainder of THAT call. The budget prices the call's ACCUMULATED usage and debits the increment over what the call already paid, so a long-context tier crossed by the accumulation re-prices the whole call live exactly as the settled fold will; per-slice pricing can never see that crossing (no single slice crosses the threshold). Optional: hooks without it keep the historical per-slice debit into onUsage.packages/core/src/runtime/agent-loop.ts:418
remainingUsd?() => number | undefinedThe remaining chain headroom in USD (RV301): the same arithmetic the output bound above reads, before pricing. Undefined = no ceiling anywhere on the chain. The tool budget extension admits a grant against it.packages/core/src/runtime/agent-loop.ts:357
signal?AbortSignalLayer 3: the ceiling AbortSignal.packages/core/src/runtime/agent-loop.ts:420

Methods

beforeTurn()

ts
beforeTurn(): void;

Defined in: packages/core/src/runtime/agent-loop.ts:338

Layer 2: before every turn; throws BudgetExhaustedError to block dispatch.

Returns

void


onUsage()

ts
onUsage(usage, servedBy): void;

Defined in: packages/core/src/runtime/agent-loop.ts:407

Live usage accounting; layer 3 may respond by aborting signal.

Parameters

ParameterType
usageUsage
servedBy`${string}:${string}`

Returns

void