floe-guard 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -64,6 +64,23 @@ declare namespace pricing {
64
64
  * same epsilon handling, same fail-closed default.
65
65
  */
66
66
 
67
+ /**
68
+ * A two-dimensional in-flight hold (USD + tokens) returned by
69
+ * {@link BudgetGuard.reserve} when a token ceiling or an active
70
+ * {@link BudgetGuard.step} is involved. A dumb value handle — pass it straight
71
+ * back to {@link BudgetGuard.settle} / {@link BudgetGuard.release}. When neither
72
+ * tokens nor a step are involved, `reserve` returns a plain `number` instead
73
+ * (byte-for-byte the old behaviour), so {@link ReservationHandle} is the union.
74
+ */
75
+ interface BudgetReservation {
76
+ readonly usd: number;
77
+ readonly tokens: number;
78
+ }
79
+ /**
80
+ * A plain `number` when USD-only (backward compatible); a
81
+ * {@link BudgetReservation} when a token ceiling or an active step is in play.
82
+ */
83
+ type ReservationHandle = number | BudgetReservation;
67
84
  /**
68
85
  * One priced spend event in the guard's per-call ledger.
69
86
  *
@@ -103,7 +120,9 @@ interface BudgetGuardOptions {
103
120
  /**
104
121
  * Optional callback invoked with `(spentUsd, limitUsd)` right before
105
122
  * {@link BudgetExceeded} is thrown. Defaults to printing the
106
- * `BUDGET EXCEEDED — call blocked` banner to stderr.
123
+ * `BUDGET EXCEEDED — call blocked` banner to stderr. Fires on **USD** ceiling
124
+ * crossings only; a token-ceiling block ({@link TokenBudgetExceeded}, aggregate
125
+ * or step) is thrown without invoking it, since the callback is dollar-shaped.
107
126
  */
108
127
  onBlock?: (spentUsd: number, limitUsd: number) => void;
109
128
  /**
@@ -118,6 +137,11 @@ interface BudgetGuardOptions {
118
137
  * unaffected. Default: keep every event.
119
138
  */
120
139
  maxLogEvents?: number;
140
+ /**
141
+ * Aggregate token ceiling. `null`/undefined disables the token dimension
142
+ * entirely (a pure USD guard, unchanged). `0` blocks the first token.
143
+ */
144
+ tokenLimit?: number;
121
145
  }
122
146
  /**
123
147
  * A context-aware spend signal for the single local budget.
@@ -142,6 +166,35 @@ interface BudgetAdvisory {
142
166
  spentUsd: number;
143
167
  /** Hosted reports the tightest cap across all scopes; local is always "local". */
144
168
  scope: "local";
169
+ /**
170
+ * The guard's own next-call estimate (the costlier of the last LLM and last
171
+ * tool call — the same value the default reservation uses). 0 until the first
172
+ * call is recorded, so a planner can't divide by a cold estimate.
173
+ *
174
+ * Optional so adding it stays a non-breaking, additive change for any code
175
+ * that constructs a `BudgetAdvisory` literal; `advisory()` always sets it.
176
+ */
177
+ expectedCost?: number;
178
+ /**
179
+ * How many more calls the remaining budget buys at expectedCost:
180
+ * floor(remainingUsd / expectedCost). null when expectedCost is 0 (no call
181
+ * recorded yet) — unknown, not zero. Optional for the same additive reason as
182
+ * expectedCost; `advisory()` always sets it.
183
+ */
184
+ estCallsRemaining?: number | null;
185
+ /**
186
+ * Aggregate token utilization in basis points (0..10000), mirroring usedBps
187
+ * for the token ceiling. null when no `tokenLimit` is set (nothing to signal).
188
+ */
189
+ tokenUsedBps?: number | null;
190
+ /** Tokens left before the aggregate token ceiling. null when no `tokenLimit`. */
191
+ remainingTokens?: number | null;
192
+ /**
193
+ * Per-step headroom for the innermost active step — present (non-null) ONLY
194
+ * while a step() is active AND that dimension is capped. null otherwise.
195
+ */
196
+ stepRemainingUsd?: number | null;
197
+ stepRemainingTokens?: number | null;
145
198
  }
146
199
  declare class BudgetGuard {
147
200
  readonly limitUsd: number;
@@ -149,6 +202,14 @@ declare class BudgetGuard {
149
202
  priceOverrides?: Record<string, ManualPrice>;
150
203
  failClosed: boolean;
151
204
  nearLimitBps: number;
205
+ /** Aggregate token ceiling, or null when the token dimension is disabled. */
206
+ readonly tokenLimit: number | null;
207
+ /** Aggregate tokens accrued (prompt + completion + cache buckets). */
208
+ spentTokens: number;
209
+ /** Tokens held in-flight — the token twin of `reserved`. */
210
+ private reservedTokens;
211
+ /** Step stack: innermost step is last. Empty when no step() is active. */
212
+ private readonly steps;
152
213
  private readonly onBlock;
153
214
  /**
154
215
  * Costs of the most recent priced LLM call and tool call, tracked
@@ -187,7 +248,9 @@ declare class BudgetGuard {
187
248
  * Note: `check` is a non-binding peek. For parallel calls, use `reserve()` /
188
249
  * `settle()`, which hold the estimate across the await.
189
250
  */
190
- check(estimatedNextCost?: number): void;
251
+ check(estimatedNextCost?: number, options?: {
252
+ estimatedTokens?: number;
253
+ }): void;
191
254
  /**
192
255
  * Atomically check the ceiling AND hold the estimated cost in flight.
193
256
  *
@@ -199,7 +262,9 @@ declare class BudgetGuard {
199
262
  * `estimatedCost` defaults to the costlier of the last LLM call and the last
200
263
  * tool call.
201
264
  */
202
- reserve(estimatedCost?: number): number;
265
+ reserve(estimatedCost?: number, options?: {
266
+ estimatedTokens?: number;
267
+ }): ReservationHandle;
203
268
  /**
204
269
  * Release a reservation and record the actual cost. `record` is `settle` with
205
270
  * no reservation. Returns the USD cost of this call; unpriceable-model handling
@@ -210,7 +275,7 @@ declare class BudgetGuard {
210
275
  * in lockstep with `spentUsd`.
211
276
  */
212
277
  settle(model: string, promptTokens: number, completionTokens: number, options?: {
213
- reserved?: number;
278
+ reserved?: ReservationHandle;
214
279
  price?: ManualPrice;
215
280
  label?: string;
216
281
  }): number;
@@ -242,7 +307,7 @@ declare class BudgetGuard {
242
307
  * worth falling back to. Pass the returned handle to
243
308
  * {@link BudgetGuard.settleTool}, or {@link BudgetGuard.release} on failure.
244
309
  */
245
- reserveTool(estimatedCost: number): number;
310
+ reserveTool(estimatedCost: number): ReservationHandle;
246
311
  /**
247
312
  * Release a reservation and record a tool call's actual cost.
248
313
  *
@@ -258,7 +323,7 @@ declare class BudgetGuard {
258
323
  * Returns `costUsd`.
259
324
  */
260
325
  settleTool(tool: string, costUsd: number, options?: {
261
- reserved?: number;
326
+ reserved?: ReservationHandle;
262
327
  label?: string;
263
328
  }): number;
264
329
  /**
@@ -276,7 +341,7 @@ declare class BudgetGuard {
276
341
  * Drop an in-flight reservation without recording spend (e.g. the call failed
277
342
  * before producing usage). Safe to call with `0`.
278
343
  */
279
- release(reserved: number): void;
344
+ release(reserved: ReservationHandle): void;
280
345
  /** USD left before the ceiling, net of in-flight reservations (never negative). */
281
346
  get remainingUsd(): number;
282
347
  /**
@@ -322,6 +387,50 @@ declare class BudgetGuard {
322
387
  * responsibility.
323
388
  */
324
389
  private consumeReservation;
390
+ /**
391
+ * The USD amount of a handle, validated. Both shapes are checked — a raw
392
+ * number and a {@link BudgetReservation}'s `usd`/`tokens` fields — so a bad
393
+ * hand-rolled handle can't corrupt the in-flight tally.
394
+ */
395
+ /**
396
+ * Validate a caller-supplied token estimate. Rejects a fraction, NaN,
397
+ * Infinity, or boolean (via `Number.isInteger`) so it can't disable the
398
+ * integer token hard-stops or corrupt the in-flight token tally. `undefined`
399
+ * (the default) is fine; a negative integer is clamped by `Math.max(0, ...)`,
400
+ * matching the lenient USD estimate. Mirrors Python's `_validate_token_estimate`.
401
+ */
402
+ private validateTokenEstimate;
403
+ private reservedUsdOf;
404
+ /**
405
+ * Accrue a settled call into the innermost active step (no-op when none).
406
+ * Sequential-loop contract: the innermost step owns the call.
407
+ */
408
+ private accrueStep;
409
+ /**
410
+ * The ONE choke point, across two dimensions and two scopes. Returns the first
411
+ * ceiling that blocks as `[dimension, scope]` — dimension `"usd" | "tokens"`,
412
+ * scope `"aggregate" | "step"` — or `null` if the call fits everywhere.
413
+ * Aggregate is checked before step (the guard-wide ceiling is the hard limit).
414
+ */
415
+ private blockingCross;
416
+ /** Notify + throw the right error for a [dimension, scope] block. */
417
+ private raiseBlock;
418
+ /**
419
+ * Scope a per-step USD and/or token cap for a **sequential agent loop**.
420
+ *
421
+ * Runs `fn` with a step pushed onto the guard's stack; the
422
+ * enforcement/accrual path honours the innermost active step *on top of* the
423
+ * aggregate ceilings. A call that would cross the step's `maxUsd` / `maxTokens`
424
+ * is hard-blocked ({@link BudgetExceeded} / {@link TokenBudgetExceeded} with
425
+ * `scope: "step"`) even if the aggregate budget has room. `fn` receives the
426
+ * SAME guard, so no adapter needs to know about steps. The step is popped when
427
+ * `fn` settles or throws. **Not for concurrent parallel steps on one guard** —
428
+ * that's out of scope for issue #46; use one guard per parallel branch.
429
+ */
430
+ step<T>(options: {
431
+ maxUsd?: number;
432
+ maxTokens?: number;
433
+ }, fn: (guard: BudgetGuard) => T): T;
325
434
  private appendEvent;
326
435
  /**
327
436
  * Context-aware spend advisory for this budget — see {@link BudgetAdvisory}.
@@ -426,7 +535,25 @@ declare class FloeGuardError extends Error {
426
535
  declare class BudgetExceeded extends FloeGuardError {
427
536
  readonly spentUsd: number;
428
537
  readonly limitUsd: number;
429
- constructor(spentUsd: number, limitUsd: number);
538
+ constructor(spentUsd: number, limitUsd: number, message?: string);
539
+ }
540
+ /**
541
+ * Thrown before a call that would cross a token ceiling (aggregate or step).
542
+ *
543
+ * The token twin of {@link BudgetExceeded} — it extends it so the same retry
544
+ * logic that treats a USD block as terminal (`!(error instanceof
545
+ * BudgetExceeded)`) treats a token block as terminal too, with no extra wiring.
546
+ * `spentUsd` / `limitUsd` are inherited but not meaningful here; the token
547
+ * fields are the payload.
548
+ *
549
+ * `scope` is `"aggregate"` (the guard-wide `tokenLimit`) or `"step"` (the
550
+ * innermost active {@link BudgetGuard.step} cap).
551
+ */
552
+ declare class TokenBudgetExceeded extends BudgetExceeded {
553
+ readonly spentTokens: number;
554
+ readonly limitTokens: number;
555
+ readonly scope: "aggregate" | "step";
556
+ constructor(spentTokens: number, limitTokens: number, scope: "aggregate" | "step");
430
557
  }
431
558
  /**
432
559
  * Thrown when a model cannot be priced and the guard is fail-closed.
@@ -543,4 +670,4 @@ interface BudgetRetryOptions<T> {
543
670
  */
544
671
  declare function withBudgetRetry<T>(guard: BudgetGuard, call: () => T | Promise<T>, options?: BudgetRetryOptions<T>): Promise<T>;
545
672
 
546
- export { type BudgetAdvisory, BudgetExceeded, BudgetGuard, type BudgetGuardMiddleware, type BudgetGuardOptions, type BudgetRetryOptions, DeadlineExceeded, FloeGuardError, type LatencyAdvisory, LatencyBudget, type LatencyBudgetOptions, type ManualPrice, type PricedModel, type RetryPlan, type SpendEvent, UnpriceableModelError, budgetGuardMiddleware, priceTokens, pricing, resolvePrice, withBudgetRetry };
673
+ export { type BudgetAdvisory, BudgetExceeded, BudgetGuard, type BudgetGuardMiddleware, type BudgetGuardOptions, type BudgetReservation, type BudgetRetryOptions, DeadlineExceeded, FloeGuardError, type LatencyAdvisory, LatencyBudget, type LatencyBudgetOptions, type ManualPrice, type PricedModel, type ReservationHandle, type RetryPlan, type SpendEvent, TokenBudgetExceeded, UnpriceableModelError, budgetGuardMiddleware, priceTokens, pricing, resolvePrice, withBudgetRetry };
package/dist/index.d.ts CHANGED
@@ -64,6 +64,23 @@ declare namespace pricing {
64
64
  * same epsilon handling, same fail-closed default.
65
65
  */
66
66
 
67
+ /**
68
+ * A two-dimensional in-flight hold (USD + tokens) returned by
69
+ * {@link BudgetGuard.reserve} when a token ceiling or an active
70
+ * {@link BudgetGuard.step} is involved. A dumb value handle — pass it straight
71
+ * back to {@link BudgetGuard.settle} / {@link BudgetGuard.release}. When neither
72
+ * tokens nor a step are involved, `reserve` returns a plain `number` instead
73
+ * (byte-for-byte the old behaviour), so {@link ReservationHandle} is the union.
74
+ */
75
+ interface BudgetReservation {
76
+ readonly usd: number;
77
+ readonly tokens: number;
78
+ }
79
+ /**
80
+ * A plain `number` when USD-only (backward compatible); a
81
+ * {@link BudgetReservation} when a token ceiling or an active step is in play.
82
+ */
83
+ type ReservationHandle = number | BudgetReservation;
67
84
  /**
68
85
  * One priced spend event in the guard's per-call ledger.
69
86
  *
@@ -103,7 +120,9 @@ interface BudgetGuardOptions {
103
120
  /**
104
121
  * Optional callback invoked with `(spentUsd, limitUsd)` right before
105
122
  * {@link BudgetExceeded} is thrown. Defaults to printing the
106
- * `BUDGET EXCEEDED — call blocked` banner to stderr.
123
+ * `BUDGET EXCEEDED — call blocked` banner to stderr. Fires on **USD** ceiling
124
+ * crossings only; a token-ceiling block ({@link TokenBudgetExceeded}, aggregate
125
+ * or step) is thrown without invoking it, since the callback is dollar-shaped.
107
126
  */
108
127
  onBlock?: (spentUsd: number, limitUsd: number) => void;
109
128
  /**
@@ -118,6 +137,11 @@ interface BudgetGuardOptions {
118
137
  * unaffected. Default: keep every event.
119
138
  */
120
139
  maxLogEvents?: number;
140
+ /**
141
+ * Aggregate token ceiling. `null`/undefined disables the token dimension
142
+ * entirely (a pure USD guard, unchanged). `0` blocks the first token.
143
+ */
144
+ tokenLimit?: number;
121
145
  }
122
146
  /**
123
147
  * A context-aware spend signal for the single local budget.
@@ -142,6 +166,35 @@ interface BudgetAdvisory {
142
166
  spentUsd: number;
143
167
  /** Hosted reports the tightest cap across all scopes; local is always "local". */
144
168
  scope: "local";
169
+ /**
170
+ * The guard's own next-call estimate (the costlier of the last LLM and last
171
+ * tool call — the same value the default reservation uses). 0 until the first
172
+ * call is recorded, so a planner can't divide by a cold estimate.
173
+ *
174
+ * Optional so adding it stays a non-breaking, additive change for any code
175
+ * that constructs a `BudgetAdvisory` literal; `advisory()` always sets it.
176
+ */
177
+ expectedCost?: number;
178
+ /**
179
+ * How many more calls the remaining budget buys at expectedCost:
180
+ * floor(remainingUsd / expectedCost). null when expectedCost is 0 (no call
181
+ * recorded yet) — unknown, not zero. Optional for the same additive reason as
182
+ * expectedCost; `advisory()` always sets it.
183
+ */
184
+ estCallsRemaining?: number | null;
185
+ /**
186
+ * Aggregate token utilization in basis points (0..10000), mirroring usedBps
187
+ * for the token ceiling. null when no `tokenLimit` is set (nothing to signal).
188
+ */
189
+ tokenUsedBps?: number | null;
190
+ /** Tokens left before the aggregate token ceiling. null when no `tokenLimit`. */
191
+ remainingTokens?: number | null;
192
+ /**
193
+ * Per-step headroom for the innermost active step — present (non-null) ONLY
194
+ * while a step() is active AND that dimension is capped. null otherwise.
195
+ */
196
+ stepRemainingUsd?: number | null;
197
+ stepRemainingTokens?: number | null;
145
198
  }
146
199
  declare class BudgetGuard {
147
200
  readonly limitUsd: number;
@@ -149,6 +202,14 @@ declare class BudgetGuard {
149
202
  priceOverrides?: Record<string, ManualPrice>;
150
203
  failClosed: boolean;
151
204
  nearLimitBps: number;
205
+ /** Aggregate token ceiling, or null when the token dimension is disabled. */
206
+ readonly tokenLimit: number | null;
207
+ /** Aggregate tokens accrued (prompt + completion + cache buckets). */
208
+ spentTokens: number;
209
+ /** Tokens held in-flight — the token twin of `reserved`. */
210
+ private reservedTokens;
211
+ /** Step stack: innermost step is last. Empty when no step() is active. */
212
+ private readonly steps;
152
213
  private readonly onBlock;
153
214
  /**
154
215
  * Costs of the most recent priced LLM call and tool call, tracked
@@ -187,7 +248,9 @@ declare class BudgetGuard {
187
248
  * Note: `check` is a non-binding peek. For parallel calls, use `reserve()` /
188
249
  * `settle()`, which hold the estimate across the await.
189
250
  */
190
- check(estimatedNextCost?: number): void;
251
+ check(estimatedNextCost?: number, options?: {
252
+ estimatedTokens?: number;
253
+ }): void;
191
254
  /**
192
255
  * Atomically check the ceiling AND hold the estimated cost in flight.
193
256
  *
@@ -199,7 +262,9 @@ declare class BudgetGuard {
199
262
  * `estimatedCost` defaults to the costlier of the last LLM call and the last
200
263
  * tool call.
201
264
  */
202
- reserve(estimatedCost?: number): number;
265
+ reserve(estimatedCost?: number, options?: {
266
+ estimatedTokens?: number;
267
+ }): ReservationHandle;
203
268
  /**
204
269
  * Release a reservation and record the actual cost. `record` is `settle` with
205
270
  * no reservation. Returns the USD cost of this call; unpriceable-model handling
@@ -210,7 +275,7 @@ declare class BudgetGuard {
210
275
  * in lockstep with `spentUsd`.
211
276
  */
212
277
  settle(model: string, promptTokens: number, completionTokens: number, options?: {
213
- reserved?: number;
278
+ reserved?: ReservationHandle;
214
279
  price?: ManualPrice;
215
280
  label?: string;
216
281
  }): number;
@@ -242,7 +307,7 @@ declare class BudgetGuard {
242
307
  * worth falling back to. Pass the returned handle to
243
308
  * {@link BudgetGuard.settleTool}, or {@link BudgetGuard.release} on failure.
244
309
  */
245
- reserveTool(estimatedCost: number): number;
310
+ reserveTool(estimatedCost: number): ReservationHandle;
246
311
  /**
247
312
  * Release a reservation and record a tool call's actual cost.
248
313
  *
@@ -258,7 +323,7 @@ declare class BudgetGuard {
258
323
  * Returns `costUsd`.
259
324
  */
260
325
  settleTool(tool: string, costUsd: number, options?: {
261
- reserved?: number;
326
+ reserved?: ReservationHandle;
262
327
  label?: string;
263
328
  }): number;
264
329
  /**
@@ -276,7 +341,7 @@ declare class BudgetGuard {
276
341
  * Drop an in-flight reservation without recording spend (e.g. the call failed
277
342
  * before producing usage). Safe to call with `0`.
278
343
  */
279
- release(reserved: number): void;
344
+ release(reserved: ReservationHandle): void;
280
345
  /** USD left before the ceiling, net of in-flight reservations (never negative). */
281
346
  get remainingUsd(): number;
282
347
  /**
@@ -322,6 +387,50 @@ declare class BudgetGuard {
322
387
  * responsibility.
323
388
  */
324
389
  private consumeReservation;
390
+ /**
391
+ * The USD amount of a handle, validated. Both shapes are checked — a raw
392
+ * number and a {@link BudgetReservation}'s `usd`/`tokens` fields — so a bad
393
+ * hand-rolled handle can't corrupt the in-flight tally.
394
+ */
395
+ /**
396
+ * Validate a caller-supplied token estimate. Rejects a fraction, NaN,
397
+ * Infinity, or boolean (via `Number.isInteger`) so it can't disable the
398
+ * integer token hard-stops or corrupt the in-flight token tally. `undefined`
399
+ * (the default) is fine; a negative integer is clamped by `Math.max(0, ...)`,
400
+ * matching the lenient USD estimate. Mirrors Python's `_validate_token_estimate`.
401
+ */
402
+ private validateTokenEstimate;
403
+ private reservedUsdOf;
404
+ /**
405
+ * Accrue a settled call into the innermost active step (no-op when none).
406
+ * Sequential-loop contract: the innermost step owns the call.
407
+ */
408
+ private accrueStep;
409
+ /**
410
+ * The ONE choke point, across two dimensions and two scopes. Returns the first
411
+ * ceiling that blocks as `[dimension, scope]` — dimension `"usd" | "tokens"`,
412
+ * scope `"aggregate" | "step"` — or `null` if the call fits everywhere.
413
+ * Aggregate is checked before step (the guard-wide ceiling is the hard limit).
414
+ */
415
+ private blockingCross;
416
+ /** Notify + throw the right error for a [dimension, scope] block. */
417
+ private raiseBlock;
418
+ /**
419
+ * Scope a per-step USD and/or token cap for a **sequential agent loop**.
420
+ *
421
+ * Runs `fn` with a step pushed onto the guard's stack; the
422
+ * enforcement/accrual path honours the innermost active step *on top of* the
423
+ * aggregate ceilings. A call that would cross the step's `maxUsd` / `maxTokens`
424
+ * is hard-blocked ({@link BudgetExceeded} / {@link TokenBudgetExceeded} with
425
+ * `scope: "step"`) even if the aggregate budget has room. `fn` receives the
426
+ * SAME guard, so no adapter needs to know about steps. The step is popped when
427
+ * `fn` settles or throws. **Not for concurrent parallel steps on one guard** —
428
+ * that's out of scope for issue #46; use one guard per parallel branch.
429
+ */
430
+ step<T>(options: {
431
+ maxUsd?: number;
432
+ maxTokens?: number;
433
+ }, fn: (guard: BudgetGuard) => T): T;
325
434
  private appendEvent;
326
435
  /**
327
436
  * Context-aware spend advisory for this budget — see {@link BudgetAdvisory}.
@@ -426,7 +535,25 @@ declare class FloeGuardError extends Error {
426
535
  declare class BudgetExceeded extends FloeGuardError {
427
536
  readonly spentUsd: number;
428
537
  readonly limitUsd: number;
429
- constructor(spentUsd: number, limitUsd: number);
538
+ constructor(spentUsd: number, limitUsd: number, message?: string);
539
+ }
540
+ /**
541
+ * Thrown before a call that would cross a token ceiling (aggregate or step).
542
+ *
543
+ * The token twin of {@link BudgetExceeded} — it extends it so the same retry
544
+ * logic that treats a USD block as terminal (`!(error instanceof
545
+ * BudgetExceeded)`) treats a token block as terminal too, with no extra wiring.
546
+ * `spentUsd` / `limitUsd` are inherited but not meaningful here; the token
547
+ * fields are the payload.
548
+ *
549
+ * `scope` is `"aggregate"` (the guard-wide `tokenLimit`) or `"step"` (the
550
+ * innermost active {@link BudgetGuard.step} cap).
551
+ */
552
+ declare class TokenBudgetExceeded extends BudgetExceeded {
553
+ readonly spentTokens: number;
554
+ readonly limitTokens: number;
555
+ readonly scope: "aggregate" | "step";
556
+ constructor(spentTokens: number, limitTokens: number, scope: "aggregate" | "step");
430
557
  }
431
558
  /**
432
559
  * Thrown when a model cannot be priced and the guard is fail-closed.
@@ -543,4 +670,4 @@ interface BudgetRetryOptions<T> {
543
670
  */
544
671
  declare function withBudgetRetry<T>(guard: BudgetGuard, call: () => T | Promise<T>, options?: BudgetRetryOptions<T>): Promise<T>;
545
672
 
546
- export { type BudgetAdvisory, BudgetExceeded, BudgetGuard, type BudgetGuardMiddleware, type BudgetGuardOptions, type BudgetRetryOptions, DeadlineExceeded, FloeGuardError, type LatencyAdvisory, LatencyBudget, type LatencyBudgetOptions, type ManualPrice, type PricedModel, type RetryPlan, type SpendEvent, UnpriceableModelError, budgetGuardMiddleware, priceTokens, pricing, resolvePrice, withBudgetRetry };
673
+ export { type BudgetAdvisory, BudgetExceeded, BudgetGuard, type BudgetGuardMiddleware, type BudgetGuardOptions, type BudgetReservation, type BudgetRetryOptions, DeadlineExceeded, FloeGuardError, type LatencyAdvisory, LatencyBudget, type LatencyBudgetOptions, type ManualPrice, type PricedModel, type ReservationHandle, type RetryPlan, type SpendEvent, TokenBudgetExceeded, UnpriceableModelError, budgetGuardMiddleware, priceTokens, pricing, resolvePrice, withBudgetRetry };