@rulvar/core 1.198.0 → 1.200.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
@@ -9421,6 +9421,24 @@ interface OrchestrateOptions {
9421
9421
  */
9422
9422
  onUnsettledAtExit?: "cancel" | "drain";
9423
9423
  /**
9424
+ * The parallel_agents admission policy (RV1908). 'fail-fast' (the
9425
+ * default, the RV805 shape) admits in submission order and stops at
9426
+ * the first refusal, tasks after it never attempted. 'try-all'
9427
+ * attempts every task and reports every refusal, so one refused
9428
+ * sibling no longer hides whether the rest would seat. 'all-or-none'
9429
+ * projects the WHOLE batch against the live remainder first and
9430
+ * refuses it typed with zero admissions when it cannot seat
9431
+ * entirely; a non-budget failure mid-batch cancels the admitted
9432
+ * siblings, best-effort atomicity over a machinery that cannot
9433
+ * un-admit. Independent of the policy, a declared
9434
+ * acceptance.minSpawnedChildren arms the roster pre-check: a batch
9435
+ * large enough to seat the floor whose feasible count cannot reach
9436
+ * it is refused before paying for the first child, the four-role
9437
+ * benchmark's primary arm shape, where two workers were paid in
9438
+ * full and the settle verdict was bound to reject them.
9439
+ */
9440
+ parallelAdmission?: "fail-fast" | "try-all" | "all-or-none";
9441
+ /**
9424
9442
  * The opt in deterministic host validation of the finish result, with
9425
9443
  * bounded repair; see {@link FinishValidationSpec}.
9426
9444
  */
@@ -12973,6 +12991,15 @@ interface PreflightReport {
12973
12991
  * synthesis reserve, matching the runtime that then commits none.
12974
12992
  */
12975
12993
  synthesisReserveUsd: number;
12994
+ /**
12995
+ * The smallest run ceiling that seats the WHOLE declared wave
12996
+ * (RV1907): every row's reserve plus the finalization and synthesis
12997
+ * carve-outs. Children admit strictly below exact fill, so a viable
12998
+ * ceiling must sit strictly ABOVE this figure; the four-role
12999
+ * benchmark's $6.00 sat $0.98 below it and lost its third and
13000
+ * fourth workers. Present whenever the wave has rows.
13001
+ */
13002
+ requiredMinimumCeilingUsd?: number;
12976
13003
  wave: PreflightAdmissionRow[];
12977
13004
  admitted: number;
12978
13005
  denied: number;
@@ -12985,7 +13012,18 @@ interface PreflightReport {
12985
13012
  * documented overshoot bound is one turn per in-flight agent; real
12986
13013
  * turns grow with the prompt, so this is the floor of that bound.
12987
13014
  */
12988
- overshootOneTurnFloorUsd?: number; /** Per-provider first-wave demand at the declared estimates. */
13015
+ overshootOneTurnFloorUsd?: number;
13016
+ /**
13017
+ * The smallest in-flight exposure cap under which the declared wave
13018
+ * can breathe (RV1907): the finalization and synthesis carve-outs
13019
+ * plus the turn floors of the maxInFlight most expensive declared
13020
+ * dispatches, the orchestrator's own turn among them. Below it the
13021
+ * root's next turn is refused beside a full child wave, the
13022
+ * recovery arm's exact death; the RV1902 wait recovers the run, but
13023
+ * only a cap at or above this floor avoids the stall entirely.
13024
+ * Absent when no declared turn prices.
13025
+ */
13026
+ requiredMinimumExposureUsd?: number; /** Per-provider first-wave demand at the declared estimates. */
12989
13027
  perProvider: Record<string, {
12990
13028
  inFlight: number;
12991
13029
  requestsPerWave: number;
@@ -13311,7 +13349,21 @@ declare function buildOrchestratorTools(runtime: OrchestratorRuntime, profileCar
13311
13349
  * opted-in run's toolset hash and re-key their resumes, so the new
13312
13350
  * tool re-keys only runs that opt into IT.
13313
13351
  */
13314
- settledResultsTool?: boolean;
13352
+ settledResultsTool?: boolean; /** The parallel_agents admission policy (RV1908); default 'fail-fast'. */
13353
+ parallelAdmission?: "fail-fast" | "try-all" | "all-or-none";
13354
+ /**
13355
+ * The batch projection seam (RV1908): the live remainder and the
13356
+ * per-task dispatch projection the embedded gate itself uses, plus
13357
+ * the run's admitted-children count and the declared acceptance
13358
+ * roster floor. Runtime behavior only, never part of the tool
13359
+ * schema or description, so toolset hashes stay byte identical.
13360
+ */
13361
+ batchGate?: {
13362
+ rosterFloor?: number;
13363
+ admittedChildren: () => number;
13364
+ projectionUsd: (task: SpawnAgentParams) => number;
13365
+ remainderUsd: () => number | undefined;
13366
+ };
13315
13367
  }): ToolDef[];
13316
13368
  //#endregion
13317
13369
  //#region src/engine/events.d.ts
package/dist/index.js CHANGED
@@ -19058,23 +19058,77 @@ function buildOrchestratorTools(runtime, profileCardText, options) {
19058
19058
  parameters: PARALLEL_AGENTS_SCHEMA,
19059
19059
  execute: async (input) => {
19060
19060
  const tasks = input.tasks;
19061
+ const policy = options?.parallelAdmission ?? "fail-fast";
19062
+ const gate = options?.batchGate;
19063
+ if (gate !== void 0) {
19064
+ const remainder = gate.remainderUsd();
19065
+ if (remainder !== void 0) {
19066
+ let accumulated = 0;
19067
+ let feasible = 0;
19068
+ for (const task of tasks) {
19069
+ const projection = gate.projectionUsd(task);
19070
+ if (remainder < accumulated + projection) break;
19071
+ accumulated += projection;
19072
+ feasible += 1;
19073
+ }
19074
+ const admittedSoFar = gate.admittedChildren();
19075
+ const floor = gate.rosterFloor;
19076
+ if (floor !== void 0 && admittedSoFar + tasks.length >= floor && admittedSoFar + feasible < floor) return {
19077
+ handles: [],
19078
+ refused: {
19079
+ index: feasible,
19080
+ code: "roster_floor",
19081
+ reason: `the batch can seat ${String(feasible)} of its ${String(tasks.length)} tasks under the live remainder, and the ${String(admittedSoFar)} already admitted cannot reach acceptance.minSpawnedChildren ${String(floor)}: refused before paying for a roster the settle verdict is bound to reject`
19082
+ }
19083
+ };
19084
+ if (policy === "all-or-none" && feasible < tasks.length) return {
19085
+ handles: [],
19086
+ refused: {
19087
+ index: feasible,
19088
+ code: "batch_atomic",
19089
+ reason: `all-or-none: the batch's ${String(tasks.length)} reserves do not fit the live remainder ${remainder.toFixed(4)} USD (${String(feasible)} would seat); nothing was admitted and nothing was paid`
19090
+ }
19091
+ };
19092
+ }
19093
+ }
19061
19094
  const handles = [];
19095
+ const refusals = [];
19062
19096
  for (const [index, task] of tasks.entries()) {
19063
19097
  let spawned;
19064
19098
  try {
19065
19099
  spawned = await runtime.spawn(task);
19066
19100
  } catch (thrown) {
19101
+ const failure = {
19102
+ index,
19103
+ ...thrown instanceof RulvarError ? { code: thrown.code } : {},
19104
+ reason: thrown instanceof Error ? thrown.message : String(thrown)
19105
+ };
19106
+ if (policy === "try-all") {
19107
+ refusals.push(failure);
19108
+ continue;
19109
+ }
19110
+ if (policy === "all-or-none" && handles.length > 0) {
19111
+ for (const handle of handles) await runtime.cancel(handle, "rulvar:batch-atomic-rollback");
19112
+ return {
19113
+ handles: [],
19114
+ refused: {
19115
+ ...failure,
19116
+ reason: `all-or-none: task ${String(index)} failed after ${String(handles.length)} sibling(s) were admitted; the siblings were cancelled (${failure.reason})`
19117
+ }
19118
+ };
19119
+ }
19067
19120
  return {
19068
19121
  handles,
19069
- refused: {
19070
- index,
19071
- ...thrown instanceof RulvarError ? { code: thrown.code } : {},
19072
- reason: thrown instanceof Error ? thrown.message : String(thrown)
19073
- }
19122
+ refused: failure
19074
19123
  };
19075
19124
  }
19076
19125
  handles.push(spawned.handle);
19077
19126
  }
19127
+ if (refusals.length > 0) return {
19128
+ handles,
19129
+ refused: refusals[0],
19130
+ refusals
19131
+ };
19078
19132
  return { handles };
19079
19133
  }
19080
19134
  });
@@ -20817,6 +20871,7 @@ function validateOrchestrateOptions(opts) {
20817
20871
  if (opts.maxSpawns !== void 0) requireNonNegativeInteger(opts.maxSpawns, "orchestrate maxSpawns");
20818
20872
  if (opts.renderBudgetChars !== void 0) requireNonNegativeInteger(opts.renderBudgetChars, "orchestrate renderBudgetChars");
20819
20873
  if (opts.onUnsettledAtExit !== void 0 && opts.onUnsettledAtExit !== "cancel" && opts.onUnsettledAtExit !== "drain") throw new ConfigError(`orchestrate onUnsettledAtExit must be 'cancel' or 'drain'; got ${String(opts.onUnsettledAtExit)}`);
20874
+ if (opts.parallelAdmission !== void 0 && opts.parallelAdmission !== "fail-fast" && opts.parallelAdmission !== "try-all" && opts.parallelAdmission !== "all-or-none") throw new ConfigError(`orchestrate parallelAdmission must be 'fail-fast', 'try-all' or 'all-or-none'; got ${String(opts.parallelAdmission)}`);
20820
20875
  if (opts.acceptance !== void 0) {
20821
20876
  const policy = opts.acceptance.childPolicy;
20822
20877
  const minSuccessful = typeof policy === "object" && policy !== null && !Array.isArray(policy) ? policy.minSuccessful : void 0;
@@ -22036,7 +22091,20 @@ function makeOrchestratorWorkflow(goal, opts) {
22036
22091
  const tools = [...buildOrchestratorTools(orchestratorRuntime, fullCardText, {
22037
22092
  childResultTools: opts?.exposeChildResultTools === true,
22038
22093
  settledResultsTool: opts?.exposeSettledResultsTool === true,
22039
- sectionalFinish: coordSectionalFinish
22094
+ sectionalFinish: coordSectionalFinish,
22095
+ ...opts?.parallelAdmission === void 0 ? {} : { parallelAdmission: opts.parallelAdmission },
22096
+ batchGate: {
22097
+ ...(opts?.acceptance)?.minSpawnedChildren === void 0 ? {} : { rosterFloor: (opts?.acceptance)?.minSpawnedChildren },
22098
+ admittedChildren: () => admittedSpawnCount,
22099
+ projectionUsd: (task) => {
22100
+ const profile = internals.defaults.profiles?.[task.agentType];
22101
+ return admission.projectedDispatchReserveUsd({
22102
+ ...profile?.estCost === void 0 ? {} : { estCostUsd: profile.estCost },
22103
+ ...task.budgetUsd === void 0 ? {} : { budgetUsd: task.budgetUsd }
22104
+ });
22105
+ },
22106
+ remainderUsd: () => internals.budget.remainderOf(callingState.budgetScope ?? "run")
22107
+ }
22040
22108
  }), ...extension?.tools(io) ?? []];
22041
22109
  /**
22042
22110
  * The RV-204 finish validation hook, installed on the terminal tool
@@ -24550,6 +24618,7 @@ function preflightEstimate(input) {
24550
24618
  }
24551
24619
  const admitted = wave.filter((row) => row.admitted).length;
24552
24620
  const denied = wave.length - admitted;
24621
+ const requiredMinimumCeilingUsd = wave.length === 0 ? void 0 : wave.reduce((sum, row) => sum + row.reserveUsd, 0) + reservedForFinalizationUsd + synthesisHoldUsd;
24553
24622
  if (wave.length > 0 && denied > 0) {
24554
24623
  const deniedLabels = wave.filter((row) => !row.admitted).map((row) => row.label);
24555
24624
  if (admitted === 0) say({
@@ -24614,6 +24683,12 @@ function preflightEstimate(input) {
24614
24683
  code: "in-flight-exposure-cap",
24615
24684
  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`
24616
24685
  });
24686
+ const requiredMinimumExposureUsd = overshootOneTurnFloorUsd === void 0 ? void 0 : reservedForFinalizationUsd + synthesisHoldUsd + overshootOneTurnFloorUsd;
24687
+ if (exposureCapUsd !== void 0 && overshootOneTurnFloorUsd !== void 0 && requiredMinimumExposureUsd !== void 0 && requiredMinimumExposureUsd > exposureCapUsd) say({
24688
+ severity: "warning",
24689
+ code: "exposure-cap-tight",
24690
+ 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`
24691
+ });
24617
24692
  const quotaConfigured = engine.quota !== void 0;
24618
24693
  if (!quotaConfigured && maxInFlight > 1 && units.length > 0) say({
24619
24694
  severity: "info",
@@ -24800,6 +24875,7 @@ function preflightEstimate(input) {
24800
24875
  ...ceilingUsd === void 0 ? {} : { ceilingUsd },
24801
24876
  reservedForFinalizationUsd,
24802
24877
  synthesisReserveUsd: synthesisHoldUsd,
24878
+ ...requiredMinimumCeilingUsd === void 0 ? {} : { requiredMinimumCeilingUsd },
24803
24879
  wave,
24804
24880
  admitted,
24805
24881
  denied
@@ -24807,6 +24883,7 @@ function preflightEstimate(input) {
24807
24883
  exposure: {
24808
24884
  maxInFlight,
24809
24885
  ...overshootOneTurnFloorUsd === void 0 ? {} : { overshootOneTurnFloorUsd },
24886
+ ...requiredMinimumExposureUsd === void 0 ? {} : { requiredMinimumExposureUsd },
24810
24887
  perProvider,
24811
24888
  ...runCeiling === void 0 ? {} : { runCeiling }
24812
24889
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.198.0",
3
+ "version": "1.200.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",