@rulvar/core 1.220.0 → 1.221.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.js +109 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12604,6 +12604,102 @@ async function runAgent(options) {
|
|
|
12604
12604
|
resolved: options.resolved
|
|
12605
12605
|
}, ...options.fallbacks ?? []];
|
|
12606
12606
|
const loopCursor = { index: 0 };
|
|
12607
|
+
/**
|
|
12608
|
+
* The drained-finalization grant (RV2204, the third parity rerun).
|
|
12609
|
+
* The exposure drain is terminal mid-work: spend only grows, so a
|
|
12610
|
+
* seat refused with no live holder left can never dispatch an
|
|
12611
|
+
* ordinary turn again. The third parity rerun killed three workers
|
|
12612
|
+
* ~30 turns into research with evidence pools of 17 and 22 under a
|
|
12613
|
+
* floor of 24 and a CONFIGURED finalization window: the drain came
|
|
12614
|
+
* before the window, and the window's play needs the very wire the
|
|
12615
|
+
* drain refuses. With `limits.finalizationReserve.maxOutputTokens`
|
|
12616
|
+
* declared, a drained seat that already did work now spends ONE
|
|
12617
|
+
* clamped finalization turn before its typed terminal: the output
|
|
12618
|
+
* clamp shrinks the turn's exposure estimate (the full estimate was
|
|
12619
|
+
* refused; the clamped one prices the summary allowance instead of
|
|
12620
|
+
* the whole per-turn cap), the finalization-window allowlist rides
|
|
12621
|
+
* as the turn's only tools so outstanding record_evidence calls can
|
|
12622
|
+
* land in parallel, and the executed calls persist through the
|
|
12623
|
+
* ordinary tool machinery. Best effort, exactly like the tool-budget
|
|
12624
|
+
* reserve turn: a refusal of even the clamped estimate warns and
|
|
12625
|
+
* keeps the typed drained terminal; a seat with NO completed turns
|
|
12626
|
+
* keeps dying free (the RV2002 zero-cost doctrine: nothing to
|
|
12627
|
+
* summarize, nothing paid).
|
|
12628
|
+
*/
|
|
12629
|
+
let drainFinalizationRan = false;
|
|
12630
|
+
const runDrainFinalization = async (refusal) => {
|
|
12631
|
+
const allow = limits.finalizationWindow?.allow;
|
|
12632
|
+
const allowedTools = allow === void 0 ? void 0 : options.tools?.contracts.filter((contract) => allow.includes(contract.name));
|
|
12633
|
+
const toolsRide = allowedTools !== void 0 && allowedTools.length > 0;
|
|
12634
|
+
const drainMessages = [...messages, {
|
|
12635
|
+
role: "user",
|
|
12636
|
+
parts: [{
|
|
12637
|
+
type: "text",
|
|
12638
|
+
text: `The run's in-flight exposure pool is drained; no further ordinary turns can dispatch (${refusal}). This is your single finalization turn` + (toolsRide ? ": record any outstanding evidence with the allowed tools IN THIS turn (parallel calls), then close" : `: close`) + ` with your best final summary of the work already done.`
|
|
12639
|
+
}]
|
|
12640
|
+
}];
|
|
12641
|
+
let grant;
|
|
12642
|
+
try {
|
|
12643
|
+
grant = await dispatchPhase({
|
|
12644
|
+
role: primaryRole,
|
|
12645
|
+
chain: loopChain,
|
|
12646
|
+
cursor: loopCursor,
|
|
12647
|
+
requestFor: (target) => {
|
|
12648
|
+
let req = buildRequest(target.resolved, projectHistory(drainMessages, providerOf(target.adapter)), limits, toolsRide ? allowedTools : void 0);
|
|
12649
|
+
const reserveMax = limits.finalizationReserve?.maxOutputTokens;
|
|
12650
|
+
if (reserveMax !== void 0) req = {
|
|
12651
|
+
...req,
|
|
12652
|
+
maxOutputTokens: Math.min(req.maxOutputTokens ?? reserveMax, reserveMax)
|
|
12653
|
+
};
|
|
12654
|
+
req = applyCachePolicy(req, target, options.cache);
|
|
12655
|
+
return applyOutputBudget(req, target, options.budget);
|
|
12656
|
+
},
|
|
12657
|
+
streamOptionsFor: (target) => {
|
|
12658
|
+
const drainStreamOptions = {
|
|
12659
|
+
idleTimeoutMs: limits.streamIdleTimeoutMs,
|
|
12660
|
+
signals: options.signal === void 0 ? [] : [options.signal],
|
|
12661
|
+
onUsage: (delta) => options.budget?.onUsage(delta, target.resolved.ref)
|
|
12662
|
+
};
|
|
12663
|
+
if (options.budget?.signal !== void 0) drainStreamOptions.budgetSignal = options.budget.signal;
|
|
12664
|
+
if (options.stream === true) drainStreamOptions.onDelta = (delta) => events?.emit({
|
|
12665
|
+
type: "agent:stream",
|
|
12666
|
+
delta
|
|
12667
|
+
});
|
|
12668
|
+
return drainStreamOptions;
|
|
12669
|
+
}
|
|
12670
|
+
});
|
|
12671
|
+
} catch (grantThrown) {
|
|
12672
|
+
if (!(grantThrown instanceof BudgetExhaustedError)) throw grantThrown;
|
|
12673
|
+
events?.emit({
|
|
12674
|
+
type: "log",
|
|
12675
|
+
level: "warn",
|
|
12676
|
+
msg: `the drained-finalization turn was skipped: ${grantThrown.message}`
|
|
12677
|
+
});
|
|
12678
|
+
return;
|
|
12679
|
+
}
|
|
12680
|
+
const { outcome: grantOutcome, target: grantTarget } = grant;
|
|
12681
|
+
servedBy = grantTarget.resolved.ref;
|
|
12682
|
+
usageApprox = usageApprox || grantOutcome.usageApprox;
|
|
12683
|
+
messages.push(assistantMsg(grantOutcome.turn, liftRetainedParts(grantOutcome.providerMetadata, grantTarget.adapter)));
|
|
12684
|
+
if (toolsRide && grantOutcome.turn.toolCalls.length > 0) try {
|
|
12685
|
+
await runToolCalls(grantOutcome.turn.toolCalls, []);
|
|
12686
|
+
} catch (toolThrown) {
|
|
12687
|
+
events?.emit({
|
|
12688
|
+
type: "log",
|
|
12689
|
+
level: "warn",
|
|
12690
|
+
msg: `a drained-finalization tool call failed: ${toolThrown instanceof Error ? toolThrown.message : String(toolThrown)}`
|
|
12691
|
+
});
|
|
12692
|
+
}
|
|
12693
|
+
events?.emit({
|
|
12694
|
+
type: "log",
|
|
12695
|
+
level: "info",
|
|
12696
|
+
msg: "the drained seat spent its finalization turn",
|
|
12697
|
+
data: {
|
|
12698
|
+
toolCalls: grantOutcome.turn.toolCalls.length,
|
|
12699
|
+
outputTokens: grantOutcome.usage.outputTokens
|
|
12700
|
+
}
|
|
12701
|
+
});
|
|
12702
|
+
};
|
|
12607
12703
|
loop: while (status === "ok" && !finishedViaTool) {
|
|
12608
12704
|
if (limits.timeoutMs !== void 0 && now() - startedAt >= limits.timeoutMs) {
|
|
12609
12705
|
status = "limit";
|
|
@@ -12657,8 +12753,12 @@ async function runAgent(options) {
|
|
|
12657
12753
|
});
|
|
12658
12754
|
} catch (thrown) {
|
|
12659
12755
|
if (!(thrown instanceof BudgetExhaustedError)) throw thrown;
|
|
12660
|
-
status = "error";
|
|
12661
12756
|
const typedReason = thrown.data?.reason;
|
|
12757
|
+
if (typedReason === "exposure-drained" && limits.finalizationReserve !== void 0 && turns > 1 && !drainFinalizationRan) {
|
|
12758
|
+
drainFinalizationRan = true;
|
|
12759
|
+
await runDrainFinalization(thrown.message);
|
|
12760
|
+
}
|
|
12761
|
+
status = "error";
|
|
12662
12762
|
agentError = {
|
|
12663
12763
|
kind: "budget",
|
|
12664
12764
|
retryable: false,
|
|
@@ -24792,10 +24892,16 @@ function preflightEstimate(input) {
|
|
|
24792
24892
|
spawn: label
|
|
24793
24893
|
});
|
|
24794
24894
|
}
|
|
24795
|
-
if (limits.finalizationReserve !== void 0 && limits.maxToolCalls === void 0 && limits.toolUnits === void 0) say({
|
|
24895
|
+
if (limits.finalizationReserve !== void 0 && limits.maxToolCalls === void 0 && limits.toolUnits === void 0 && input.run?.maxInFlightExposureUsd === void 0) say({
|
|
24796
24896
|
severity: "warning",
|
|
24797
24897
|
code: "inert-finalization-reserve",
|
|
24798
|
-
message: `spawn '${label}' sets finalizationReserve without maxToolCalls or toolUnits: no tool budget limiter exists for it to fire on`,
|
|
24898
|
+
message: `spawn '${label}' sets finalizationReserve without maxToolCalls or toolUnits: no tool budget limiter exists for it to fire on, and no in-flight exposure cap is declared for the drained-finalization grant (RV2204) to spend it at`,
|
|
24899
|
+
spawn: label
|
|
24900
|
+
});
|
|
24901
|
+
if (limits.finalizationWindow !== void 0 && limits.finalizationReserve?.maxOutputTokens === void 0 && input.run?.maxInFlightExposureUsd !== void 0) say({
|
|
24902
|
+
severity: "info",
|
|
24903
|
+
code: "drained-finalization-unfunded",
|
|
24904
|
+
message: `spawn '${label}' declares finalizationWindow under an in-flight exposure cap but no finalizationReserve.maxOutputTokens: a mid-work exposure drain refuses the wire the window needs, and without the reserve's clamped turn the seat dies with its window unplayed (RV2204); declare the reserve to fund one drained-finalization turn`,
|
|
24799
24905
|
spawn: label
|
|
24800
24906
|
});
|
|
24801
24907
|
if (limits.toolBudgetNotices === true && limits.maxToolCalls === void 0) say({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.221.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",
|