@rulvar/core 1.101.0 → 1.102.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 +26 -5
- package/dist/index.js +100 -38
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -4719,26 +4719,47 @@ interface RunAgentOptions<S extends SchemaSpec = JsonSchema> {
|
|
|
4719
4719
|
* already promised the raised cap), never re-admitted or re-announced,
|
|
4720
4720
|
* and a restored window entry keeps the summary's
|
|
4721
4721
|
* finalizationWindowEntered truthful even when a later grant moved the
|
|
4722
|
-
* counts back out of the window.
|
|
4723
|
-
*
|
|
4724
|
-
*
|
|
4722
|
+
* counts back out of the window.
|
|
4723
|
+
*
|
|
4724
|
+
* Both hooks are AWAITED before the thing they authorize becomes
|
|
4725
|
+
* observable (RV601): a grant lifts no expiry and queues no notice
|
|
4726
|
+
* until its decision is durable, and the window regime binds no call
|
|
4727
|
+
* until its entry is. A rejected append therefore leaves the grant
|
|
4728
|
+
* unissued and the entry unrecorded, and the rejection propagates
|
|
4729
|
+
* exactly like a failed boundary checkpoint rather than being
|
|
4730
|
+
* swallowed. Pressure notices stay events and are never journaled.
|
|
4731
|
+
* Absent, the loop is byte-identical to before.
|
|
4725
4732
|
*/
|
|
4726
4733
|
toolBudgetDurability?: {
|
|
4727
4734
|
restored?: {
|
|
4728
4735
|
extensionsGranted: number;
|
|
4729
4736
|
finalizationWindowEntered: boolean;
|
|
4737
|
+
/**
|
|
4738
|
+
* The effective cap the journaled grant announced (RV602). It
|
|
4739
|
+
* anchors the resumed ceiling, because the live `maxToolCalls`
|
|
4740
|
+
* and `increment` are not part of the dispatch identity and may
|
|
4741
|
+
* legitimately drift between segments: without the anchor the two
|
|
4742
|
+
* recovery paths (pure replay, which reads the journal, and live
|
|
4743
|
+
* resume, which recomputed) disagreed, and a promise already made
|
|
4744
|
+
* to the model could be silently revoked. Validated as a
|
|
4745
|
+
* persistent inlet: a non-integer, or one below the base cap, is
|
|
4746
|
+
* ignored with a warning, leaving the count derivation as the
|
|
4747
|
+
* floor. Grants taken AFTER the restore point still measure the
|
|
4748
|
+
* current increment from this anchor.
|
|
4749
|
+
*/
|
|
4750
|
+
cap?: number;
|
|
4730
4751
|
};
|
|
4731
4752
|
onExtensionGrant?: (grant: {
|
|
4732
4753
|
grant: number;
|
|
4733
4754
|
maxExtensions: number;
|
|
4734
4755
|
toolCallsUsed: number;
|
|
4735
4756
|
cap: number;
|
|
4736
|
-
}) => void
|
|
4757
|
+
}) => Promise<void>;
|
|
4737
4758
|
onWindowEntry?: (entry: {
|
|
4738
4759
|
remaining: number;
|
|
4739
4760
|
reserveCalls: number;
|
|
4740
4761
|
budget: FinalizationWindowBudget;
|
|
4741
|
-
}) => void
|
|
4762
|
+
}) => Promise<void>;
|
|
4742
4763
|
};
|
|
4743
4764
|
/** Emits agent:stream deltas when true (telemetry only). */
|
|
4744
4765
|
stream?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -10927,7 +10927,17 @@ async function runAgent(options) {
|
|
|
10927
10927
|
let extensionGrants = 0;
|
|
10928
10928
|
let extensionEvidenceAtLastGrant = 0;
|
|
10929
10929
|
const pendingExtensionNotices = [];
|
|
10930
|
-
|
|
10930
|
+
/**
|
|
10931
|
+
* The ceiling the effective cap is measured from (RV602). The base cap
|
|
10932
|
+
* normally, so a fresh loop is byte-identical arithmetic; the journaled
|
|
10933
|
+
* cap of the restored grant when a resume carried one, so drifting live
|
|
10934
|
+
* limits cannot revoke a raise the journal already recorded. Grants
|
|
10935
|
+
* taken after the restore point count from here at the CURRENT
|
|
10936
|
+
* increment, which is the live policy doing what it should.
|
|
10937
|
+
*/
|
|
10938
|
+
let capBase = limits.maxToolCalls;
|
|
10939
|
+
let grantsOverCapBase = 0;
|
|
10940
|
+
const effectiveMaxToolCalls = () => capBase === void 0 ? void 0 : extension === void 0 ? capBase : capBase + grantsOverCapBase * extension.increment;
|
|
10931
10941
|
/** The limiter that ended the loop; rides the RV304 pressure snapshot. */
|
|
10932
10942
|
let limitLimiter;
|
|
10933
10943
|
/** True once the finalization reserve summary turn actually ran. */
|
|
@@ -10979,7 +10989,7 @@ async function runAgent(options) {
|
|
|
10979
10989
|
* join the history.
|
|
10980
10990
|
*/
|
|
10981
10991
|
const tryToolBudgetGrant = () => {
|
|
10982
|
-
if (extension === void 0 || limits.maxToolCalls === void 0) return false;
|
|
10992
|
+
if (extension === void 0 || limits.maxToolCalls === void 0 || capBase === void 0) return false;
|
|
10983
10993
|
if (extensionGrants >= extension.maxExtensions) return false;
|
|
10984
10994
|
if (extension.requireNewEvidence !== false) {
|
|
10985
10995
|
if ((guard?.evidenceCount() ?? 0) <= extensionEvidenceAtLastGrant) return false;
|
|
@@ -10989,22 +10999,28 @@ async function runAgent(options) {
|
|
|
10989
10999
|
const floor = extension.minHeadroomUsd ?? 0;
|
|
10990
11000
|
if (floor > 0 ? remaining < floor : remaining <= 0) return false;
|
|
10991
11001
|
}
|
|
10992
|
-
extensionGrants
|
|
10993
|
-
|
|
10994
|
-
const
|
|
10995
|
-
|
|
10996
|
-
|
|
11002
|
+
const grant = extensionGrants + 1;
|
|
11003
|
+
const cap = capBase + (grantsOverCapBase + 1) * extension.increment;
|
|
11004
|
+
const commit = () => {
|
|
11005
|
+
extensionGrants = grant;
|
|
11006
|
+
grantsOverCapBase += 1;
|
|
11007
|
+
extensionEvidenceAtLastGrant = guard?.evidenceCount() ?? 0;
|
|
11008
|
+
pendingExtensionNotices.push(toolBudgetExtensionNoticeText(grant, extension.maxExtensions, toolCallsUsed, cap));
|
|
11009
|
+
events?.emit({
|
|
11010
|
+
type: "log",
|
|
11011
|
+
level: "info",
|
|
11012
|
+
msg: `tool budget extended (grant ${String(grant)}/${String(extension.maxExtensions)}): maxToolCalls now ${String(cap)}`
|
|
11013
|
+
});
|
|
11014
|
+
return true;
|
|
11015
|
+
};
|
|
11016
|
+
const durable = options.toolBudgetDurability?.onExtensionGrant;
|
|
11017
|
+
if (durable === void 0) return commit();
|
|
11018
|
+
return durable({
|
|
11019
|
+
grant,
|
|
10997
11020
|
maxExtensions: extension.maxExtensions,
|
|
10998
11021
|
toolCallsUsed,
|
|
10999
11022
|
cap
|
|
11000
|
-
});
|
|
11001
|
-
pendingExtensionNotices.push(toolBudgetExtensionNoticeText(extensionGrants, extension.maxExtensions, toolCallsUsed, cap));
|
|
11002
|
-
events?.emit({
|
|
11003
|
-
type: "log",
|
|
11004
|
-
level: "info",
|
|
11005
|
-
msg: `tool budget extended (grant ${String(extensionGrants)}/${String(extension.maxExtensions)}): maxToolCalls now ${String(cap)}`
|
|
11006
|
-
});
|
|
11007
|
-
return true;
|
|
11023
|
+
}).then(commit);
|
|
11008
11024
|
};
|
|
11009
11025
|
const flushExtensionNotices = () => {
|
|
11010
11026
|
for (const text of pendingExtensionNotices.splice(0)) messages.push({
|
|
@@ -11058,19 +11074,26 @@ async function runAgent(options) {
|
|
|
11058
11074
|
if (finalizationWindow === void 0 || windowNoticeFired) return;
|
|
11059
11075
|
const state = windowActive();
|
|
11060
11076
|
if (state === void 0) return;
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11077
|
+
const commit = () => {
|
|
11078
|
+
windowEntered = true;
|
|
11079
|
+
windowNoticeFired = true;
|
|
11080
|
+
pendingWindowNotices.push(finalizationWindowNoticeText(state.remaining, finalizationWindow.reserveCalls, state.budget));
|
|
11081
|
+
events?.emit({
|
|
11082
|
+
type: "log",
|
|
11083
|
+
level: "info",
|
|
11084
|
+
msg: `finalization window entered: ${String(state.remaining)} of the reserved final ${String(finalizationWindow.reserveCalls)} ${state.budget} remain`
|
|
11085
|
+
});
|
|
11086
|
+
};
|
|
11087
|
+
const durable = options.toolBudgetDurability?.onWindowEntry;
|
|
11088
|
+
if (durable === void 0) {
|
|
11089
|
+
commit();
|
|
11090
|
+
return;
|
|
11091
|
+
}
|
|
11092
|
+
return durable({
|
|
11064
11093
|
remaining: state.remaining,
|
|
11065
11094
|
reserveCalls: finalizationWindow.reserveCalls,
|
|
11066
11095
|
budget: state.budget
|
|
11067
|
-
});
|
|
11068
|
-
pendingWindowNotices.push(finalizationWindowNoticeText(state.remaining, finalizationWindow.reserveCalls, state.budget));
|
|
11069
|
-
events?.emit({
|
|
11070
|
-
type: "log",
|
|
11071
|
-
level: "info",
|
|
11072
|
-
msg: `finalization window entered: ${String(state.remaining)} of the reserved final ${String(finalizationWindow.reserveCalls)} ${state.budget} remain`
|
|
11073
|
-
});
|
|
11096
|
+
}).then(commit);
|
|
11074
11097
|
};
|
|
11075
11098
|
const flushWindowNotices = () => {
|
|
11076
11099
|
for (const text of pendingWindowNotices.splice(0)) messages.push({
|
|
@@ -11127,6 +11150,7 @@ async function runAgent(options) {
|
|
|
11127
11150
|
extensionGrants = Math.min(extension.maxExtensions, Math.ceil((toolCallsUsed - limits.maxToolCalls) / extension.increment));
|
|
11128
11151
|
extensionEvidenceAtLastGrant = guard?.evidenceCount() ?? 0;
|
|
11129
11152
|
}
|
|
11153
|
+
const derivedCap = limits.maxToolCalls === void 0 || extension === void 0 ? void 0 : limits.maxToolCalls + extensionGrants * extension.increment;
|
|
11130
11154
|
const durableRestored = options.toolBudgetDurability?.restored;
|
|
11131
11155
|
if (extension !== void 0 && durableRestored !== void 0) {
|
|
11132
11156
|
const journaled = Math.min(extension.maxExtensions, durableRestored.extensionsGranted);
|
|
@@ -11135,6 +11159,16 @@ async function runAgent(options) {
|
|
|
11135
11159
|
extensionEvidenceAtLastGrant = guard?.evidenceCount() ?? 0;
|
|
11136
11160
|
}
|
|
11137
11161
|
}
|
|
11162
|
+
grantsOverCapBase = extensionGrants;
|
|
11163
|
+
const journaledCap = durableRestored?.cap;
|
|
11164
|
+
if (journaledCap !== void 0 && limits.maxToolCalls !== void 0 && derivedCap !== void 0) if (Number.isSafeInteger(journaledCap) && journaledCap >= limits.maxToolCalls) {
|
|
11165
|
+
capBase = Math.max(journaledCap, derivedCap);
|
|
11166
|
+
grantsOverCapBase = 0;
|
|
11167
|
+
} else events?.emit({
|
|
11168
|
+
type: "log",
|
|
11169
|
+
level: "warn",
|
|
11170
|
+
msg: `restored tool budget cap ${String(journaledCap)} is not an integer at or above the base cap ${String(limits.maxToolCalls)}; ignoring it and deriving from the counts`
|
|
11171
|
+
});
|
|
11138
11172
|
if (windowActive() !== void 0 || finalizationWindow !== void 0 && durableRestored?.finalizationWindowEntered === true) {
|
|
11139
11173
|
windowEntered = true;
|
|
11140
11174
|
windowNoticeFired = true;
|
|
@@ -11231,7 +11265,10 @@ async function runAgent(options) {
|
|
|
11231
11265
|
return cap !== void 0 && toolCallsUsed >= cap ? "maxToolCalls" : guard !== void 0 && guard.unitsExhausted() ? "toolUnits" : void 0;
|
|
11232
11266
|
};
|
|
11233
11267
|
let expiredLimiter = expiryOf();
|
|
11234
|
-
if (expiredLimiter === "maxToolCalls" && call.name !== options.terminalTool?.name
|
|
11268
|
+
if (expiredLimiter === "maxToolCalls" && call.name !== options.terminalTool?.name) {
|
|
11269
|
+
const attempt = tryToolBudgetGrant();
|
|
11270
|
+
if (typeof attempt === "boolean" ? attempt : await attempt) expiredLimiter = expiryOf();
|
|
11271
|
+
}
|
|
11235
11272
|
if (expiredLimiter !== void 0) {
|
|
11236
11273
|
const tail = calls.slice(index);
|
|
11237
11274
|
const terminalName = options.terminalTool?.name;
|
|
@@ -11419,10 +11456,14 @@ async function runAgent(options) {
|
|
|
11419
11456
|
};
|
|
11420
11457
|
}
|
|
11421
11458
|
if (finalizationWindow !== void 0) {
|
|
11422
|
-
maybeMarkWindowEntry();
|
|
11459
|
+
const entry = maybeMarkWindowEntry();
|
|
11460
|
+
if (entry !== void 0) await entry;
|
|
11423
11461
|
let windowState = windowActive();
|
|
11424
11462
|
if (windowState !== void 0 && !windowAllows(gatedCall.name)) {
|
|
11425
|
-
if (windowState.budget === "tool calls" && extension !== void 0 && windowState.remaining + extension.increment > finalizationWindow.reserveCalls
|
|
11463
|
+
if (windowState.budget === "tool calls" && extension !== void 0 && windowState.remaining + extension.increment > finalizationWindow.reserveCalls) {
|
|
11464
|
+
const attempt = tryToolBudgetGrant();
|
|
11465
|
+
if (typeof attempt === "boolean" ? attempt : await attempt) windowState = windowActive();
|
|
11466
|
+
}
|
|
11426
11467
|
if (windowState !== void 0 && !windowAllows(gatedCall.name)) {
|
|
11427
11468
|
events?.emit({
|
|
11428
11469
|
type: "tool:end",
|
|
@@ -11492,7 +11533,8 @@ async function runAgent(options) {
|
|
|
11492
11533
|
});
|
|
11493
11534
|
}
|
|
11494
11535
|
}
|
|
11495
|
-
maybeMarkWindowEntry();
|
|
11536
|
+
const lastEntry = maybeMarkWindowEntry();
|
|
11537
|
+
if (lastEntry !== void 0) await lastEntry;
|
|
11496
11538
|
return {
|
|
11497
11539
|
parts,
|
|
11498
11540
|
limitHit: false
|
|
@@ -14847,10 +14889,11 @@ function createCtx(internals, rootWorkflow) {
|
|
|
14847
14889
|
runAgentOptions.toolBudgetDurability = {
|
|
14848
14890
|
...durableRestored === void 0 ? {} : { restored: {
|
|
14849
14891
|
extensionsGranted: durableRestored.extensionsGranted,
|
|
14850
|
-
finalizationWindowEntered: durableRestored.finalizationWindowEntered
|
|
14892
|
+
finalizationWindowEntered: durableRestored.finalizationWindowEntered,
|
|
14893
|
+
...durableRestored.cap === void 0 ? {} : { cap: durableRestored.cap }
|
|
14851
14894
|
} },
|
|
14852
|
-
onExtensionGrant: (grant) => {
|
|
14853
|
-
internals.replayer.appendSinglePhase({
|
|
14895
|
+
onExtensionGrant: async (grant) => {
|
|
14896
|
+
await internals.replayer.appendSinglePhase({
|
|
14854
14897
|
scope: state.scope,
|
|
14855
14898
|
key: "",
|
|
14856
14899
|
kind: "decision",
|
|
@@ -14863,8 +14906,8 @@ function createCtx(internals, rootWorkflow) {
|
|
|
14863
14906
|
}
|
|
14864
14907
|
});
|
|
14865
14908
|
},
|
|
14866
|
-
onWindowEntry: (entry) => {
|
|
14867
|
-
internals.replayer.appendSinglePhase({
|
|
14909
|
+
onWindowEntry: async (entry) => {
|
|
14910
|
+
await internals.replayer.appendSinglePhase({
|
|
14868
14911
|
scope: state.scope,
|
|
14869
14912
|
key: "",
|
|
14870
14913
|
kind: "decision",
|
|
@@ -18431,13 +18474,30 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18431
18474
|
}
|
|
18432
18475
|
}, callingState.spanId);
|
|
18433
18476
|
};
|
|
18434
|
-
const
|
|
18435
|
-
|
|
18477
|
+
const draftValue = draft ?? null;
|
|
18478
|
+
const draftHash = createHash("sha256").update(jcsSerialize(draftValue), "utf8").digest("hex");
|
|
18479
|
+
const validatorNames = validationSpec.validators.map((validator) => validator.name);
|
|
18480
|
+
/**
|
|
18481
|
+
* A journaled skip is the authority only for the generation and
|
|
18482
|
+
* the draft it judged (RV603). The documented remedy for a
|
|
18483
|
+
* broken contract is to fix it and resume, and a verdict that
|
|
18484
|
+
* outlives its contract defeats exactly that: the run would
|
|
18485
|
+
* settle ok carrying output the CURRENT contract rejects.
|
|
18486
|
+
* Bound by three facts, in descending strength: the contract
|
|
18487
|
+
* identity (the same `contractGenerationCurrent` test the
|
|
18488
|
+
* finish-validation decisions already use), the draft the
|
|
18489
|
+
* verdict actually judged, and the validator names that
|
|
18490
|
+
* rendered it. Entries written before this field existed carry
|
|
18491
|
+
* no draftHash and stay reusable, so journals in flight roll
|
|
18492
|
+
* forward byte for byte.
|
|
18493
|
+
*/
|
|
18494
|
+
const applies = (value) => contractGenerationCurrent(value) && (value.draftHash === void 0 || value.draftHash === draftHash) && (!Array.isArray(value.validators) || JSON.stringify(value.validators) === JSON.stringify(validatorNames));
|
|
18495
|
+
const prior = internals.replayer.snapshot().filter((entry) => entry.kind === "decision" && entry.scope === callingState.scope && entry.key === skipKey).at(-1);
|
|
18496
|
+
if (prior !== void 0 && applies(prior.value)) {
|
|
18436
18497
|
synthesisSkippedByValidDraft = true;
|
|
18437
18498
|
announceSkip(prior.seq);
|
|
18438
18499
|
return draft;
|
|
18439
18500
|
}
|
|
18440
|
-
const draftValue = draft ?? null;
|
|
18441
18501
|
const input = {
|
|
18442
18502
|
result: draftValue,
|
|
18443
18503
|
text: typeof draftValue === "string" ? draftValue : JSON.stringify(draftValue),
|
|
@@ -18467,7 +18527,9 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18467
18527
|
value: {
|
|
18468
18528
|
decisionType: "orchestrator_synthesis_skip",
|
|
18469
18529
|
reason: "synthesis_skipped_by_valid_draft",
|
|
18470
|
-
validators:
|
|
18530
|
+
validators: validatorNames,
|
|
18531
|
+
...validationSpec.contract === void 0 ? {} : { contractHash: validationSpec.contract.hash },
|
|
18532
|
+
draftHash
|
|
18471
18533
|
}
|
|
18472
18534
|
});
|
|
18473
18535
|
if (orchestratorAccount !== void 0 && (opts?.budget?.synthesisReserveUsd ?? 0) > 0) internals.budget.releaseSynthesisReserve(orchestratorAccount);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.102.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",
|