@rulvar/core 1.211.0 → 1.212.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.ts CHANGED
@@ -382,13 +382,18 @@ type AgentError = {
382
382
  retryAfterMs?: number;
383
383
  issues?: Issue$1[];
384
384
  /**
385
- * The typed refusal marker (RV2002): 'exposure-drained' names a
386
- * spawned child refused pre-wire by the in-flight exposure cap with
387
- * no live holder left to wait out. Zero provider attempts by
388
- * construction, so the seat is cheap to re-spawn; an orchestrator
389
- * treats it as a starved seat, never a crashed child.
390
- */
391
- reason?: "exposure-drained";
385
+ * The typed refusal marker (RV2002, widened by RV2101):
386
+ * 'exposure-drained' names a spawned child refused pre-wire by the
387
+ * in-flight exposure cap with no live holder left to wait out (zero
388
+ * provider attempts by construction, so the seat is cheap to
389
+ * re-spawn; an orchestrator treats it as a starved seat, never a
390
+ * crashed child). 'output-floor' names a turn refused pre-wire
391
+ * because the remaining budget past the held reserves cannot afford
392
+ * the model's output floor: at the reserve line this is the
393
+ * boundary where the coordination loop settles partial and the
394
+ * synthesis promise is redeemed, never a crash.
395
+ */
396
+ reason?: "exposure-drained" | "output-floor";
392
397
  };
393
398
  /**
394
399
  * Projects an AgentError to its WireError form: code 'agent', with kind,
@@ -6513,10 +6518,17 @@ declare class RunBudget {
6513
6518
  * price rows as the layer-2b clamp) right before the wire call and
6514
6519
  * releases at the attempt's settle, so the reservation lives exactly
6515
6520
  * as long as the exposure it covers. The admission refuses, typed
6516
- * and without waiting, when spent + the named reserves (finalize and
6517
- * synthesis money is promised elsewhere) + live reservations + this
6521
+ * and without waiting, when spent + live reservations + this
6518
6522
  * estimate does not fit the cap; an exact fill admits, mirroring
6519
- * admitSpawn, and a full cap refuses even a zero estimate. A refusal
6523
+ * admitSpawn, and a full cap refuses even a zero estimate. The tail
6524
+ * reserves (finalize and synthesis) stay OUT of the sum (RV2101):
6525
+ * the budget chain already fences them (remainingUsd subtracts the
6526
+ * synthesis promise, and the finalize carve-out nets out of the
6527
+ * orchestrator's own cap), so counting them here too made the cap
6528
+ * bind at cap minus reserves while the actual wire risk was far
6529
+ * below it: the fourth parity run's root was refused at spent
6530
+ * 4.71 + reserve 1.00 against 5.70 with zero live estimates, one
6531
+ * turn short of the synthesis the reserve existed to fund. A refusal
6520
6532
  * is TRANSIENT (in-flight money returns at settle), so it never
6521
6533
  * marks the run exhausted and never severs a stream. A model without
6522
6534
  * a price row reserves zero, exactly as it debits zero (the
@@ -13290,6 +13302,21 @@ interface PreflightReport {
13290
13302
  * coordination turn prices.
13291
13303
  */
13292
13304
  liveRootExposureTermUsd?: number;
13305
+ /**
13306
+ * The reserve line (RV2101): the run ceiling minus the synthesis
13307
+ * reserve, the boundary the budget chain fences every non-tail
13308
+ * dispatch at while the promise is held. Present when a ceiling
13309
+ * and a positive synthesis reserve are both declared.
13310
+ */
13311
+ reserveLineUsd?: number;
13312
+ /**
13313
+ * How far the admitted wave's steady state sits under the reserve
13314
+ * line (RV2101). Child spend past the declared estimates consumes
13315
+ * this headroom before the coordination loop is refused at the
13316
+ * line; under two coordination turn floors the projection warns
13317
+ * with `reserve-line-headroom`. Present beside reserveLineUsd.
13318
+ */
13319
+ reserveLineHeadroomUsd?: number;
13293
13320
  wave: PreflightAdmissionRow[];
13294
13321
  admitted: number;
13295
13322
  denied: number;
package/dist/index.js CHANGED
@@ -10931,7 +10931,7 @@ function applyOutputBudget(req, target, budget) {
10931
10931
  if (affordable === void 0) return req;
10932
10932
  if (affordable < floor) {
10933
10933
  const zeroInputAffordable = hook(target.resolved.ref, 0);
10934
- if (zeroInputAffordable !== void 0 && zeroInputAffordable < floor) throw new BudgetExhaustedError(floor === 1 ? `the remaining budget cannot afford one output token from ${target.resolved.ref}; the turn was not dispatched` : `the remaining budget cannot afford the ${String(floor)} token output floor of ${target.resolved.ref}; the turn was not dispatched`);
10934
+ if (zeroInputAffordable !== void 0 && zeroInputAffordable < floor) throw new BudgetExhaustedError(floor === 1 ? `the remaining budget cannot afford one output token from ${target.resolved.ref}; the turn was not dispatched` : `the remaining budget cannot afford the ${String(floor)} token output floor of ${target.resolved.ref}; the turn was not dispatched`, { data: { reason: "output-floor" } });
10935
10935
  return {
10936
10936
  ...req,
10937
10937
  maxOutputTokens: floor
@@ -12657,10 +12657,11 @@ async function runAgent(options) {
12657
12657
  } catch (thrown) {
12658
12658
  if (!(thrown instanceof BudgetExhaustedError)) throw thrown;
12659
12659
  status = "error";
12660
+ const typedReason = thrown.data?.reason;
12660
12661
  agentError = {
12661
12662
  kind: "budget",
12662
12663
  retryable: false,
12663
- ...thrown.data?.reason === "exposure-drained" ? { reason: "exposure-drained" } : {}
12664
+ ...typedReason === "exposure-drained" || typedReason === "output-floor" ? { reason: typedReason } : {}
12664
12665
  };
12665
12666
  errorMessage = thrown.message;
12666
12667
  break;
@@ -14001,10 +14002,17 @@ var RunBudget = class {
14001
14002
  * price rows as the layer-2b clamp) right before the wire call and
14002
14003
  * releases at the attempt's settle, so the reservation lives exactly
14003
14004
  * as long as the exposure it covers. The admission refuses, typed
14004
- * and without waiting, when spent + the named reserves (finalize and
14005
- * synthesis money is promised elsewhere) + live reservations + this
14005
+ * and without waiting, when spent + live reservations + this
14006
14006
  * estimate does not fit the cap; an exact fill admits, mirroring
14007
- * admitSpawn, and a full cap refuses even a zero estimate. A refusal
14007
+ * admitSpawn, and a full cap refuses even a zero estimate. The tail
14008
+ * reserves (finalize and synthesis) stay OUT of the sum (RV2101):
14009
+ * the budget chain already fences them (remainingUsd subtracts the
14010
+ * synthesis promise, and the finalize carve-out nets out of the
14011
+ * orchestrator's own cap), so counting them here too made the cap
14012
+ * bind at cap minus reserves while the actual wire risk was far
14013
+ * below it: the fourth parity run's root was refused at spent
14014
+ * 4.71 + reserve 1.00 against 5.70 with zero live estimates, one
14015
+ * turn short of the synthesis the reserve existed to fund. A refusal
14008
14016
  * is TRANSIENT (in-flight money returns at settle), so it never
14009
14017
  * marks the run exhausted and never severs a stream. A model without
14010
14018
  * a price row reserves zero, exactly as it debits zero (the
@@ -14028,8 +14036,8 @@ var RunBudget = class {
14028
14036
  });
14029
14037
  const estimateUsd = Number.isFinite(rawEstimate) && rawEstimate > 0 ? rawEstimate : 0;
14030
14038
  const root = this.root;
14031
- const committed = root.spentUsd + root.finalizeReserveUsd + root.synthesisReserveUsd + this.inFlightExposureUsd;
14032
- if (committed >= cap || committed + estimateUsd > cap) throw new BudgetExhaustedError(`${IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX}: spent ${root.spentUsd.toFixed(4)} USD plus reserves ${(root.finalizeReserveUsd + root.synthesisReserveUsd).toFixed(4)} USD plus live dispatch estimates ${this.inFlightExposureUsd.toFixed(4)} USD plus this turn's estimate ${estimateUsd.toFixed(4)} USD does not fit maxInFlightExposureUsd ${cap.toFixed(4)} USD; the dispatch was refused before any provider call`, { data: {
14039
+ const committed = root.spentUsd + this.inFlightExposureUsd;
14040
+ if (committed >= cap || committed + estimateUsd > cap) throw new BudgetExhaustedError(`${IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX}: spent ${root.spentUsd.toFixed(4)} USD plus live dispatch estimates ${this.inFlightExposureUsd.toFixed(4)} USD plus this turn's estimate ${estimateUsd.toFixed(4)} USD does not fit maxInFlightExposureUsd ${cap.toFixed(4)} USD; the dispatch was refused before any provider call`, { data: {
14033
14041
  reason: "in-flight-exposure",
14034
14042
  capUsd: cap,
14035
14043
  spentUsd: root.spentUsd,
@@ -18396,6 +18404,12 @@ function createCtx(internals, rootWorkflow) {
18396
18404
  source: "in-flight-exposure",
18397
18405
  reason: "in-flight-exposure"
18398
18406
  } });
18407
+ if (!internals.budget.exhausted && result.error?.kind === "budget" && result.error.reason === "output-floor" && result.errorMessage !== void 0) throw new BudgetExhaustedError(result.errorMessage, { data: {
18408
+ scope: state.scope,
18409
+ entryRef: terminal.seq,
18410
+ source: "output-floor",
18411
+ reason: "output-floor"
18412
+ } });
18399
18413
  const diagnostics = internals.budget.exhaustionDiagnostics(state.budgetScope ?? "run");
18400
18414
  const crossed = diagnostics.crossed;
18401
18415
  const rootSuffix = `run root: spent ${diagnostics.root.spentUsd.toFixed(4)}` + (diagnostics.root.ceilingUsd === void 0 ? " USD, no ceiling" : ` of ${diagnostics.root.ceilingUsd.toFixed(4)} USD`);
@@ -23884,7 +23898,8 @@ function makeOrchestratorWorkflow(goal, opts) {
23884
23898
  try {
23885
23899
  result = await runtime.runInScope(orchestratorState, () => ctx.agent(orchestratorPrompt(goal, opts?.maxSpawns, promptLines.length === 0 ? void 0 : promptLines), agentOpts));
23886
23900
  } catch (thrown) {
23887
- if (!(thrown instanceof BudgetExhaustedError) || thrown.data?.reason !== "in-flight-exposure") throw thrown;
23901
+ const budgetReason = thrown instanceof BudgetExhaustedError ? thrown.data?.reason : void 0;
23902
+ if (budgetReason !== "in-flight-exposure" && budgetReason !== "output-floor") throw thrown;
23888
23903
  const exposureKey = deriverV2.deriveKey({ kind: "orchestrator-exposure-fallback" });
23889
23904
  if (!internals.replayer.snapshot().some((entry) => entry.kind === "decision" && entry.key === exposureKey)) await internals.replayer.appendSinglePhase({
23890
23905
  scope: callingState.scope,
@@ -23895,7 +23910,7 @@ function makeOrchestratorWorkflow(goal, opts) {
23895
23910
  site: "orchestrator-budget",
23896
23911
  value: {
23897
23912
  decisionType: "orchestrator_finalize_fallback",
23898
- reason: "exposure-abort",
23913
+ reason: budgetReason === "output-floor" ? "budget-floor" : "exposure-abort",
23899
23914
  turnsUsed: 0,
23900
23915
  foldParams: {
23901
23916
  planHash: "",
@@ -23904,12 +23919,24 @@ function makeOrchestratorWorkflow(goal, opts) {
23904
23919
  }
23905
23920
  });
23906
23921
  internals.budget.markExhausted();
23907
- return {
23922
+ const fold = {
23908
23923
  forcedFinishFallback: true,
23909
23924
  completion: "partial",
23910
23925
  planHash: "",
23911
23926
  completed: [...byOrdinal.values()].filter((record) => record.settled !== void 0).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => digestOf(record, record.settled))
23912
23927
  };
23928
+ const synthesisReserveStillCommitted = orchestratorAccount !== void 0 && (internals.budget.accountView(orchestratorAccount)?.synthesisReserveUsd ?? 0) > 0;
23929
+ const anySettled = [...byOrdinal.values()].some((record) => record.settled !== void 0);
23930
+ if (opts?.synthesis !== void 0 && synthesisReserveStillCommitted && anySettled) try {
23931
+ const synthesized = await runSynthesis(void 0);
23932
+ return {
23933
+ ...fold,
23934
+ result: synthesized
23935
+ };
23936
+ } catch {
23937
+ return fold;
23938
+ }
23939
+ return fold;
23913
23940
  }
23914
23941
  const liveTermination = extensionTermination;
23915
23942
  if (liveTermination !== void 0) throw liveTermination;
@@ -24932,6 +24959,13 @@ function preflightEstimate(input) {
24932
24959
  const admitted = wave.filter((row) => row.admitted).length;
24933
24960
  const denied = wave.length - admitted;
24934
24961
  const requiredMinimumCeilingUsd = wave.length === 0 ? void 0 : wave.reduce((sum, row) => sum + row.reserveUsd, 0) + reservedForFinalizationUsd + synthesisHoldUsd + liveRootExposureTermUsd;
24962
+ const reserveLineUsd = ceilingUsd === void 0 || synthesisHoldUsd <= 0 ? void 0 : ceilingUsd - synthesisHoldUsd;
24963
+ const reserveLineHeadroomUsd = reserveLineUsd === void 0 ? void 0 : reserveLineUsd - wave.filter((row) => row.admitted).reduce((sum, row) => sum + row.reserveUsd, 0);
24964
+ if (orchestrateWave && reserveLineUsd !== void 0 && reserveLineHeadroomUsd !== void 0 && wave.some((row) => row.admitted) && reserveLineHeadroomUsd < 2 * Math.max(liveRootExposureTermUsd, 1e-4)) say({
24965
+ severity: "warning",
24966
+ code: "reserve-line-headroom",
24967
+ message: `the admitted wave's steady state sits ${reserveLineHeadroomUsd.toFixed(4)} USD under the reserve line ${reserveLineUsd.toFixed(4)} USD (the ceiling minus the synthesis reserve), less than two coordination turn floors of headroom (${liveRootExposureTermUsd.toFixed(4)} USD each): child spend past the declared estimates eats that headroom, the coordination loop is then refused at the line, and the run settles partial with the synthesis redeemed from its reserve (RV2101); size the wave below the line or raise the ceiling to keep coordinating past it`
24968
+ });
24935
24969
  if (wave.length > 0 && denied > 0) {
24936
24970
  const deniedLabels = wave.filter((row) => !row.admitted).map((row) => row.label);
24937
24971
  if (admitted === 0) say({
@@ -24996,11 +25030,11 @@ function preflightEstimate(input) {
24996
25030
  code: "in-flight-exposure-cap",
24997
25031
  message: `RunOptions.maxInFlightExposureUsd ${exposureCapUsd.toFixed(4)} USD bounds spent money plus live dispatch estimates: a dispatch whose estimate does not fit is refused typed before the provider call, so the worst concurrent overshoot past the cap is the estimate error of the in-flight turns, not one whole turn per agent`
24998
25032
  });
24999
- const requiredMinimumExposureUsd = overshootOneTurnFloorUsd === void 0 ? void 0 : reservedForFinalizationUsd + synthesisHoldUsd + overshootOneTurnFloorUsd;
25000
- if (exposureCapUsd !== void 0 && overshootOneTurnFloorUsd !== void 0 && requiredMinimumExposureUsd !== void 0 && requiredMinimumExposureUsd > exposureCapUsd) say({
25033
+ const requiredMinimumExposureUsd = overshootOneTurnFloorUsd;
25034
+ if (exposureCapUsd !== void 0 && requiredMinimumExposureUsd !== void 0 && requiredMinimumExposureUsd > exposureCapUsd) say({
25001
25035
  severity: "warning",
25002
25036
  code: "exposure-cap-tight",
25003
- message: `maxInFlightExposureUsd ${exposureCapUsd.toFixed(4)} USD sits below the declared wave's breathing floor ${requiredMinimumExposureUsd.toFixed(4)} USD (finalize + synthesis reserves ${(reservedForFinalizationUsd + synthesisHoldUsd).toFixed(4)} plus the ${String(pricedTurns.length)} most expensive concurrent turn floors ${overshootOneTurnFloorUsd.toFixed(4)}): a coordinating turn beside a full child wave will be refused pre-wire and park until a hold releases (RV1902); raise the cap to at least the floor to avoid the stall entirely`
25037
+ message: `maxInFlightExposureUsd ${exposureCapUsd.toFixed(4)} USD sits below the declared wave's breathing floor ${requiredMinimumExposureUsd.toFixed(4)} USD (the ${String(pricedTurns.length)} most expensive concurrent turn floors): a coordinating turn beside a full child wave will be refused pre-wire and park until a hold releases (RV1902); raise the cap to at least the floor to avoid the stall entirely`
25004
25038
  });
25005
25039
  const quotaConfigured = engine.quota !== void 0;
25006
25040
  if (!quotaConfigured && maxInFlight > 1 && units.length > 0) say({
@@ -25190,6 +25224,8 @@ function preflightEstimate(input) {
25190
25224
  synthesisReserveUsd: synthesisHoldUsd,
25191
25225
  ...requiredMinimumCeilingUsd === void 0 ? {} : { requiredMinimumCeilingUsd },
25192
25226
  ...liveRootExposureTermUsd > 0 ? { liveRootExposureTermUsd } : {},
25227
+ ...reserveLineUsd === void 0 ? {} : { reserveLineUsd },
25228
+ ...reserveLineHeadroomUsd === void 0 ? {} : { reserveLineHeadroomUsd },
25193
25229
  wave,
25194
25230
  admitted,
25195
25231
  denied
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.211.0",
3
+ "version": "1.212.0",
4
4
  "description": "Rulvar core: L0 contracts, journal kernel, ctx primitives, agent runtime, model router, tool system, dynamic orchestrator, InMemory and JSONL stores, event stream.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",