@rulvar/core 1.205.0 → 1.207.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
@@ -6758,6 +6758,23 @@ interface BudgetReserve {
6758
6758
  reserveUsd: number;
6759
6759
  /** The child sub-account ceiling; absent when the parent is uncapped. */
6760
6760
  childCeilingUsd?: number;
6761
+ /**
6762
+ * The reserve derivation (RV2004): where reserveUsd came from, so a
6763
+ * journal reader never reverse-engineers the arithmetic. 'estCost'
6764
+ * is the declared estimate (spawn opts or the agentType profile),
6765
+ * 'default' the engine flat reserve.
6766
+ */
6767
+ source?: "estCost" | "default";
6768
+ /**
6769
+ * Set when the derived reserve was clamped DOWN to the child's
6770
+ * ceiling: 'explicit-budget' by a declared budgetUsd,
6771
+ * 'fraction-ceiling' by the childBudgetFraction allowance an ORIGIN
6772
+ * WITH a materialized allowance account enforces (ctx.workflow).
6773
+ * The spawn-tool path never carries 'fraction-ceiling': its
6774
+ * dispatch enforces no fraction account, and journaling that clamp
6775
+ * is exactly the parity rerun's 0.50-versus-0.70 lie (RV2004).
6776
+ */
6777
+ clampedBy?: "explicit-budget" | "fraction-ceiling";
6761
6778
  }
6762
6779
  /** The lineage block every non-reject verdict carries (DEF-3). */
6763
6780
  interface AdmitLineage {
@@ -6801,6 +6818,25 @@ type AdmitRejectReason = {
6801
6818
  code: "osc_guard";
6802
6819
  spawnKey: SpawnKey;
6803
6820
  oscillationCount: number;
6821
+ } | {
6822
+ /**
6823
+ * The sequential roster feasibility refusal (RV2005): under a
6824
+ * declared acceptance.minSpawnedChildren, the whole remaining
6825
+ * roster (priced at this seat's own projection) plus the live
6826
+ * in-flight exposure does not fit the parent remainder, so the
6827
+ * FIRST infeasible seat refuses before any child is paid. The
6828
+ * batchGate symmetry (RV1908) on the seat-by-seat path the
6829
+ * parity rerun's model actually took, where three seats were
6830
+ * paid in full under a floor of four the money could never
6831
+ * reach.
6832
+ */
6833
+ code: "roster_floor";
6834
+ floor: number;
6835
+ admittedChildren: number;
6836
+ seatsRemaining: number;
6837
+ perSeatProjectionUsd: number;
6838
+ liveExposureUsd: number;
6839
+ remainderUsd: number;
6804
6840
  } | {
6805
6841
  /**
6806
6842
  * The declared estimate cannot fit the child's own ceiling: the
@@ -6842,6 +6878,20 @@ interface AdmitSpec {
6842
6878
  */
6843
6879
  pendingReserveUsd?: number;
6844
6880
  /**
6881
+ * The sequential roster feasibility inputs (RV2005), passed by the
6882
+ * SINGLE spawn_agent path when acceptance.minSpawnedChildren is
6883
+ * declared: the admission projects the whole REMAINING roster at
6884
+ * this seat's own dispatch projection, live in-flight exposure
6885
+ * included, and refuses the first infeasible seat typed
6886
+ * 'roster_floor' before any child is paid. Batch seats never carry
6887
+ * this: the RV1908 batchGate already judged their batch entire.
6888
+ */
6889
+ roster?: {
6890
+ floor: number;
6891
+ admittedChildren: number;
6892
+ liveExposureUsd: number;
6893
+ };
6894
+ /**
6845
6895
  * Lineage continuation (DEF-3); absence mints a fresh lineage root. A
6846
6896
  * continuation demands a causeRef: the seq of the entry that caused the
6847
6897
  * rebirth.
@@ -8734,7 +8784,7 @@ interface OrchestratorRuntime {
8734
8784
  causeRef: number;
8735
8785
  };
8736
8786
  taskClass?: string;
8737
- }): Promise<{
8787
+ }, origin?: "spawn_agent" | "parallel_agents"): Promise<{
8738
8788
  handle: number;
8739
8789
  }>;
8740
8790
  awaitAny(handles: number[]): Promise<TaskDigest>;
@@ -9535,6 +9585,19 @@ interface OrchestrateOptions {
9535
9585
  */
9536
9586
  parallelAdmission?: "fail-fast" | "try-all" | "all-or-none";
9537
9587
  /**
9588
+ * The batch-spawn discipline (RV2005). The third parity rerun's
9589
+ * model ignored the instruction to spawn its roster in one
9590
+ * parallel_agents call and spawned seat by seat through spawn_agent,
9591
+ * so the RV1908 batchGate never saw a batch and the roster
9592
+ * feasibility rode on per-seat luck. 'reject-spawn-agent' refuses
9593
+ * every SINGLE spawn_agent call typed (code 'batch_required',
9594
+ * nothing journaled, nothing paid) so model disobedience cannot
9595
+ * split the policy: the model reads the refusal and re-issues the
9596
+ * wave as one parallel_agents batch. Absent, both tools behave as
9597
+ * documented.
9598
+ */
9599
+ requireBatchSpawn?: "reject-spawn-agent";
9600
+ /**
9538
9601
  * The opt in deterministic host validation of the finish result, with
9539
9602
  * bounded repair; see {@link FinishValidationSpec}.
9540
9603
  */
@@ -13096,6 +13159,18 @@ interface PreflightReport {
13096
13159
  * fourth workers. Present whenever the wave has rows.
13097
13160
  */
13098
13161
  requiredMinimumCeilingUsd?: number;
13162
+ /**
13163
+ * The live-root-exposure term of the wave projection (RV2004): the
13164
+ * orchestrator's own worst-case turn floor, the money coordination
13165
+ * has ALWAYS already spent (and holds in flight) by the time any
13166
+ * spawn tool runs. The parity rerun's fourth seat fit the plain
13167
+ * wave (5.95 under 6.00) and was refused live by exactly this
13168
+ * term; the embedded spawn gate and requiredMinimumCeilingUsd now
13169
+ * carry it, so a seat that cannot admit live cannot admit in
13170
+ * preflight either. Present on orchestrate waves whose
13171
+ * coordination turn prices.
13172
+ */
13173
+ liveRootExposureTermUsd?: number;
13099
13174
  wave: PreflightAdmissionRow[];
13100
13175
  admitted: number;
13101
13176
  denied: number;
package/dist/index.js CHANGED
@@ -16047,15 +16047,49 @@ var AdmissionController = class {
16047
16047
  },
16048
16048
  statsBefore
16049
16049
  };
16050
+ if (spec.roster !== void 0) {
16051
+ const seatsRemaining = spec.roster.floor - spec.roster.admittedChildren;
16052
+ if (seatsRemaining > 0) {
16053
+ const perSeatProjectionUsd = this.projectedDispatchReserveUsd(spec);
16054
+ const remainder = this.budget.remainderOf(spec.parentAccountScope);
16055
+ if (remainder !== void 0 && remainder < seatsRemaining * perSeatProjectionUsd + spec.roster.liveExposureUsd) return {
16056
+ verdict: {
16057
+ kind: "reject",
16058
+ reason: {
16059
+ code: "roster_floor",
16060
+ floor: spec.roster.floor,
16061
+ admittedChildren: spec.roster.admittedChildren,
16062
+ seatsRemaining,
16063
+ perSeatProjectionUsd,
16064
+ liveExposureUsd: spec.roster.liveExposureUsd,
16065
+ remainderUsd: remainder
16066
+ }
16067
+ },
16068
+ statsBefore
16069
+ };
16070
+ }
16071
+ }
16072
+ const spawnToolOrigin = spec.origin === "spawn_agent" || spec.origin === "parallel_agents";
16050
16073
  let childCeilingUsd;
16051
16074
  const parentRemainder = this.budget.remainderOf(spec.parentAccountScope);
16052
- if (parentRemainder !== void 0) {
16075
+ if (spawnToolOrigin) {
16076
+ if (spec.budgetUsd !== void 0) childCeilingUsd = spec.budgetUsd;
16077
+ } else if (parentRemainder !== void 0) {
16053
16078
  const fractionCap = this.childBudgetFraction * parentRemainder;
16054
16079
  childCeilingUsd = spec.budgetUsd === void 0 ? fractionCap : Math.min(spec.budgetUsd, fractionCap);
16055
16080
  } else if (spec.budgetUsd !== void 0) childCeilingUsd = spec.budgetUsd;
16056
16081
  let reserveUsd = spec.estCostUsd ?? this.flatReserveUsd;
16057
- if (childCeilingUsd !== void 0) reserveUsd = Math.min(reserveUsd, childCeilingUsd);
16058
- const reserve = { reserveUsd };
16082
+ const source = spec.estCostUsd === void 0 ? "default" : "estCost";
16083
+ let clampedBy;
16084
+ if (childCeilingUsd !== void 0 && reserveUsd > childCeilingUsd) {
16085
+ clampedBy = spec.budgetUsd !== void 0 && childCeilingUsd === spec.budgetUsd ? "explicit-budget" : "fraction-ceiling";
16086
+ reserveUsd = childCeilingUsd;
16087
+ }
16088
+ const reserve = {
16089
+ reserveUsd,
16090
+ source
16091
+ };
16092
+ if (clampedBy !== void 0) reserve.clampedBy = clampedBy;
16059
16093
  if (childCeilingUsd !== void 0) reserve.childCeilingUsd = childCeilingUsd;
16060
16094
  if (this.budget.spawnHeadroom <= 0) return {
16061
16095
  verdict: {
@@ -19235,7 +19269,7 @@ function buildOrchestratorTools(runtime, profileCardText, options) {
19235
19269
  for (const [index, task] of tasks.entries()) {
19236
19270
  let spawned;
19237
19271
  try {
19238
- spawned = await runtime.spawn(task);
19272
+ spawned = await runtime.spawn(task, "parallel_agents");
19239
19273
  } catch (thrown) {
19240
19274
  const failure = {
19241
19275
  index,
@@ -21860,8 +21894,16 @@ function makeOrchestratorWorkflow(goal, opts) {
21860
21894
  };
21861
21895
  const executionFactsEnabled = opts?.executionFacts === true;
21862
21896
  const orchestratorRuntime = {
21863
- async spawn(params) {
21897
+ async spawn(params, origin = "spawn_agent") {
21864
21898
  await recoveryDone;
21899
+ if (origin === "spawn_agent" && opts?.requireBatchSpawn === "reject-spawn-agent") {
21900
+ internals.events.emit({
21901
+ type: "spawn:rejected",
21902
+ code: "batch_required",
21903
+ agentType: params.agentType
21904
+ }, callingState.spanId);
21905
+ throw new AdmissionRejectedError("orchestrate requireBatchSpawn 'reject-spawn-agent': single spawn_agent calls are refused; submit the whole wave as ONE parallel_agents call so the batch roster feasibility gate sees it entire", { data: { reason: { code: "batch_required" } } });
21906
+ }
21865
21907
  const specKey = jcsSerialize(params);
21866
21908
  const recoveredOrdinal = unclaimedRecoveredBySpec.get(specKey)?.shift();
21867
21909
  if (recoveredOrdinal !== void 0) {
@@ -21895,14 +21937,20 @@ function makeOrchestratorWorkflow(goal, opts) {
21895
21937
  const profile = Object.hasOwn(advertisedProfiles, params.agentType) ? advertisedProfiles[params.agentType] : void 0;
21896
21938
  const profileModel = profile?.model;
21897
21939
  if (profileModel !== void 0 && typeof profileModel !== "string" && "ladder" in profileModel) throw new ConfigError(`agentType '${params.agentType}' declares a ladder; ladder execution is owned by the plan extension, which resolves each rung attempt to a concrete model override; spawn a concrete profile instead`);
21940
+ const rosterFloor = (opts?.acceptance)?.minSpawnedChildren;
21898
21941
  const decision = admission.admit({
21899
- origin: "spawn_agent",
21942
+ origin,
21900
21943
  name: params.agentType,
21901
21944
  childScope: scope,
21902
21945
  parentAccountScope: callingState.budgetScope ?? "run",
21903
21946
  nodeKey: scope,
21904
21947
  ...params.budgetUsd === void 0 ? {} : { budgetUsd: params.budgetUsd },
21905
21948
  ...profile?.estCost === void 0 ? {} : { estCostUsd: profile.estCost },
21949
+ ...origin === "spawn_agent" && rosterFloor !== void 0 ? { roster: {
21950
+ floor: rosterFloor,
21951
+ admittedChildren: admittedSpawnCount,
21952
+ liveExposureUsd: internals.budget.liveExposureUsd
21953
+ } } : {},
21906
21954
  ...params.lineage === void 0 ? {} : { lineage: {
21907
21955
  continues: params.lineage.continues,
21908
21956
  causeRef: params.lineage.causeRef,
@@ -21916,7 +21964,7 @@ function makeOrchestratorWorkflow(goal, opts) {
21916
21964
  }, { commitReserve: false });
21917
21965
  const admissionValue = {
21918
21966
  decisionType: "spawn-admission",
21919
- origin: "spawn_agent",
21967
+ origin,
21920
21968
  orchestratorScope: scope,
21921
21969
  spawnOrdinal,
21922
21970
  name: params.agentType,
@@ -24726,6 +24774,7 @@ function preflightEstimate(input) {
24726
24774
  }
24727
24775
  const maxSpawns = input.orchestrator?.maxSpawns;
24728
24776
  const orchestrateWave = input.orchestrator !== void 0;
24777
+ const liveRootExposureTermUsd = orchestrateWave ? units.find((unit) => unit.label === "orchestrator")?.turnFloorUsd ?? 0 : 0;
24729
24778
  for (const [reportIndex, report] of spawnReports.entries()) {
24730
24779
  const gate = waveGateInputs[reportIndex] ?? {};
24731
24780
  for (let i = 0; i < report.count; i += 1) {
@@ -24736,7 +24785,7 @@ function preflightEstimate(input) {
24736
24785
  else if (maxSpawns !== void 0 && children >= maxSpawns) deniedBy = "orchestrator-max-spawns";
24737
24786
  else {
24738
24787
  if (orchestrateWave && ceilingUsd !== void 0) {
24739
- const remainder = ceilingUsd - heldAgainstRoot();
24788
+ const remainder = ceilingUsd - heldAgainstRoot() - liveRootExposureTermUsd;
24740
24789
  const projection = dispatchProjectionReserveUsd(gate, flatReserveUsd);
24741
24790
  if (remainder <= 0 || remainder <= projection) deniedBy = "budget";
24742
24791
  }
@@ -24758,7 +24807,7 @@ function preflightEstimate(input) {
24758
24807
  }
24759
24808
  const admitted = wave.filter((row) => row.admitted).length;
24760
24809
  const denied = wave.length - admitted;
24761
- const requiredMinimumCeilingUsd = wave.length === 0 ? void 0 : wave.reduce((sum, row) => sum + row.reserveUsd, 0) + reservedForFinalizationUsd + synthesisHoldUsd;
24810
+ const requiredMinimumCeilingUsd = wave.length === 0 ? void 0 : wave.reduce((sum, row) => sum + row.reserveUsd, 0) + reservedForFinalizationUsd + synthesisHoldUsd + liveRootExposureTermUsd;
24762
24811
  if (wave.length > 0 && denied > 0) {
24763
24812
  const deniedLabels = wave.filter((row) => !row.admitted).map((row) => row.label);
24764
24813
  if (admitted === 0) say({
@@ -25016,6 +25065,7 @@ function preflightEstimate(input) {
25016
25065
  reservedForFinalizationUsd,
25017
25066
  synthesisReserveUsd: synthesisHoldUsd,
25018
25067
  ...requiredMinimumCeilingUsd === void 0 ? {} : { requiredMinimumCeilingUsd },
25068
+ ...liveRootExposureTermUsd > 0 ? { liveRootExposureTermUsd } : {},
25019
25069
  wave,
25020
25070
  admitted,
25021
25071
  denied
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.205.0",
3
+ "version": "1.207.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",