@rulvar/core 1.70.0 → 1.71.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
@@ -8646,6 +8646,15 @@ interface PreflightSpawnReport {
8646
8646
  turnFloorUsd?: number;
8647
8647
  /** Executed-call ceiling across any tool mix; null = unlimited. */
8648
8648
  executedToolCallCeiling: number | null;
8649
+ /**
8650
+ * The provider-call ceiling of ONE spawn's whole loop: maxTurns
8651
+ * bounded by the executed-call ceiling plus its final no-tool turn,
8652
+ * plus the finalization summary turn when a tool budget limiter arms
8653
+ * it. Every provider turn is one wire request and one quota
8654
+ * reservation, so this is the per-spawn multiplier of quota demand;
8655
+ * retries sit on top of it.
8656
+ */
8657
+ projectedProviderTurns: number;
8649
8658
  /** Per-tool ceilings for every tool a cap or a unit cost names. */
8650
8659
  toolCeilings: PreflightToolCeiling[];
8651
8660
  }
@@ -8672,7 +8681,8 @@ interface PreflightReport {
8672
8681
  /** min(capUsd, (capFraction ?? 0.2) x ceiling); absent when unresolvable. */effectiveCapUsd?: number;
8673
8682
  finalizeReserveUsd: number;
8674
8683
  finalizeTurns: number; /** Whether the finalize reserve is committed against the run root (extension runs). */
8675
- reserveCommitted: boolean;
8684
+ reserveCommitted: boolean; /** The orchestrator agent's own loop ceiling, derived exactly like a spawn's. */
8685
+ projectedProviderTurns: number;
8676
8686
  };
8677
8687
  };
8678
8688
  quota: {
@@ -8704,6 +8714,19 @@ interface PreflightReport {
8704
8714
  requestsPerWave: number;
8705
8715
  tokensPerWaveFloor: number;
8706
8716
  }>;
8717
+ /**
8718
+ * The declared wave run to its derived turn ceilings, at the
8719
+ * declared estimates (the second experiment report, rec 9): total
8720
+ * provider calls (fan-out times per-spawn projected turns, before
8721
+ * any retries) and the cumulative token demand with the context
8722
+ * regrowing every turn (turn k re-sends the declared prompt plus
8723
+ * the k-1 prior output bounds, so K turns cost K x est +
8724
+ * outputBound x K(K+1)/2). Absent when nothing is declared.
8725
+ */
8726
+ runCeiling?: {
8727
+ requests: number;
8728
+ tokens: number;
8729
+ };
8707
8730
  };
8708
8731
  findings: PreflightFinding[];
8709
8732
  }
package/dist/index.js CHANGED
@@ -16722,6 +16722,30 @@ function orchestrate(engine, goal, opts, runOptions) {
16722
16722
  }
16723
16723
  //#endregion
16724
16724
  //#region src/engine/preflight.ts
16725
+ /**
16726
+ * The overall executed-call ceiling across any tool mix: the cheapest
16727
+ * single-tool strategy bounds it from above; maxToolCalls bounds it
16728
+ * regardless of mix. A free tool (unit cost 0) lifts the units bound
16729
+ * entirely for calls of that tool.
16730
+ */
16731
+ function overallExecutedCeiling(limits, toolCeilings) {
16732
+ const overall = toolCeilings.reduce((best, row) => row.ceiling === null ? best : best === null ? row.ceiling : Math.max(best, row.ceiling), null);
16733
+ return limits.maxToolCalls !== void 0 && (overall === null || limits.maxToolCalls < overall) ? limits.maxToolCalls : overall;
16734
+ }
16735
+ /**
16736
+ * The provider-call ceiling of one whole loop: maxTurns bounded by the
16737
+ * executed-call ceiling plus the final no-tool turn, plus the
16738
+ * finalization summary turn when a tool budget limiter arms it.
16739
+ */
16740
+ function projectedProviderTurnsOf(limits, executedToolCallCeiling) {
16741
+ const toolBound = executedToolCallCeiling === null ? void 0 : executedToolCallCeiling + 1;
16742
+ const finalizeTurn = limits.finalizationReserve !== void 0 && (limits.maxToolCalls !== void 0 || limits.toolUnits !== void 0) ? 1 : 0;
16743
+ return (toolBound === void 0 ? limits.maxTurns : Math.min(limits.maxTurns, toolBound)) + finalizeTurn;
16744
+ }
16745
+ /** Cumulative token demand of one whole loop at the declared estimates. */
16746
+ function shapeRunTokens(shape) {
16747
+ return shape.turns * shape.inputFloor + shape.outputBound * shape.turns * (shape.turns + 1) / 2;
16748
+ }
16725
16749
  const ANY_TOOL = "(any)";
16726
16750
  function resolveServing(spec) {
16727
16751
  if (spec === void 0) return;
@@ -16828,11 +16852,13 @@ function preflightEstimate(input) {
16828
16852
  const finalizeReserveUsd = spec?.finalizeReserveUsd ?? finalizeTurns * flatReserveUsd;
16829
16853
  const reserveCommitted = input.orchestrator.extension === true;
16830
16854
  if (reserveCommitted) reservedForFinalizationUsd = finalizeReserveUsd;
16855
+ const echoLimits = mergeUsageLimits(input.orchestrator.limits, void 0, defaults.limits);
16831
16856
  orchestratorEcho = {
16832
16857
  ...effectiveCapUsd === void 0 ? {} : { effectiveCapUsd },
16833
16858
  finalizeReserveUsd,
16834
16859
  finalizeTurns,
16835
- reserveCommitted
16860
+ reserveCommitted,
16861
+ projectedProviderTurns: projectedProviderTurnsOf(echoLimits, overallExecutedCeiling(echoLimits, toolCeilingsOf(echoLimits)))
16836
16862
  };
16837
16863
  if (spec?.capUsd !== void 0 && spec.capFraction === void 0 && effectiveCapUsd !== void 0 && effectiveCapUsd < spec.capUsd) say({
16838
16864
  severity: "warning",
@@ -16856,6 +16882,7 @@ function preflightEstimate(input) {
16856
16882
  */
16857
16883
  const waveGateInputs = [];
16858
16884
  const units = [];
16885
+ const shapes = [];
16859
16886
  for (const spec of spawnSpecs) {
16860
16887
  const role = spec.role ?? "loop";
16861
16888
  const label = spec.label ?? role;
@@ -16908,8 +16935,8 @@ function preflightEstimate(input) {
16908
16935
  cacheWriteTokens: 0
16909
16936
  });
16910
16937
  const toolCeilings = toolCeilingsOf(limits);
16911
- const overall = toolCeilings.reduce((best, row) => row.ceiling === null ? best : best === null ? row.ceiling : Math.max(best, row.ceiling), null);
16912
- const executedToolCallCeiling = limits.maxToolCalls !== void 0 && (overall === null || limits.maxToolCalls < overall) ? limits.maxToolCalls : overall;
16938
+ const executedToolCallCeiling = overallExecutedCeiling(limits, toolCeilings);
16939
+ const projectedProviderTurns = projectedProviderTurnsOf(limits, executedToolCallCeiling);
16913
16940
  for (const row of toolCeilings) {
16914
16941
  if (row.tool === ANY_TOOL) continue;
16915
16942
  const cost = limits.toolUnits?.costs?.[row.tool];
@@ -16976,6 +17003,7 @@ function preflightEstimate(input) {
16976
17003
  ...outputBound === void 0 ? {} : { maxOutputTokensPerTurn: outputBound },
16977
17004
  ...turnFloorUsd === void 0 ? {} : { turnFloorUsd },
16978
17005
  executedToolCallCeiling,
17006
+ projectedProviderTurns,
16979
17007
  toolCeilings
16980
17008
  });
16981
17009
  {
@@ -16998,6 +17026,21 @@ function preflightEstimate(input) {
16998
17026
  if (turnFloorUsd !== void 0) unit.turnFloorUsd = turnFloorUsd;
16999
17027
  units.push(unit);
17000
17028
  }
17029
+ {
17030
+ const shape = {
17031
+ label,
17032
+ inputFloor: spec.estInputTokens ?? 0,
17033
+ outputBound: outputBound ?? 0,
17034
+ turns: projectedProviderTurns,
17035
+ count
17036
+ };
17037
+ if (servedBy !== void 0) {
17038
+ const { adapterId, model } = parseModelRef(servedBy);
17039
+ shape.provider = adapterId;
17040
+ shape.model = model;
17041
+ }
17042
+ shapes.push(shape);
17043
+ }
17001
17044
  }
17002
17045
  let orchestratorReserveUsd;
17003
17046
  if (input.orchestrator !== void 0) {
@@ -17027,6 +17070,15 @@ function preflightEstimate(input) {
17027
17070
  cacheWriteTokens: 0
17028
17071
  });
17029
17072
  units.push(unit);
17073
+ shapes.push({
17074
+ label: "orchestrator",
17075
+ provider: adapterId,
17076
+ model,
17077
+ inputFloor: input.orchestrator.estInputTokens ?? 0,
17078
+ outputBound: outputBound ?? 0,
17079
+ turns: projectedProviderTurnsOf(orchLimits, overallExecutedCeiling(orchLimits, toolCeilingsOf(orchLimits))),
17080
+ count: 1
17081
+ });
17030
17082
  if (effectiveCapUsd !== void 0) orchestratorReserveUsd = Math.max(0, orchestratorAdmissionEstCostUsd(effectiveCapUsd, reservedForFinalizationUsd));
17031
17083
  else if (pricing === void 0) orchestratorReserveUsd = 0;
17032
17084
  else orchestratorReserveUsd = admissionReserveUsd({
@@ -17180,6 +17232,48 @@ function preflightEstimate(input) {
17180
17232
  code: "quota-tokens-below-wave",
17181
17233
  message: `${name}: the declared wave demands at least ${String(tokens)} tokens against tokensPerMinute ${String(rule.tokensPerMinute)}: expect estimate-driven throttling inside one window`
17182
17234
  });
17235
+ let runRequests = 0;
17236
+ let runTokens = 0;
17237
+ for (const shape of shapes) {
17238
+ if (shape.provider === void 0 || shape.model === void 0) continue;
17239
+ if (!quotaRuleMatches(rule, {
17240
+ provider: shape.provider,
17241
+ model: shape.model,
17242
+ ...engine.quota?.tenant === void 0 ? {} : { tenant: engine.quota.tenant },
17243
+ estimate: {
17244
+ requests: 1,
17245
+ inputTokens: 0
17246
+ }
17247
+ })) continue;
17248
+ runRequests += shape.count * shape.turns;
17249
+ runTokens += shape.count * shapeRunTokens(shape);
17250
+ if (rule.tokensPerMinute !== void 0 && shape.outputBound > 0) {
17251
+ const k = Math.max(1, Math.floor((rule.tokensPerMinute - shape.inputFloor) / shape.outputBound) + 1);
17252
+ if (k <= shape.turns) say({
17253
+ severity: "warning",
17254
+ code: "quota-turn-never-fits",
17255
+ message: `spawn '${shape.label}': by turn ${String(k)} of ${String(shape.turns)} the context-grown reservation (about ${String(shape.inputFloor + k * shape.outputBound)} tokens) exceeds ${name} tokensPerMinute ${String(rule.tokensPerMinute)} outright: the limiter denies it as never fitting the window (no wait helps) and the invocation fails after paying for the earlier turns`,
17256
+ spawn: shape.label
17257
+ });
17258
+ }
17259
+ }
17260
+ if (rule.requestsPerMinute !== void 0 && requests <= rule.requestsPerMinute && runRequests > rule.requestsPerMinute) say({
17261
+ severity: "warning",
17262
+ code: "quota-requests-below-run",
17263
+ message: `${name}: the declared wave fits ${String(requests)} dispatches under requestsPerMinute ${String(rule.requestsPerMinute)}, but run to its turn ceilings it projects up to ${String(runRequests)} provider calls (fan-out times per-spawn turns, before any retries): expect synthetic rate-limit denials and backoff across about ${String(Math.ceil(runRequests / rule.requestsPerMinute))} windows`
17264
+ });
17265
+ if (rule.tokensPerMinute !== void 0 && tokens <= rule.tokensPerMinute && runTokens > rule.tokensPerMinute) say({
17266
+ severity: "warning",
17267
+ code: "quota-tokens-below-run",
17268
+ message: `${name}: the declared wave demands ${String(tokens)} tokens up front, but with the context regrowing every turn its loops project about ${String(runTokens)} tokens against tokensPerMinute ${String(rule.tokensPerMinute)}: expect estimate-driven throttling across about ${String(Math.ceil(runTokens / rule.tokensPerMinute))} windows`
17269
+ });
17270
+ });
17271
+ const runCeiling = shapes.length === 0 ? void 0 : shapes.reduce((sum, shape) => ({
17272
+ requests: sum.requests + shape.count * shape.turns,
17273
+ tokens: sum.tokens + shape.count * shapeRunTokens(shape)
17274
+ }), {
17275
+ requests: 0,
17276
+ tokens: 0
17183
17277
  });
17184
17278
  const severityRank = {
17185
17279
  error: 0,
@@ -17217,7 +17311,8 @@ function preflightEstimate(input) {
17217
17311
  exposure: {
17218
17312
  maxInFlight,
17219
17313
  ...overshootOneTurnFloorUsd === void 0 ? {} : { overshootOneTurnFloorUsd },
17220
- perProvider
17314
+ perProvider,
17315
+ ...runCeiling === void 0 ? {} : { runCeiling }
17221
17316
  },
17222
17317
  findings
17223
17318
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.70.0",
3
+ "version": "1.71.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",