@rulvar/core 1.113.0 → 1.114.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
@@ -3130,6 +3130,17 @@ declare const QUOTA_WINDOW_MS = 6e4;
3130
3130
  * from each of them. The counters are rule-scoped: one rule matching
3131
3131
  * two models pools them under one cap; write one rule per model for
3132
3132
  * per-model buckets.
3133
+ *
3134
+ * Window semantics, named as the deliberate compromise it is (RV708):
3135
+ * every PerMinute cap counts over FIXED epoch-aligned 60 s windows
3136
+ * ({@link QUOTA_WINDOW_MS}), not a sliding minute. Each fixed window
3137
+ * enforces its cap exactly, and a burst placed astride a boundary can
3138
+ * therefore consume up to TWO caps inside one sliding 60 s; that
3139
+ * bounded burst is the price of cross-process parity (every reference
3140
+ * limiter in every process computes the same window from the same
3141
+ * clock with no shared sliding state), and provider-side minute
3142
+ * windows are themselves fuzzy. Size caps with the boundary burst in
3143
+ * mind; the semantics are pinned as intended, not scheduled to change.
3133
3144
  */
3134
3145
  interface QuotaRule {
3135
3146
  /** Adapter id, as in `concurrency.perProvider` keys. */
@@ -4761,6 +4772,18 @@ interface RunAgentOptions<S extends SchemaSpec = JsonSchema> {
4761
4772
  fallbacks?: PhaseTarget[];
4762
4773
  };
4763
4774
  /**
4775
+ * Opt-in policy-facts digest (RV709): when true AND a finalize
4776
+ * invocation fires, one additional REQUEST-ONLY user message
4777
+ * precedes the synthesis instruction, carrying the deterministic
4778
+ * runtime facts the loop observed (quota denials and recoveries,
4779
+ * tool budget pressure, the finalization window, recorded spend with
4780
+ * its cost basis), so the final model can cite the run's own live
4781
+ * evidence instead of underclaiming it. Never touches the durable
4782
+ * transcript, never enters spawn identity; unset keeps the finalize
4783
+ * request byte identical.
4784
+ */
4785
+ policyFacts?: boolean;
4786
+ /**
4764
4787
  * Summarize invocation target for compaction (M4-T03): resolved
4765
4788
  * through the chain with role 'summarize', falling back to the loop
4766
4789
  * model when routing resolves nothing. Compaction
@@ -7962,6 +7985,18 @@ interface OrchestrateSynthesis {
7962
7985
  /** Extra deterministic instruction lines appended to the synthesis prompt. */
7963
7986
  instructions?: string;
7964
7987
  /**
7988
+ * Opt-in policy-facts line in the 'single' synthesis prompt (RV709):
7989
+ * a deterministic digest of the settled children's durable
7990
+ * tool-budget facts (statuses, extension grants, finalization
7991
+ * windows and reserves), so the composing model can cite the run's
7992
+ * own observed evidence instead of underclaiming it. Folded ONLY
7993
+ * from replay-stable material (the settled results the journal
7994
+ * replays verbatim), so a resumed synthesis re-derives identical
7995
+ * prompt bytes; off by default, and the prompt stays byte identical
7996
+ * when unset (prompt bytes are journal identity).
7997
+ */
7998
+ policyFacts?: boolean;
7999
+ /**
7965
8000
  * Admission estimate for the synthesize invocation, like
7966
8001
  * AgentOpts.estCost: under a tight orchestrator cap the default
7967
8002
  * reserve (full maxOutputTokens pricing) can refuse the dispatch; an
package/dist/index.js CHANGED
@@ -11113,6 +11113,8 @@ async function runAgent(options) {
11113
11113
  let invocationCounter = 0;
11114
11114
  let transportRetries = 0;
11115
11115
  let schemaRecoveredTerminalExchanges = 0;
11116
+ let quotaDenials = 0;
11117
+ let quotaRecoveries = 0;
11116
11118
  const rateLimitObservations = /* @__PURE__ */ new Map();
11117
11119
  const roleUsageSnapshot = (role) => {
11118
11120
  const snapshot = /* @__PURE__ */ new Map();
@@ -12012,6 +12014,7 @@ async function runAgent(options) {
12012
12014
  }
12013
12015
  };
12014
12016
  const dispatchPhase = async (site) => {
12017
+ let deniedEpisode = false;
12015
12018
  for (;;) {
12016
12019
  const target = site.chain[site.cursor.index] ?? site.chain[0];
12017
12020
  let tries = 0;
@@ -12083,6 +12086,13 @@ async function runAgent(options) {
12083
12086
  msg: `the shared quota limiter failed to reconcile a reservation: ${detail}`
12084
12087
  });
12085
12088
  }
12089
+ if (outcome.quotaDenied === true) {
12090
+ quotaDenials += 1;
12091
+ deniedEpisode = true;
12092
+ } else if (deniedEpisode && outcome.neverDispatched !== true) {
12093
+ quotaRecoveries += 1;
12094
+ deniedEpisode = false;
12095
+ }
12086
12096
  if (outcome.quotaDenied !== true && outcome.neverDispatched !== true) {
12087
12097
  const accounted = recordUsage(outcome.usage, outcome.reported, target.adapter.id, target.resolved.ref, site.role, outcome.usageViolation);
12088
12098
  const namespace = outcome.providerMetadata?.[target.adapter.id];
@@ -12685,13 +12695,37 @@ async function runAgent(options) {
12685
12695
  }
12686
12696
  if (proceed) {
12687
12697
  turns += 1;
12688
- const synthesisMessages = [...messages, {
12689
- role: "user",
12690
- parts: [{
12691
- type: "text",
12692
- text: FINALIZE_SYNTHESIS_INSTRUCTION
12693
- }]
12694
- }];
12698
+ const policyFactsLines = () => {
12699
+ const lines = ["POLICY FACTS (request-only runtime digest): deterministic facts this run observed; cite the ones your answer relies on."];
12700
+ if (options.quota !== void 0) lines.push(`quota: ${String(quotaDenials)} denial(s), ${String(quotaRecoveries)} recovered`);
12701
+ if (limits.maxToolCalls !== void 0 || limits.toolUnits !== void 0 || extension !== void 0) {
12702
+ const cap = effectiveMaxToolCalls();
12703
+ let budgetLine = `tool budget: ${String(toolCallsUsed)}${cap === void 0 ? "" : ` of ${String(cap)}`} calls used`;
12704
+ if (extension !== void 0) budgetLine += `; extensions granted: ${String(extensionGrants)}`;
12705
+ lines.push(budgetLine);
12706
+ }
12707
+ if (finalizationWindow !== void 0) lines.push(`finalization window: ${windowEntered ? "entered" : "not entered"}`);
12708
+ const spend = recordedSpend();
12709
+ lines.push(`recorded spend: $${spend.usd.toFixed(4)} (${spend.basis})` + (spend.basis === "aggregate-estimate" ? "; per-call records did not cover all usage, treat the number as an estimate" : ""));
12710
+ return lines;
12711
+ };
12712
+ const synthesisMessages = [
12713
+ ...messages,
12714
+ ...options.policyFacts === true ? [{
12715
+ role: "user",
12716
+ parts: [{
12717
+ type: "text",
12718
+ text: policyFactsLines().join("\n")
12719
+ }]
12720
+ }] : [],
12721
+ {
12722
+ role: "user",
12723
+ parts: [{
12724
+ type: "text",
12725
+ text: FINALIZE_SYNTHESIS_INSTRUCTION
12726
+ }]
12727
+ }
12728
+ ];
12695
12729
  let finalizeDispatch;
12696
12730
  try {
12697
12731
  finalizeDispatch = await dispatchPhase({
@@ -17384,6 +17418,8 @@ function validateOrchestrateOptions(opts) {
17384
17418
  ].includes(synthesis.effort)) throw new ConfigError(`orchestrate synthesis.effort must be one of 'low' | 'medium' | 'high' | 'xhigh' | 'max'; got ${JSON.stringify(synthesis.effort)}`);
17385
17419
  if (synthesis.limits !== void 0) validateUsageLimits(synthesis.limits, "orchestrate synthesis.limits");
17386
17420
  if (synthesis.instructions !== void 0 && typeof synthesis.instructions !== "string") throw new ConfigError(`orchestrate synthesis.instructions must be a string; got ${typeof synthesis.instructions}`);
17421
+ const facts = synthesis;
17422
+ if (facts.policyFacts !== void 0 && typeof facts.policyFacts !== "boolean") throw new ConfigError(`orchestrate synthesis.policyFacts must be a boolean; got ${typeof facts.policyFacts}`);
17387
17423
  if (synthesis.estCost !== void 0) requireNonNegativeNumber(synthesis.estCost, "orchestrate synthesis.estCost");
17388
17424
  }
17389
17425
  const spec = opts.budget;
@@ -18922,6 +18958,26 @@ function makeOrchestratorWorkflow(goal, opts) {
18922
18958
  ...repeatedClaims === void 0 ? [] : ["Repeated claims across children were deduplicated before this prompt: only the first occurrence of each repeated line remains in the digest, and the REPEATED CLAIMS index below lists each one with its reporters."],
18923
18959
  ...spec.instructions === void 0 ? [] : [spec.instructions],
18924
18960
  ...finishValidationPromptLines(validationSpec),
18961
+ ...spec.policyFacts === true ? [(() => {
18962
+ const byStatus = {};
18963
+ let extensionsGranted = 0;
18964
+ let windowsEntered = 0;
18965
+ let reservesUsed = 0;
18966
+ for (const [, record] of settledEntries) {
18967
+ const settled = record.settled;
18968
+ byStatus[settled.status] = (byStatus[settled.status] ?? 0) + 1;
18969
+ extensionsGranted += settled.toolBudget?.extensionsGranted ?? 0;
18970
+ if (settled.toolBudget?.finalizationWindowEntered === true) windowsEntered += 1;
18971
+ if (settled.toolBudget?.finalizationReserveUsed === true) reservesUsed += 1;
18972
+ }
18973
+ return `POLICY FACTS: ${JSON.stringify({
18974
+ children: settledEntries.length,
18975
+ byStatus: Object.fromEntries(Object.keys(byStatus).sort().map((status) => [status, byStatus[status]])),
18976
+ extensionsGranted,
18977
+ finalizationWindowsEntered: windowsEntered,
18978
+ finalizationReservesUsed: reservesUsed
18979
+ })}`;
18980
+ })()] : [],
18925
18981
  `GOAL: ${goal}`,
18926
18982
  `DRAFT: ${draftJson}`,
18927
18983
  `DIGEST: ${digestJson}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.113.0",
3
+ "version": "1.114.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",