@rulvar/plan 1.234.0 → 1.236.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 -0
- package/dist/index.js +56 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -984,6 +984,32 @@ interface PlanRunnerOptions {
|
|
|
984
984
|
* enabling it changes toolsetHash by design. Default false.
|
|
985
985
|
*/
|
|
986
986
|
kbPropose?: boolean;
|
|
987
|
+
/**
|
|
988
|
+
* Disarms the finish gate (RV3202). By default the coordination
|
|
989
|
+
* finish REFUSES while any plan node is ready or running, naming the
|
|
990
|
+
* stragglers, because the plan is the extension's authority: a root
|
|
991
|
+
* that finishes over a running node used to settle a bare ok while
|
|
992
|
+
* the exit barrier cancelled the node (the 2026-08-11 experiment's
|
|
993
|
+
* blocker). Opting out restores that pre-RV3202 behavior for hosts
|
|
994
|
+
* whose acceptance policy already owns the boundary. Default false.
|
|
995
|
+
*/
|
|
996
|
+
allowEarlyFinish?: boolean;
|
|
997
|
+
/**
|
|
998
|
+
* The resume posture toward a drifted profile registry (RV3203, the
|
|
999
|
+
* 2026-08-11 experiment's resume blocker). The registry identity
|
|
1000
|
+
* frozen in `termination.init` (profile names mapped to ladder
|
|
1001
|
+
* lengths) is recomputed from the LIVE profiles on every resume:
|
|
1002
|
+
* under `'refuse'` (the default) a mismatch terminates the resumed
|
|
1003
|
+
* run typed BEFORE any model call, because ladders are live values
|
|
1004
|
+
* the journal cannot rebuild and "the journal wins" is not honorable
|
|
1005
|
+
* for them; `'warn'` downgrades the mismatch to the
|
|
1006
|
+
* `termination:config-drift` event and proceeds under the live
|
|
1007
|
+
* registry. Either way the mismatch is reported; a journal recorded
|
|
1008
|
+
* before the hash shipped skips the check (absence means NOT
|
|
1009
|
+
* RECORDED). The projection covers profile names and ladder lengths,
|
|
1010
|
+
* not the models inside same-length rungs.
|
|
1011
|
+
*/
|
|
1012
|
+
profileDrift?: "refuse" | "warn";
|
|
987
1013
|
}
|
|
988
1014
|
/**
|
|
989
1015
|
* Builds the PlanRunner orchestrator extension.
|
package/dist/index.js
CHANGED
|
@@ -1996,6 +1996,7 @@ const TERMINATION_DENIED_KEY_KIND = "termination.denied";
|
|
|
1996
1996
|
* the `orchestratePlanned` convenience surface.
|
|
1997
1997
|
*/
|
|
1998
1998
|
function planRunner(options) {
|
|
1999
|
+
if (options?.profileDrift !== void 0 && options.profileDrift !== "refuse" && options.profileDrift !== "warn") throw new ConfigError(`PlanRunnerOptions.profileDrift must be 'refuse' or 'warn'; got ` + JSON.stringify(options.profileDrift));
|
|
1999
2000
|
let io;
|
|
2000
2001
|
let planScope = "plan";
|
|
2001
2002
|
let rootScope = "";
|
|
@@ -3684,6 +3685,11 @@ function planRunner(options) {
|
|
|
3684
3685
|
"plan_revise (add_task, amend_task, park_task, unpark_task, cancel_task,",
|
|
3685
3686
|
"reprioritize, rewire_deps, waive_dep), inspect it with plan_view, and sleep",
|
|
3686
3687
|
"with wait_for_events; the ENGINE schedules ready plan nodes for you.",
|
|
3688
|
+
...options?.allowEarlyFinish === true ? [] : [
|
|
3689
|
+
"finish is refused while any plan node is ready or running: wait for them",
|
|
3690
|
+
"with wait_for_events, or close them deliberately with plan_revise",
|
|
3691
|
+
"(cancel_task or park_task) before finishing."
|
|
3692
|
+
],
|
|
3687
3693
|
...options?.kbPropose === true ? ["kb_propose records ONE quarantined model observation about a ladder tier of a", "journaled lineage; it renders into no prompt and commits nothing during the run."] : []
|
|
3688
3694
|
],
|
|
3689
3695
|
boot: async (bound) => {
|
|
@@ -3711,6 +3717,36 @@ function planRunner(options) {
|
|
|
3711
3717
|
liveValue
|
|
3712
3718
|
});
|
|
3713
3719
|
}
|
|
3720
|
+
const liveUsd = {
|
|
3721
|
+
runBudgetUsdCeiling: io.runCeilingUsd ?? 0,
|
|
3722
|
+
orchestratorCapUsd: io.orchestratorCapUsd ?? 0,
|
|
3723
|
+
finalizeReserveUsd: io.finalizeReserveUsd ?? 0
|
|
3724
|
+
};
|
|
3725
|
+
for (const [field, liveValue] of Object.entries(liveUsd)) {
|
|
3726
|
+
const frozenValue = frozen[field];
|
|
3727
|
+
if (typeof frozenValue === "number" && frozenValue !== 0 && frozenValue !== liveValue) io.emit({
|
|
3728
|
+
type: "termination:config-drift",
|
|
3729
|
+
field,
|
|
3730
|
+
frozenValue,
|
|
3731
|
+
liveValue
|
|
3732
|
+
});
|
|
3733
|
+
}
|
|
3734
|
+
const frozenRegistryHash = folded.init.profileRegistrySnapshotHash;
|
|
3735
|
+
if (typeof frozenRegistryHash === "string") {
|
|
3736
|
+
const liveRegistryHash = profileRegistrySnapshotHash(io.profiles);
|
|
3737
|
+
if (frozenRegistryHash !== liveRegistryHash) {
|
|
3738
|
+
io.emit({
|
|
3739
|
+
type: "termination:config-drift",
|
|
3740
|
+
field: "profileRegistrySnapshotHash",
|
|
3741
|
+
frozenValue: frozenRegistryHash,
|
|
3742
|
+
liveValue: liveRegistryHash
|
|
3743
|
+
});
|
|
3744
|
+
if (options?.profileDrift !== "warn") {
|
|
3745
|
+
io.terminate?.(new ConfigError(`resume: the profile registry drifted since run '${io.runId}' started (frozen ${frozenRegistryHash.slice(0, 12)}, live ${liveRegistryHash.slice(0, 12)}): profile names or ladder lengths changed, and the journaled plan cannot be honored under a different registry. Resume with the original profiles, or pass PlanRunnerOptions.profileDrift: 'warn' to proceed under the live registry deliberately`));
|
|
3746
|
+
return;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3714
3750
|
} else {
|
|
3715
3751
|
const limits = validateTerminationLimits({
|
|
3716
3752
|
maxRevisionsPerRun: options?.maxRevisionsPerRun ?? 32,
|
|
@@ -3787,6 +3823,17 @@ function planRunner(options) {
|
|
|
3787
3823
|
absorbPlan();
|
|
3788
3824
|
return !Object.values(fold.plan.nodes).some((node) => node.status === "ready" || node.status === "running");
|
|
3789
3825
|
},
|
|
3826
|
+
finishGate: () => {
|
|
3827
|
+
if (options?.allowEarlyFinish === true) return { ok: true };
|
|
3828
|
+
absorbPlan();
|
|
3829
|
+
const live = Object.values(fold.plan.nodes).filter((node) => node.status === "ready" || node.status === "running").sort((a, b) => a.nodeId.localeCompare(b.nodeId));
|
|
3830
|
+
if (live.length === 0) return { ok: true };
|
|
3831
|
+
const named = live.slice(0, 8).map((node) => `${node.nodeId} (${node.logicalTaskId}, ${node.status})`).join(", ");
|
|
3832
|
+
return {
|
|
3833
|
+
ok: false,
|
|
3834
|
+
reason: `finish refused: the plan still has ${String(live.length)} node(s) ready or running: ${named}${live.length > 8 ? ", ..." : ""}. Wait for them with wait_for_events, or close them deliberately with plan_revise (cancel_task or park_task), then call finish again`
|
|
3835
|
+
};
|
|
3836
|
+
},
|
|
3790
3837
|
digestExtras: () => {
|
|
3791
3838
|
absorbPlan();
|
|
3792
3839
|
const spend = DedupIndex.fold(io.snapshot(), { priceUsd: (servedBy, usage) => io.priceUsd(servedBy, usage) }).abandonedSpend();
|
|
@@ -4172,6 +4219,15 @@ async function runOscillationFreeze(options) {
|
|
|
4172
4219
|
prompt: "flip flop"
|
|
4173
4220
|
}
|
|
4174
4221
|
}], `oscillation ${String(phase)}`);
|
|
4222
|
+
if (phase === 7) return { toolCall: {
|
|
4223
|
+
name: "finish",
|
|
4224
|
+
args: { result: "frozen" }
|
|
4225
|
+
} };
|
|
4226
|
+
if (phase === 8) return revise([{
|
|
4227
|
+
op: "cancel_task",
|
|
4228
|
+
nodeId: liveNode ?? "MISSING",
|
|
4229
|
+
reason: "frozen: close out"
|
|
4230
|
+
}], "close the straggler");
|
|
4175
4231
|
return { toolCall: {
|
|
4176
4232
|
name: "finish",
|
|
4177
4233
|
args: { result: "frozen" }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/plan",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.236.0",
|
|
4
4
|
"description": "Rulvar adaptive orchestration extension: PlanRunner, RunLedger, escalation extensions, ModelLadder configuration. Replans during the run; not the plan-writing hybrid mode, which is @rulvar/planner.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rulvar/core": "1.
|
|
25
|
+
"@rulvar/core": "1.236.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.20.1",
|
|
29
29
|
"tsdown": "^0.22.14",
|
|
30
30
|
"typescript": "~6.0.3",
|
|
31
|
-
"@rulvar/store-sqlite": "1.
|
|
31
|
+
"@rulvar/store-sqlite": "1.236.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|