@rulvar/core 1.79.0 → 1.80.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
@@ -4925,6 +4925,8 @@ interface BudgetAccountView {
4925
4925
  spentUsd: number;
4926
4926
  committedReserveUsd: number;
4927
4927
  finalizeReserveUsd: number;
4928
+ /** The synthesis payload hold (cycle 76); zero when none is committed. */
4929
+ synthesisReserveUsd: number;
4928
4930
  parentScope?: string;
4929
4931
  }
4930
4932
  /**
@@ -5081,6 +5083,21 @@ declare class RunBudget {
5081
5083
  * frozen past the cap, so nothing else can take it.
5082
5084
  */
5083
5085
  releaseFinalizeReserve(scope: string): void;
5086
+ /**
5087
+ * Registers the synthesis payload reserve (the sixth comparison
5088
+ * experiment, cycle 76): absolute dollars held on the orchestrator
5089
+ * account AND the run root, so neither spawn admission nor the
5090
+ * per-turn output clamp lets the coordination prefix eat the money
5091
+ * the synthesis finish needs. Unlike the finalize reserve it is
5092
+ * released BEFORE the synthesis invocation dispatches (the held
5093
+ * money is exactly what that invocation is meant to spend), and it
5094
+ * never joins the severing check: a coordination running against the
5095
+ * hold is clamped smaller, never aborted. Idempotent per account:
5096
+ * re-registering adjusts the root by the delta.
5097
+ */
5098
+ commitSynthesisReserve(scope: string, reserveUsd: number): void;
5099
+ /** The synthesis dispatch consumes its reserve; see commitSynthesisReserve. */
5100
+ releaseSynthesisReserve(scope: string): void;
5084
5101
  /** The reserve is replaced by real spend when the spawn settles. */
5085
5102
  releaseReserve(reserveUsd: number, accountScope?: string): void;
5086
5103
  /** Layer 2: the per-turn guard. A turn that would cross any ceiling in the chain is not dispatched. */
@@ -7124,6 +7141,21 @@ interface OrchestratorBudgetSpec {
7124
7141
  */
7125
7142
  finalizeReserveUsd?: number;
7126
7143
  /**
7144
+ * The synthesis payload reserve (the sixth comparison experiment,
7145
+ * cycle 76): absolute USD held out of the orchestrator sub account
7146
+ * while the coordination loop runs, released to the synthesis
7147
+ * invocation just before it dispatches. Without it a pricey
7148
+ * coordination can leave the synthesis turns a remainder the budget
7149
+ * clamp shrinks below the contract's minimal accepting payload: the
7150
+ * finish is then cut at the output allowance before any tool call,
7151
+ * the invocation dies at maxTurns, and a validator-bound run fails
7152
+ * closed (the rematch run 1 lost an entire paid run exactly there).
7153
+ * Requires the `synthesis` option (single mode); must stay below the
7154
+ * effective cap. Declaring it changes budget arithmetic only; absent
7155
+ * keeps every account byte identical.
7156
+ */
7157
+ synthesisReserveUsd?: number;
7158
+ /**
7127
7159
  * A positive integer, validated before any journal entry or dispatch:
7128
7160
  * the turn limit of the reserved final wake.
7129
7161
  */
package/dist/index.js CHANGED
@@ -12141,6 +12141,7 @@ var RunBudget = class {
12141
12141
  spentUsd: 0,
12142
12142
  committedReserveUsd: 0,
12143
12143
  finalizeReserveUsd: 0,
12144
+ synthesisReserveUsd: 0,
12144
12145
  controller: new AbortController()
12145
12146
  };
12146
12147
  if (options.ceilingUsd !== void 0) root.ceilingUsd = options.ceilingUsd;
@@ -12185,6 +12186,7 @@ var RunBudget = class {
12185
12186
  spentUsd: 0,
12186
12187
  committedReserveUsd: 0,
12187
12188
  finalizeReserveUsd: options.finalizeReserveUsd ?? 0,
12189
+ synthesisReserveUsd: 0,
12188
12190
  parentScope,
12189
12191
  controller: new AbortController()
12190
12192
  };
@@ -12236,7 +12238,8 @@ var RunBudget = class {
12236
12238
  scope: account.scope,
12237
12239
  spentUsd: account.spentUsd,
12238
12240
  committedReserveUsd: account.committedReserveUsd,
12239
- finalizeReserveUsd: account.finalizeReserveUsd
12241
+ finalizeReserveUsd: account.finalizeReserveUsd,
12242
+ synthesisReserveUsd: account.synthesisReserveUsd
12240
12243
  };
12241
12244
  if (account.ceilingUsd !== void 0) view.ceilingUsd = account.ceilingUsd;
12242
12245
  if (account.parentScope !== void 0) view.parentScope = account.parentScope;
@@ -12250,7 +12253,7 @@ var RunBudget = class {
12250
12253
  remainderOf(scope) {
12251
12254
  const account = this.accounts.get(scope);
12252
12255
  if (account?.ceilingUsd === void 0) return;
12253
- return Math.max(0, account.ceilingUsd - account.spentUsd - account.committedReserveUsd - account.finalizeReserveUsd);
12256
+ return Math.max(0, account.ceilingUsd - account.spentUsd - account.committedReserveUsd - account.finalizeReserveUsd - account.synthesisReserveUsd);
12254
12257
  }
12255
12258
  /**
12256
12259
  * The tightest allowance headroom on the chain of `scope`: the minimum
@@ -12320,7 +12323,7 @@ var RunBudget = class {
12320
12323
  const chain = this.chainOf(accountScope);
12321
12324
  for (const account of chain) {
12322
12325
  if (account.ceilingUsd === void 0) continue;
12323
- const committed = account.spentUsd + account.committedReserveUsd + account.finalizeReserveUsd;
12326
+ const committed = account.spentUsd + account.committedReserveUsd + account.finalizeReserveUsd + account.synthesisReserveUsd;
12324
12327
  if (committed >= account.ceilingUsd || committed + reserveUsd > account.ceilingUsd) {
12325
12328
  if (account.scope === "run") this.exhaustedInternal = true;
12326
12329
  throw new BudgetExhaustedError(`budget ceiling reached on account '${account.scope}': spent ${account.spentUsd.toFixed(4)} USD plus committed reserves ${(account.committedReserveUsd + account.finalizeReserveUsd).toFixed(4)} USD plus the proposed reserve ${reserveUsd.toFixed(4)} USD does not fit the ceiling ${account.ceilingUsd.toFixed(4)} USD`, { data: {
@@ -12375,6 +12378,34 @@ var RunBudget = class {
12375
12378
  this.root.finalizeReserveUsd = 0;
12376
12379
  this.emitUpdate();
12377
12380
  }
12381
+ /**
12382
+ * Registers the synthesis payload reserve (the sixth comparison
12383
+ * experiment, cycle 76): absolute dollars held on the orchestrator
12384
+ * account AND the run root, so neither spawn admission nor the
12385
+ * per-turn output clamp lets the coordination prefix eat the money
12386
+ * the synthesis finish needs. Unlike the finalize reserve it is
12387
+ * released BEFORE the synthesis invocation dispatches (the held
12388
+ * money is exactly what that invocation is meant to spend), and it
12389
+ * never joins the severing check: a coordination running against the
12390
+ * hold is clamped smaller, never aborted. Idempotent per account:
12391
+ * re-registering adjusts the root by the delta.
12392
+ */
12393
+ commitSynthesisReserve(scope, reserveUsd) {
12394
+ const account = this.accounts.get(scope);
12395
+ if (account === void 0) throw new ConfigError(`unknown budget account '${scope}' for the synthesis reserve`);
12396
+ const previous = account.synthesisReserveUsd;
12397
+ account.synthesisReserveUsd = reserveUsd;
12398
+ if (account.scope !== "run") this.root.synthesisReserveUsd = Math.max(0, this.root.synthesisReserveUsd + reserveUsd - previous);
12399
+ this.emitUpdate();
12400
+ }
12401
+ /** The synthesis dispatch consumes its reserve; see commitSynthesisReserve. */
12402
+ releaseSynthesisReserve(scope) {
12403
+ const account = this.accounts.get(scope);
12404
+ if (account === void 0 || account.synthesisReserveUsd === 0) return;
12405
+ if (account.scope !== "run") this.root.synthesisReserveUsd = Math.max(0, this.root.synthesisReserveUsd - account.synthesisReserveUsd);
12406
+ account.synthesisReserveUsd = 0;
12407
+ this.emitUpdate();
12408
+ }
12378
12409
  /** The reserve is replaced by real spend when the spawn settles. */
12379
12410
  releaseReserve(reserveUsd, accountScope = "run") {
12380
12411
  for (const account of this.chainOf(accountScope)) account.committedReserveUsd = Math.max(0, account.committedReserveUsd - reserveUsd);
@@ -12407,7 +12438,7 @@ var RunBudget = class {
12407
12438
  let remainingUsd;
12408
12439
  for (const account of this.chainOf(accountScope)) {
12409
12440
  if (account.ceilingUsd === void 0) continue;
12410
- const headroom = account.ceilingUsd - account.spentUsd;
12441
+ const headroom = account.ceilingUsd - account.spentUsd - account.synthesisReserveUsd;
12411
12442
  remainingUsd = remainingUsd === void 0 ? headroom : Math.min(remainingUsd, headroom);
12412
12443
  }
12413
12444
  if (remainingUsd === void 0) return;
@@ -16159,6 +16190,11 @@ function validateOrchestrateOptions(opts) {
16159
16190
  if (spec.capUsd !== void 0) requireNonNegativeNumber(spec.capUsd, "orchestrate budget.capUsd");
16160
16191
  if (spec.capFraction !== void 0) requireFraction(spec.capFraction, "orchestrate budget.capFraction");
16161
16192
  if (spec.finalizeReserveUsd !== void 0) requireNonNegativeNumber(spec.finalizeReserveUsd, "orchestrate budget.finalizeReserveUsd");
16193
+ if (spec.synthesisReserveUsd !== void 0) {
16194
+ requireNonNegativeNumber(spec.synthesisReserveUsd, "orchestrate budget.synthesisReserveUsd");
16195
+ if (opts.synthesis === void 0) throw new ConfigError("orchestrate budget.synthesisReserveUsd requires the synthesis option: the reserve holds sub account money for the separate synthesis invocation");
16196
+ if (opts.synthesis.mode === "incremental") throw new ConfigError("orchestrate budget.synthesisReserveUsd is incompatible with synthesis.mode 'incremental': the reserve protects the single post-fan-in invocation");
16197
+ }
16162
16198
  if (spec.finalizeTurns !== void 0) requirePositiveInteger(spec.finalizeTurns, "orchestrate budget.finalizeTurns");
16163
16199
  if (spec.atCap !== void 0 && spec.atCap !== "finish-with-partial" && spec.atCap !== "fail-run") throw new ConfigError(`orchestrate budget.atCap must be 'finish-with-partial' or 'fail-run'; got ${String(spec.atCap)}`);
16164
16200
  }
@@ -16308,12 +16344,14 @@ function makeOrchestratorWorkflow(goal, opts) {
16308
16344
  liveValue: internals.pricingVersion ?? "unpriced"
16309
16345
  }] : []);
16310
16346
  orchestratorAccount = callingState.scope === "" ? "orchestrator" : `${callingState.scope}/orchestrator`;
16347
+ if (spec?.synthesisReserveUsd !== void 0 && frozen.capUsd <= spec.synthesisReserveUsd) throw new OrchestratorCapConfigError(`effectiveCap ${frozen.capUsd.toFixed(4)} USD is not above the synthesis reserve ${spec.synthesisReserveUsd.toFixed(4)} USD`);
16311
16348
  internals.budget.openAccount(orchestratorAccount, {
16312
16349
  parentScope: callingState.budgetScope ?? "run",
16313
16350
  ceilingUsd: frozen.capUsd,
16314
16351
  kind: "orchestrator-cap"
16315
16352
  });
16316
16353
  if (extension !== void 0) internals.budget.commitFinalizeReserve(orchestratorAccount, frozen.finalizeReserveUsd);
16354
+ if (spec?.synthesisReserveUsd !== void 0 && spec.synthesisReserveUsd > 0) internals.budget.commitSynthesisReserve(orchestratorAccount, spec.synthesisReserveUsd);
16317
16355
  capState = {
16318
16356
  effectiveCapUsd: frozen.capUsd,
16319
16357
  finalizeReserveUsd: frozen.finalizeReserveUsd,
@@ -16335,6 +16373,7 @@ function makeOrchestratorWorkflow(goal, opts) {
16335
16373
  level: "warn",
16336
16374
  msg: `orchestrator budget.capUsd ${spec.capUsd.toFixed(4)} USD is bounded to ${effectiveCapUsd.toFixed(4)} USD by the default capFraction 0.2 of the run ceiling (effectiveCap = min(capUsd, capFraction * ceiling)); pass capFraction: 1.0 to make capUsd the sole bound`
16337
16375
  }, callingState.spanId);
16376
+ if (spec?.synthesisReserveUsd !== void 0 && effectiveCapUsd <= spec.synthesisReserveUsd) throw new OrchestratorCapConfigError(`effectiveCap ${effectiveCapUsd.toFixed(4)} USD is not above the synthesis reserve ${spec.synthesisReserveUsd.toFixed(4)} USD: the coordination loop would have no money at all`);
16338
16377
  orchestratorAccount = callingState.scope === "" ? "orchestrator" : `${callingState.scope}/orchestrator`;
16339
16378
  internals.budget.openAccount(orchestratorAccount, {
16340
16379
  parentScope: callingState.budgetScope ?? "run",
@@ -16342,6 +16381,7 @@ function makeOrchestratorWorkflow(goal, opts) {
16342
16381
  kind: "orchestrator-cap"
16343
16382
  });
16344
16383
  if (extension !== void 0) internals.budget.commitFinalizeReserve(orchestratorAccount, finalizeReserveUsd);
16384
+ if (spec?.synthesisReserveUsd !== void 0 && spec.synthesisReserveUsd > 0) internals.budget.commitSynthesisReserve(orchestratorAccount, spec.synthesisReserveUsd);
16345
16385
  capState = {
16346
16386
  effectiveCapUsd,
16347
16387
  finalizeReserveUsd,
@@ -17268,7 +17308,7 @@ function makeOrchestratorWorkflow(goal, opts) {
17268
17308
  role: "orchestrate",
17269
17309
  result: "full",
17270
17310
  tools,
17271
- ...capState === void 0 ? {} : { estCost: orchestratorAdmissionEstCostUsd(capState.effectiveCapUsd, orchestratorAccount === void 0 ? 0 : internals.budget.accountView(orchestratorAccount)?.finalizeReserveUsd ?? 0) },
17311
+ ...capState === void 0 ? {} : { estCost: orchestratorAdmissionEstCostUsd(capState.effectiveCapUsd, orchestratorAccount === void 0 ? 0 : (internals.budget.accountView(orchestratorAccount)?.finalizeReserveUsd ?? 0) + (internals.budget.accountView(orchestratorAccount)?.synthesisReserveUsd ?? 0)) },
17272
17312
  ...opts?.model === void 0 ? {} : { model: opts.model },
17273
17313
  ...opts?.limits === void 0 ? {} : { limits: opts.limits },
17274
17314
  [kOnRunning]: (seq) => {
@@ -17584,7 +17624,10 @@ function makeOrchestratorWorkflow(goal, opts) {
17584
17624
  }
17585
17625
  }, callingState.spanId);
17586
17626
  const synthesisState = { ...callingState };
17587
- if (orchestratorAccount !== void 0) synthesisState.budgetScope = orchestratorAccount;
17627
+ if (orchestratorAccount !== void 0) {
17628
+ synthesisState.budgetScope = orchestratorAccount;
17629
+ internals.budget.releaseSynthesisReserve(orchestratorAccount);
17630
+ }
17588
17631
  const synthesisBreak = validationSpec === void 0 ? void 0 : validationAbort.signal;
17589
17632
  if (synthesisBreak !== void 0) synthesisState.signal = callingState.signal === void 0 ? synthesisBreak : AbortSignal.any([callingState.signal, synthesisBreak]);
17590
17633
  const synthesisOpts = {
@@ -18237,7 +18280,7 @@ function preflightEstimate(input) {
18237
18280
  turns: projectedProviderTurnsOf(orchLimits, overallExecutedCeiling(orchLimits, toolCeilingsOf(orchLimits))) + coordinationRepairReserve,
18238
18281
  count: 1
18239
18282
  });
18240
- if (effectiveCapUsd !== void 0) orchestratorReserveUsd = Math.max(0, orchestratorAdmissionEstCostUsd(effectiveCapUsd, reservedForFinalizationUsd));
18283
+ if (effectiveCapUsd !== void 0) orchestratorReserveUsd = Math.max(0, orchestratorAdmissionEstCostUsd(effectiveCapUsd, reservedForFinalizationUsd + (input.orchestrator.budget?.synthesisReserveUsd ?? 0)));
18241
18284
  else if (pricing === void 0) orchestratorReserveUsd = 0;
18242
18285
  else orchestratorReserveUsd = admissionReserveUsd({
18243
18286
  ...input.orchestrator.estInputTokens === void 0 ? {} : { inputTokens: input.orchestrator.estInputTokens },
@@ -18293,6 +18336,21 @@ function preflightEstimate(input) {
18293
18336
  spawn: "synthesis"
18294
18337
  });
18295
18338
  contractFeasibilityFindings(outputBound, "synthesis", servedBy);
18339
+ {
18340
+ const contract = input.finishValidation?.contract;
18341
+ const pricing = pricingOf(servedBy);
18342
+ if (contract !== void 0 && pricing !== void 0 && pricing.outputUsdPerMTok > 0) {
18343
+ const minTokens = Math.ceil(JSON.stringify({ result: contract.goldenAccept.text }).length / 4);
18344
+ const payloadUsd = minTokens / 1e6 * pricing.outputUsdPerMTok;
18345
+ const declared = input.orchestrator?.budget?.synthesisReserveUsd;
18346
+ if (declared === void 0 || declared < payloadUsd) say({
18347
+ severity: "warning",
18348
+ code: "synthesis-reserve-unfunded",
18349
+ message: `the contract's minimal accepting payload is about ${String(minTokens)} output tokens, about ${payloadUsd.toFixed(4)} USD at the output rate of '${servedBy}', and the orchestrator sub account holds ${declared === void 0 ? "no synthesis reserve" : `only ${declared.toFixed(4)} USD`} for it: a pricey coordination prefix can leave the synthesis turns a remainder the budget clamp shrinks below the payload, cutting the finish before any tool call; declare budget.synthesisReserveUsd at or above the payload price`,
18350
+ spawn: "synthesis"
18351
+ });
18352
+ }
18353
+ }
18296
18354
  const projected = projectedProviderTurnsOf(merged, overallExecutedCeiling(merged, toolCeilingsOf(merged))) + finishRepairReserve;
18297
18355
  if (orchestratorEcho !== void 0) orchestratorEcho.synthesis = {
18298
18356
  projectedProviderTurns: projected,
@@ -18315,10 +18373,12 @@ function preflightEstimate(input) {
18315
18373
  let committed = 0;
18316
18374
  let spawned = 0;
18317
18375
  let children = 0;
18318
- const admitAgainstRoot = (reserveUsd) => {
18376
+ const admitAgainstRoot = (reserveUsd, strictAtFill = false) => {
18319
18377
  if (ceilingUsd === void 0) return true;
18320
18378
  const held = committed + reservedForFinalizationUsd;
18321
- return !(held >= ceilingUsd || held + reserveUsd > ceilingUsd);
18379
+ if (held >= ceilingUsd) return false;
18380
+ const fill = held + reserveUsd;
18381
+ return strictAtFill ? fill < ceilingUsd : fill <= ceilingUsd;
18322
18382
  };
18323
18383
  if (input.orchestrator !== void 0) {
18324
18384
  const reserveUsd = orchestratorReserveUsd ?? flatReserveUsd;
@@ -18350,9 +18410,9 @@ function preflightEstimate(input) {
18350
18410
  if (orchestrateWave && ceilingUsd !== void 0) {
18351
18411
  const remainder = ceilingUsd - committed - reservedForFinalizationUsd;
18352
18412
  const projection = dispatchProjectionReserveUsd(gate, flatReserveUsd);
18353
- if (remainder <= 0 || remainder < projection) deniedBy = "budget";
18413
+ if (remainder <= 0 || remainder <= projection) deniedBy = "budget";
18354
18414
  }
18355
- if (deniedBy === void 0 && !admitAgainstRoot(reserveUsd)) deniedBy = "budget";
18415
+ if (deniedBy === void 0 && !admitAgainstRoot(reserveUsd, orchestrateWave)) deniedBy = "budget";
18356
18416
  }
18357
18417
  wave.push({
18358
18418
  label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.79.0",
3
+ "version": "1.80.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",