@rulvar/core 1.191.0 → 1.193.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
@@ -2381,6 +2381,14 @@ type AgentEvents = {
2381
2381
  label?: string;
2382
2382
  error: WireError;
2383
2383
  willRetry: boolean;
2384
+ } | {
2385
+ type: "quota:denied";
2386
+ agentType: string;
2387
+ label?: string; /** The denied model ref. */
2388
+ model?: string; /** The limiter's reason ('tokensPerMinute 1800000 exhausted'). */
2389
+ reason?: string;
2390
+ retryAfterMs?: number;
2391
+ willRetry: true;
2384
2392
  } | {
2385
2393
  type: "agent:schema-retry";
2386
2394
  agentType: string;
@@ -5192,6 +5200,12 @@ interface RunAgentOptions<S extends SchemaSpec = JsonSchema> {
5192
5200
  adapter: ProviderAdapter;
5193
5201
  resolved: ResolvedInvocation;
5194
5202
  /**
5203
+ * The versioned compat flag (RV1810): emit the legacy `agent:error`
5204
+ * twin beside `quota:denied` for recoverable pre-wire quota waits.
5205
+ * Default off: the wait speaks its own type only.
5206
+ */
5207
+ quotaDeniedAgentError?: boolean;
5208
+ /**
5195
5209
  * Transport failover chain for the loop phase (M4-T04):
5196
5210
  * resolved fallback targets tried in order on
5197
5211
  * transport or rate-limit failures after retries exhaust. Failover is
@@ -7070,6 +7084,16 @@ interface CreateEngineOptions {
7070
7084
  modelKnowledge?: ModelKnowledgeStore;
7071
7085
  };
7072
7086
  defaults?: EngineDefaults;
7087
+ /**
7088
+ * Telemetry compat posture (RV1810). `quotaDeniedAgentError: true`
7089
+ * restores the legacy `agent:error` twin beside the primary
7090
+ * `quota:denied` event for recoverable pre-wire quota waits, for
7091
+ * consumers still keyed to the old type. Default off: healthy
7092
+ * throttling speaks its own type and never reads as failure.
7093
+ */
7094
+ telemetry?: {
7095
+ quotaDeniedAgentError?: boolean;
7096
+ };
7073
7097
  budgetDefaults?: BudgetDefaults;
7074
7098
  concurrency?: {
7075
7099
  perRun?: number; /** Per-adapter-id caps; unlimited unless configured (Appendix A; M4-T07). */
@@ -10515,6 +10539,10 @@ interface RunInternals {
10515
10539
  gates?: Record<string, MechanicalGateProfile>; /** Engine-wide admission countTokens policy (RV1804); default 'allow'. */
10516
10540
  countTokens?: "allow" | "deny";
10517
10541
  };
10542
+ /** Telemetry compat posture (RV1810). */
10543
+ telemetry?: {
10544
+ /** Emit the legacy agent:error twin beside quota:denied. */quotaDeniedAgentError?: boolean;
10545
+ };
10518
10546
  /** Engine-scoped per-provider keyed limiter (M4-T07). */
10519
10547
  providerLimiter?: KeyedLimiter;
10520
10548
  /**
@@ -12525,6 +12553,14 @@ interface PreflightOrchestratorSpec {
12525
12553
  };
12526
12554
  acceptPartialChildren?: boolean;
12527
12555
  acceptValidatedTerminalOutputOnLimit?: boolean;
12556
+ /**
12557
+ * Mirrors OrchestrateAcceptance.minSpawnedChildren (RV1901, the
12558
+ * four-role benchmark's primary defect): declaring it lets the
12559
+ * admission projection judge whether the declared wave can seat
12560
+ * the roster the acceptance policy demands, instead of green-
12561
+ * lighting a wave the settle verdict is bound to reject.
12562
+ */
12563
+ minSpawnedChildren?: number;
12528
12564
  };
12529
12565
  /**
12530
12566
  * The separate synthesis invocation (RV-211), when the orchestration
@@ -12686,6 +12722,15 @@ interface PreflightAdmissionRow {
12686
12722
  reserveUsd: number;
12687
12723
  admitted: boolean;
12688
12724
  deniedBy?: "budget" | "spawn-cap" | "orchestrator-max-spawns";
12725
+ /**
12726
+ * The run-root money already held when this row was evaluated:
12727
+ * committed reserves of the earlier rows plus the finalization and
12728
+ * synthesis carve-outs (RV1901). The row admits iff held + reserveUsd
12729
+ * fits the ceiling (children strictly below it at exact fill), so a
12730
+ * denied row's arithmetic is auditable term by term. Present only
12731
+ * under a USD ceiling.
12732
+ */
12733
+ heldAtEvaluationUsd?: number;
12689
12734
  }
12690
12735
  /** The machine-readable preflight report; JSON-serializable throughout. */
12691
12736
  interface PreflightReport {
@@ -12728,6 +12773,15 @@ interface PreflightReport {
12728
12773
  admission: {
12729
12774
  ceilingUsd?: number;
12730
12775
  reservedForFinalizationUsd: number;
12776
+ /**
12777
+ * The synthesis payload carve-out the projection holds against the
12778
+ * run root, exactly the live commitSynthesisReserve mirror (RV1901):
12779
+ * a capped orchestrator with budget.synthesisReserveUsd registers it
12780
+ * on the root before any spawn admits, so the wave arithmetic must
12781
+ * hold it too. Zero when the orchestrator is uncapped or declares no
12782
+ * synthesis reserve, matching the runtime that then commits none.
12783
+ */
12784
+ synthesisReserveUsd: number;
12731
12785
  wave: PreflightAdmissionRow[];
12732
12786
  admitted: number;
12733
12787
  denied: number;
package/dist/index.js CHANGED
@@ -12407,7 +12407,25 @@ async function runAgent(options) {
12407
12407
  const retryAfter = (outcome.wireError?.data)?.retryAfterMs;
12408
12408
  if (outcome.wireError !== void 0) {
12409
12409
  if (outcome.quotaDenied !== true) transportRetries += 1;
12410
- events?.emit({
12410
+ if (outcome.quotaDenied === true) {
12411
+ const denialData = outcome.wireError.data;
12412
+ events?.emit({
12413
+ type: "quota:denied",
12414
+ agentType,
12415
+ label: options.label,
12416
+ model: target.resolved.ref,
12417
+ ...typeof denialData?.reason === "string" ? { reason: denialData.reason } : {},
12418
+ ...typeof denialData?.retryAfterMs === "number" ? { retryAfterMs: denialData.retryAfterMs } : {},
12419
+ willRetry: true
12420
+ });
12421
+ if (options.quotaDeniedAgentError === true) events?.emit({
12422
+ type: "agent:error",
12423
+ agentType,
12424
+ label: options.label,
12425
+ error: outcome.wireError,
12426
+ willRetry: true
12427
+ });
12428
+ } else events?.emit({
12411
12429
  type: "agent:error",
12412
12430
  agentType,
12413
12431
  label: options.label,
@@ -17579,6 +17597,7 @@ function createCtx(internals, rootWorkflow) {
17579
17597
  resolved: loopResolved,
17580
17598
  limits,
17581
17599
  events: agentSink,
17600
+ ...internals.telemetry?.quotaDeniedAgentError === true ? { quotaDeniedAgentError: true } : {},
17582
17601
  transcript: {
17583
17602
  mintRef: internals.mintTranscriptRef,
17584
17603
  put: (ref, blob) => internals.transcripts.put(ref, blob, internals.lease)
@@ -23765,9 +23784,11 @@ function preflightEstimate(input) {
23765
23784
  const coordinationRepairReserve = input.orchestrator?.synthesis === void 0 ? finishRepairReserve : 0;
23766
23785
  let orchestratorEcho;
23767
23786
  let reservedForFinalizationUsd = 0;
23787
+ let synthesisHoldUsd = 0;
23768
23788
  let effectiveCapUsd;
23769
23789
  if (input.orchestrator !== void 0) {
23770
23790
  if (input.orchestrator.estInputTokens !== void 0) requireNonNegativeInteger(input.orchestrator.estInputTokens, "preflight.orchestrator.estInputTokens");
23791
+ if (input.orchestrator.acceptance?.minSpawnedChildren !== void 0) requirePositiveInteger$2(input.orchestrator.acceptance.minSpawnedChildren, "preflight.orchestrator.acceptance.minSpawnedChildren");
23771
23792
  const spec = input.orchestrator.budget;
23772
23793
  const fraction = spec?.capFraction ?? .2;
23773
23794
  const fromFraction = ceilingUsd === void 0 ? void 0 : fraction * ceilingUsd;
@@ -23777,6 +23798,7 @@ function preflightEstimate(input) {
23777
23798
  const finalizeReserveUsd = spec?.finalizeReserveUsd ?? finalizeTurns * flatReserveUsd;
23778
23799
  const reserveCommitted = input.orchestrator.extension === true;
23779
23800
  if (reserveCommitted) reservedForFinalizationUsd = finalizeReserveUsd;
23801
+ if (effectiveCapUsd !== void 0) synthesisHoldUsd = Math.max(0, spec?.synthesisReserveUsd ?? 0);
23780
23802
  const echoLimits = mergeUsageLimits(input.orchestrator.limits, void 0, defaults.limits);
23781
23803
  orchestratorEcho = {
23782
23804
  ...effectiveCapUsd === void 0 ? {} : { effectiveCapUsd },
@@ -24189,9 +24211,11 @@ function preflightEstimate(input) {
24189
24211
  let committed = 0;
24190
24212
  let spawned = 0;
24191
24213
  let children = 0;
24214
+ let childrenDeniedByBudget = 0;
24215
+ const heldAgainstRoot = () => committed + reservedForFinalizationUsd + synthesisHoldUsd;
24192
24216
  const admitAgainstRoot = (reserveUsd, strictAtFill = false) => {
24193
24217
  if (ceilingUsd === void 0) return true;
24194
- const held = committed + reservedForFinalizationUsd;
24218
+ const held = heldAgainstRoot();
24195
24219
  if (held >= ceilingUsd) return false;
24196
24220
  const fill = held + reserveUsd;
24197
24221
  return strictAtFill ? fill < ceilingUsd : fill <= ceilingUsd;
@@ -24205,7 +24229,8 @@ function preflightEstimate(input) {
24205
24229
  label: "orchestrator",
24206
24230
  reserveUsd,
24207
24231
  admitted: deniedBy === void 0,
24208
- ...deniedBy === void 0 ? {} : { deniedBy }
24232
+ ...deniedBy === void 0 ? {} : { deniedBy },
24233
+ ...ceilingUsd === void 0 ? {} : { heldAtEvaluationUsd: heldAgainstRoot() }
24209
24234
  });
24210
24235
  if (deniedBy === void 0) {
24211
24236
  committed += reserveUsd;
@@ -24224,7 +24249,7 @@ function preflightEstimate(input) {
24224
24249
  else if (maxSpawns !== void 0 && children >= maxSpawns) deniedBy = "orchestrator-max-spawns";
24225
24250
  else {
24226
24251
  if (orchestrateWave && ceilingUsd !== void 0) {
24227
- const remainder = ceilingUsd - committed - reservedForFinalizationUsd;
24252
+ const remainder = ceilingUsd - heldAgainstRoot();
24228
24253
  const projection = dispatchProjectionReserveUsd(gate, flatReserveUsd);
24229
24254
  if (remainder <= 0 || remainder <= projection) deniedBy = "budget";
24230
24255
  }
@@ -24234,13 +24259,14 @@ function preflightEstimate(input) {
24234
24259
  label,
24235
24260
  reserveUsd,
24236
24261
  admitted: deniedBy === void 0,
24237
- ...deniedBy === void 0 ? {} : { deniedBy }
24262
+ ...deniedBy === void 0 ? {} : { deniedBy },
24263
+ ...ceilingUsd === void 0 ? {} : { heldAtEvaluationUsd: heldAgainstRoot() }
24238
24264
  });
24239
24265
  if (deniedBy === void 0) {
24240
24266
  committed += reserveUsd;
24241
24267
  spawned += 1;
24242
24268
  children += 1;
24243
- }
24269
+ } else if (deniedBy === "budget") childrenDeniedByBudget += 1;
24244
24270
  }
24245
24271
  }
24246
24272
  const admitted = wave.filter((row) => row.admitted).length;
@@ -24258,6 +24284,19 @@ function preflightEstimate(input) {
24258
24284
  message: `the declared wave admits ${String(admitted)} of ${String(wave.length)} spawns; denied before any work: ${deniedLabels.join(", ")}`
24259
24285
  });
24260
24286
  }
24287
+ {
24288
+ const acceptance = input.orchestrator?.acceptance;
24289
+ const minSuccessfulFloor = acceptance?.childPolicy !== void 0 && acceptance.childPolicy !== "all-ok" ? acceptance.childPolicy.minSuccessful : void 0;
24290
+ const rosterFloor = Math.max(acceptance?.minSpawnedChildren ?? 0, minSuccessfulFloor ?? 0);
24291
+ if (rosterFloor > 0 && children < rosterFloor && childrenDeniedByBudget > 0) {
24292
+ const demandedBy = (acceptance?.minSpawnedChildren ?? 0) >= (minSuccessfulFloor ?? 0) ? "acceptance.minSpawnedChildren" : "acceptance.childPolicy.minSuccessful";
24293
+ say({
24294
+ severity: "error",
24295
+ code: "admission-below-roster-floor",
24296
+ message: `the declared wave seats ${String(children)} of the ${String(rosterFloor)} children ${demandedBy} demands (${String(childrenDeniedByBudget)} denied by budget): the run would pay for the seated work and still settle rejected; re-admission after a child settles frees money only when its settled spend stays below the released reserve`
24297
+ });
24298
+ }
24299
+ }
24261
24300
  if (ceilingUsd === void 0 && wave.length > 0) say({
24262
24301
  severity: "info",
24263
24302
  code: "no-usd-ceiling",
@@ -24481,6 +24520,7 @@ function preflightEstimate(input) {
24481
24520
  admission: {
24482
24521
  ...ceilingUsd === void 0 ? {} : { ceilingUsd },
24483
24522
  reservedForFinalizationUsd,
24523
+ synthesisReserveUsd: synthesisHoldUsd,
24484
24524
  wave,
24485
24525
  admitted,
24486
24526
  denied
@@ -25184,6 +25224,7 @@ function createEngine(options) {
25184
25224
  if (profile.countTokens !== void 0 && !["allow", "deny"].includes(profile.countTokens)) throw new ConfigError(`createEngine defaults.profiles['${name}'].countTokens must be 'allow' or 'deny'`);
25185
25225
  }
25186
25226
  if (options.defaults?.countTokens !== void 0 && !["allow", "deny"].includes(options.defaults.countTokens)) throw new ConfigError("createEngine defaults.countTokens must be 'allow' or 'deny'");
25227
+ if (options.telemetry?.quotaDeniedAgentError !== void 0 && typeof options.telemetry.quotaDeniedAgentError !== "boolean") throw new ConfigError("createEngine telemetry.quotaDeniedAgentError must be a boolean");
25187
25228
  validateDeterminismConfig(options.determinism);
25188
25229
  validateEngineQuotaConfig(options.quota);
25189
25230
  if (options.security?.argsHashSalt !== void 0 && (typeof options.security.argsHashSalt !== "string" || options.security.argsHashSalt === "")) throw new ConfigError("createEngine security.argsHashSalt must be a nonempty string when given");
@@ -25360,6 +25401,7 @@ function createEngine(options) {
25360
25401
  ...defaults.gates === void 0 ? {} : { gates: defaults.gates },
25361
25402
  ...defaults.countTokens === void 0 ? {} : { countTokens: defaults.countTokens }
25362
25403
  },
25404
+ ...options.telemetry === void 0 ? {} : { telemetry: options.telemetry },
25363
25405
  errorPolicy: wf.errorPolicy,
25364
25406
  dropped: [],
25365
25407
  cost: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.191.0",
3
+ "version": "1.193.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",