@rulvar/cli 1.2.0 → 1.3.1
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/cli.js +1 -1
- package/dist/dist-DIqBj4U-.js +6902 -0
- package/dist/{dist-Cdj5YBRG.js → dist-hv4J07hq.js} +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/{io-poGpn70r.js → io-nATg3Bhx.js} +251 -10
- package/dist/{src-BA7JP7vn.js → src-pbHnkjmy.js} +1 -1
- package/package.json +7 -7
|
@@ -86992,7 +86992,7 @@ var require_eslint_helpers = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
86992
86992
|
*/
|
|
86993
86993
|
async function globMatch({ basePath, pattern }) {
|
|
86994
86994
|
let found = false;
|
|
86995
|
-
const { hfs } = await import("./src-
|
|
86995
|
+
const { hfs } = await import("./src-pbHnkjmy.js");
|
|
86996
86996
|
const patternToUse = normalizeToPosix(path$9.relative(basePath, pattern));
|
|
86997
86997
|
const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
|
86998
86998
|
const walkSettings = {
|
|
@@ -87040,7 +87040,7 @@ var require_eslint_helpers = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
87040
87040
|
return new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
|
87041
87041
|
});
|
|
87042
87042
|
const unmatchedPatterns = /* @__PURE__ */ new Set([...relativeToPatterns.keys()]);
|
|
87043
|
-
const { hfs } = await import("./src-
|
|
87043
|
+
const { hfs } = await import("./src-pbHnkjmy.js");
|
|
87044
87044
|
const walk = hfs.walk(basePath, {
|
|
87045
87045
|
async directoryFilter(entry) {
|
|
87046
87046
|
if (!matchers.some((matcher) => matcher.match(entry.path, true))) return false;
|
|
@@ -94921,7 +94921,7 @@ const duplicateIdenticalCall = {
|
|
|
94921
94921
|
meta: {
|
|
94922
94922
|
type: "suggestion",
|
|
94923
94923
|
docs: { description: "advisory: byte-identical ctx.agent/ctx.workflow calls in one function forward-match to ONE journal entry; pass opts.key to distinguish deliberate repeats" },
|
|
94924
|
-
messages: { duplicateCall: "this call is byte-identical to an earlier one in the same function; identical calls share one journal entry via forward-matching, so a deliberate repeat needs a distinguishing opts.key
|
|
94924
|
+
messages: { duplicateCall: "this call is byte-identical to an earlier one in the same function; identical calls share one journal entry via forward-matching, so a deliberate repeat needs a distinguishing opts.key" },
|
|
94925
94925
|
schema: []
|
|
94926
94926
|
},
|
|
94927
94927
|
create(context) {
|
|
@@ -95229,7 +95229,7 @@ var WorkerSandboxRunner = class {
|
|
|
95229
95229
|
this.workerUrl = options?.workerUrl ?? new URL("./sandbox-worker.js", import.meta.url);
|
|
95230
95230
|
}
|
|
95231
95231
|
async execute(wf, ctx, args) {
|
|
95232
|
-
if (wf.kind !== "compiled-workflow") throw new ConfigError("WorkerSandboxRunner executes CompiledWorkflow values only; closures run in process
|
|
95232
|
+
if (wf.kind !== "compiled-workflow") throw new ConfigError("WorkerSandboxRunner executes CompiledWorkflow values only; closures run in process ");
|
|
95233
95233
|
const argsJson = toJournalValue(args ?? null, "sandbox workflow args");
|
|
95234
95234
|
const channel = new MessageChannel();
|
|
95235
95235
|
const worker = new Worker(this.workerUrl, { resourceLimits: { maxOldGenerationSizeMb: this.memoryMb } });
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ interface CliIo {
|
|
|
16
16
|
declare function processIo(): CliIo;
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/cli-main.d.ts
|
|
19
|
-
declare const HELP = "rulvar: durable multi-agent workflows (docs
|
|
19
|
+
declare const HELP = "rulvar: durable multi-agent workflows (https://docs.rulvar.com)\n\n rulvar run <file|name> [--args JSON] [--store PATH] [--budget-usd N]\n rulvar resume <runId> [--store PATH]\n rulvar runs ls [--store PATH]\n rulvar inspect <runId> [--store PATH]\n rulvar plan \"<goal>\" [--dry-run]\n rulvar kb <list | inbox | sweep>\n\nEngine assembly: adapters, defaults, and the workflow registry come from\nrulvar.config.mjs in the working directory (default export\n{ engineOptions?, workflows? }) or from the workflow module's named\nexports. --store selects the JsonlFileStore directory (default .rulvar).\nplan asks the planner model (role plan) to write a workflow script,\nlints and self-repairs it, then runs it in the worker sandbox; --dry-run\nprints the accepted script without running. Requires @rulvar/planner\ninstalled. kb list shows the per-project claim store\n(./rulvar.models.json) with full provenance. kb sweep runs the\nfalsification matrix from the kbSweep section of rulvar.config.mjs\n(fixed pool UNIONED with every model carrying an active negative claim\nplus the re-measure queue; optional canary probes flip drifted claims\nstale first; requires @rulvar/evals installed). kb inbox arrives with\nModelKnowledge phase 3.";
|
|
20
20
|
declare function runCli(argv: string[], options: {
|
|
21
21
|
cwd: string;
|
|
22
22
|
io: CliIo;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resumeCommand, c as driveRun, d as renderEventLine, f as DEFAULT_STORE_DIR, g as looksLikeFile, h as loadWorkflowModule, i as inspectCommand, l as reportOutcome, m as loadCliConfig, n as HELP, o as runCommand, p as assembleEngine, r as runCli, s as runsLsCommand, t as processIo, u as attachProgress } from "./io-
|
|
1
|
+
import { a as resumeCommand, c as driveRun, d as renderEventLine, f as DEFAULT_STORE_DIR, g as looksLikeFile, h as loadWorkflowModule, i as inspectCommand, l as reportOutcome, m as loadCliConfig, n as HELP, o as runCommand, p as assembleEngine, r as runCli, s as runsLsCommand, t as processIo, u as attachProgress } from "./io-nATg3Bhx.js";
|
|
2
2
|
import { ConfigError, InvalidResolutionError, JournalCompatibilityError, LeaseHeldError, Replayer, RulvarError, buildDeriverRegistry, costReportFromJournal, maskSecrets, normalizeEntry, scanJournalCompatibility, validateSchemaSpec } from "@rulvar/core";
|
|
3
3
|
//#region src/server.ts
|
|
4
4
|
/**
|
|
@@ -139,7 +139,7 @@ function createServer(options) {
|
|
|
139
139
|
const workflow = workflows[name];
|
|
140
140
|
if (workflow === void 0) return json(400, { error: {
|
|
141
141
|
code: "config",
|
|
142
|
-
message: `no workflow named '${name}' in the registry
|
|
142
|
+
message: `no workflow named '${name}' in the registry`
|
|
143
143
|
} });
|
|
144
144
|
const runOptions = {
|
|
145
145
|
...body.options?.runId === void 0 ? {} : { runId: body.options.runId },
|
|
@@ -477,8 +477,8 @@ function workerIdentity() {
|
|
|
477
477
|
}
|
|
478
478
|
function createWorker(engine, options) {
|
|
479
479
|
const store = options.store;
|
|
480
|
-
if (!(typeof store.acquire === "function" && typeof store.renew === "function" && typeof store.release === "function")) throw new ConfigError("createWorker requires a LeasableStore (acquire/renew/release with fencing epochs); the supplied store has no lease capability
|
|
481
|
-
if (engine.stores.journal !== store) throw new ConfigError("createWorker must lease the SAME journal store the engine writes (engine.stores.journal); leasing a different store would fence nothing
|
|
480
|
+
if (!(typeof store.acquire === "function" && typeof store.renew === "function" && typeof store.release === "function")) throw new ConfigError("createWorker requires a LeasableStore (acquire/renew/release with fencing epochs); the supplied store has no lease capability. Use @rulvar/store-sqlite or another conformant LeasableStore.");
|
|
481
|
+
if (engine.stores.journal !== store) throw new ConfigError("createWorker must lease the SAME journal store the engine writes (engine.stores.journal); leasing a different store would fence nothing");
|
|
482
482
|
const concurrency = options.concurrency ?? 1;
|
|
483
483
|
if (!Number.isInteger(concurrency) || concurrency < 1) throw new ConfigError(`createWorker concurrency must be a positive integer, got ${concurrency}`);
|
|
484
484
|
const owner = options.owner ?? workerIdentity();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfigError, FileModelKnowledgeStore, JsonlFileStore, claimExpired, compilePermissionPreset, costReportFromJournal, createEngine, parseModelRef, priceUsdOf, remeasureQueue, resolvePricing, runProfile } from "@rulvar/core";
|
|
1
|
+
import { ConfigError, FileModelKnowledgeStore, INBOX_PROPOSAL_TTL_DAYS, JsonlFileStore, claimExpired, claimExpiry, compilePermissionPreset, costReportFromJournal, createEngine, parseModelRef, priceUsdOf, proposalStatement, remeasureQueue, resolvePricing, runProfile } from "@rulvar/core";
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { existsSync, statSync } from "node:fs";
|
|
@@ -373,7 +373,7 @@ async function resumeCommand(argv, context) {
|
|
|
373
373
|
if (meta === void 0) throw new ConfigError(`run '${runId}' not found in the store`);
|
|
374
374
|
const name = meta.workflowName;
|
|
375
375
|
const workflow = name === void 0 ? void 0 : assembled.workflows[name];
|
|
376
|
-
if (workflow === void 0) throw new ConfigError(`run '${runId}' was started from workflow '${name ?? "(unknown)"}'; register it under that name in rulvar.config.mjs workflows to resume (
|
|
376
|
+
if (workflow === void 0) throw new ConfigError(`run '${runId}' was started from workflow '${name ?? "(unknown)"}'; register it under that name in rulvar.config.mjs workflows to resume (resume requires the in-process workflow value)`);
|
|
377
377
|
const first = assembled.engine.resume(runId, workflow, { args });
|
|
378
378
|
return reportOutcome(await driveRun({
|
|
379
379
|
engine: assembled.engine,
|
|
@@ -456,7 +456,7 @@ async function planCommand(argv, context) {
|
|
|
456
456
|
if (goal === void 0 || parsed.positionals.length > 1) throw new ConfigError("usage: rulvar plan \"<goal>\" [--dry-run]");
|
|
457
457
|
let plannerModule;
|
|
458
458
|
try {
|
|
459
|
-
plannerModule = await import("./dist-
|
|
459
|
+
plannerModule = await import("./dist-hv4J07hq.js");
|
|
460
460
|
} catch {
|
|
461
461
|
throw new ConfigError("rulvar plan requires @rulvar/planner (the plan agent, compileScript, and the worker sandbox live there); install it next to the CLI");
|
|
462
462
|
}
|
|
@@ -487,15 +487,14 @@ async function planCommand(argv, context) {
|
|
|
487
487
|
* consumption path. Claims with full provenance for the humans who
|
|
488
488
|
* author ladders, floors, and profiles; no run and no pin, so model
|
|
489
489
|
* names render VERBATIM here (only in-run cards are nameless). Reads
|
|
490
|
-
* the per-project file store (./rulvar.models.json).
|
|
491
|
-
* members inbox (phase 3) and sweep (phase 2) fail loudly until their
|
|
492
|
-
* phases ship.
|
|
490
|
+
* the per-project file store (./rulvar.models.json).
|
|
493
491
|
*/
|
|
494
492
|
async function kbCommand(argv, context) {
|
|
495
493
|
const [sub, ...rest] = argv;
|
|
496
|
-
if (sub === "inbox")
|
|
494
|
+
if (sub === "inbox") return await kbInboxCommand(rest, context);
|
|
495
|
+
if (sub === "gate") return await kbGateCommand(rest, context);
|
|
497
496
|
if (sub === "sweep") return await kbSweepCommand(rest, context);
|
|
498
|
-
if (sub !== "list" || rest.length > 0) throw new ConfigError("usage: rulvar kb <list | inbox | sweep> (no aliases in v1)");
|
|
497
|
+
if (sub !== "list" || rest.length > 0) throw new ConfigError("usage: rulvar kb <list | inbox | gate | sweep> (no aliases in v1)");
|
|
499
498
|
const snapshot = await new FileModelKnowledgeStore({ path: join(context.cwd, "rulvar.models.json") }).current();
|
|
500
499
|
context.io.out(`knowledge store: rulvar.models.json (version ${String(snapshot.version)}, ${String(snapshot.claims.length)} claim${snapshot.claims.length === 1 ? "" : "s"})`);
|
|
501
500
|
renderKbList(snapshot, context);
|
|
@@ -517,6 +516,248 @@ function renderKbList(snapshot, context) {
|
|
|
517
516
|
}
|
|
518
517
|
}
|
|
519
518
|
/**
|
|
519
|
+
* rulvar kb inbox (M12-T03): aggregates kb_propose-born proposals from
|
|
520
|
+
* FINISHED runs through the RunLedger fold behind the LedgerExport
|
|
521
|
+
* seam. Grouping of matching (subject, taskClass, polarity) triples is
|
|
522
|
+
* STRICTLY display: the command writes nothing, authorizes no spend,
|
|
523
|
+
* and schedules no sweeps; gating a proposal into a claim is the
|
|
524
|
+
* separate human gate flow. The age anchor is the run's terminal
|
|
525
|
+
* updatedAt (journal entries carry no wall clock by design): proposals
|
|
526
|
+
* of runs finished more than fourteen days ago expire out of the view.
|
|
527
|
+
* This is the human review surface, so the quarantined note text and
|
|
528
|
+
* concrete model names render here VERBATIM, exactly like kb list.
|
|
529
|
+
*/
|
|
530
|
+
async function kbInboxCommand(argv, context) {
|
|
531
|
+
const flags = parseCommonFlags(argv);
|
|
532
|
+
if (flags.positionals.length > 0) throw new ConfigError("usage: rulvar kb inbox [--store PATH]");
|
|
533
|
+
let plan;
|
|
534
|
+
try {
|
|
535
|
+
plan = await import("./dist-DIqBj4U-.js");
|
|
536
|
+
} catch {
|
|
537
|
+
throw new ConfigError("rulvar kb inbox requires @rulvar/plan (the RunLedger fold behind the LedgerExport seam)");
|
|
538
|
+
}
|
|
539
|
+
const assembled = assembleEngine({
|
|
540
|
+
config: await loadCliConfig(context.cwd),
|
|
541
|
+
...flags.store === void 0 ? {} : { storePath: flags.store },
|
|
542
|
+
cwd: context.cwd
|
|
543
|
+
});
|
|
544
|
+
const finished = (await assembled.store.listRuns()).filter((meta) => meta.status !== "running");
|
|
545
|
+
const cutoffMs = Date.now() - INBOX_PROPOSAL_TTL_DAYS * 24 * 60 * 60 * 1e3;
|
|
546
|
+
const groups = /* @__PURE__ */ new Map();
|
|
547
|
+
let expired = 0;
|
|
548
|
+
for (const meta of finished) {
|
|
549
|
+
const entries = await assembled.store.load(meta.runId);
|
|
550
|
+
const view = plan.foldLedger(entries, {
|
|
551
|
+
ledgerScope: "",
|
|
552
|
+
planScope: "plan"
|
|
553
|
+
});
|
|
554
|
+
for (const row of view.observations) {
|
|
555
|
+
if (row.subject === void 0 || row.polarity === void 0 || row.trigger === void 0) continue;
|
|
556
|
+
if (Date.parse(meta.updatedAt) < cutoffMs) {
|
|
557
|
+
expired += 1;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
const key = [
|
|
561
|
+
row.subject.model,
|
|
562
|
+
row.subject.effort ?? "",
|
|
563
|
+
row.taskClass,
|
|
564
|
+
row.polarity
|
|
565
|
+
].join("|");
|
|
566
|
+
const group = groups.get(key) ?? {
|
|
567
|
+
subject: row.subject,
|
|
568
|
+
taskClass: row.taskClass,
|
|
569
|
+
polarity: row.polarity,
|
|
570
|
+
members: []
|
|
571
|
+
};
|
|
572
|
+
group.members.push({
|
|
573
|
+
runId: meta.runId,
|
|
574
|
+
runLabel: meta.name ?? meta.workflowName ?? "",
|
|
575
|
+
entryRef: row.entryRef,
|
|
576
|
+
logicalTaskId: row.logicalTaskId,
|
|
577
|
+
...row.tierObserved === void 0 ? {} : { tierObserved: row.tierObserved },
|
|
578
|
+
trigger: row.trigger,
|
|
579
|
+
note: row.note,
|
|
580
|
+
evidenceRefs: row.evidenceRefs,
|
|
581
|
+
finishedAt: meta.updatedAt
|
|
582
|
+
});
|
|
583
|
+
groups.set(key, group);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const total = [...groups.values()].reduce((sum, group) => sum + group.members.length, 0);
|
|
587
|
+
context.io.out(`kb inbox: ${String(total)} live proposal${total === 1 ? "" : "s"} in ${String(groups.size)} group${groups.size === 1 ? "" : "s"} across ${String(finished.length)} finished run${finished.length === 1 ? "" : "s"}` + (expired > 0 ? `; ${String(expired)} expired (older than 14 days)` : ""));
|
|
588
|
+
for (const key of [...groups.keys()].sort()) {
|
|
589
|
+
const group = groups.get(key);
|
|
590
|
+
const effort = group.subject.effort === void 0 ? "" : ` effort=${group.subject.effort}`;
|
|
591
|
+
context.io.out(`${group.subject.model}${effort} :: ${group.taskClass} ${group.polarity} (${String(group.members.length)} proposal${group.members.length === 1 ? "" : "s"})`);
|
|
592
|
+
context.io.out(` statement: ${proposalStatement({
|
|
593
|
+
taskClass: group.taskClass,
|
|
594
|
+
polarity: group.polarity,
|
|
595
|
+
trigger: group.members[0].trigger
|
|
596
|
+
})}`);
|
|
597
|
+
for (const member of group.members) {
|
|
598
|
+
const tier = member.tierObserved === void 0 ? "" : ` tier=${String(member.tierObserved)}`;
|
|
599
|
+
const label = member.runLabel === "" ? "" : ` (${member.runLabel})`;
|
|
600
|
+
context.io.out(` - run=${member.runId}${label}#${String(member.entryRef)}${tier} trigger=${member.trigger} lineage=${member.logicalTaskId} finished=${member.finishedAt}`);
|
|
601
|
+
if (member.note !== "") context.io.out(` note: ${member.note}`);
|
|
602
|
+
if (member.evidenceRefs.length > 0) context.io.out(` evidence: ${member.evidenceRefs.map((ref) => `#${String(ref)}`).join(", ")}`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return 0;
|
|
606
|
+
}
|
|
607
|
+
const RULED_OUT_VOCABULARY = [
|
|
608
|
+
"prompt",
|
|
609
|
+
"tools",
|
|
610
|
+
"difficulty",
|
|
611
|
+
"transient-provider"
|
|
612
|
+
];
|
|
613
|
+
/**
|
|
614
|
+
* rulvar kb gate (M12-T04): the human gate turning ONE inbox proposal
|
|
615
|
+
* into a human-editorial claim. The attribution attestation is
|
|
616
|
+
* mandatory by construction: without --ruled-out the GateRecord does
|
|
617
|
+
* not assemble and nothing is written. The born claim carries the
|
|
618
|
+
* typed template statement (never the quarantined note), the origin
|
|
619
|
+
* provenance back to the proposing run, evidence resolving into that
|
|
620
|
+
* run's journal, and the editorial TTL; the commit is CAS against the
|
|
621
|
+
* per-project file store, whose git review is the authenticating gate.
|
|
622
|
+
*/
|
|
623
|
+
async function kbGateCommand(argv, context) {
|
|
624
|
+
const { values, positionals } = parseArgs({
|
|
625
|
+
args: argv,
|
|
626
|
+
allowPositionals: true,
|
|
627
|
+
options: {
|
|
628
|
+
store: { type: "string" },
|
|
629
|
+
approver: { type: "string" },
|
|
630
|
+
"ruled-out": { type: "string" },
|
|
631
|
+
"contrast-run": { type: "string" },
|
|
632
|
+
"contrast-eval": { type: "string" },
|
|
633
|
+
confidence: { type: "string" }
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
const usage = "usage: rulvar kb gate <runId> <entryRef> --approver NAME --ruled-out a,b,c [--contrast-run runId#seq | --contrast-eval reportId:caseId[,caseId...]] [--confidence high|medium|low] [--store PATH]";
|
|
637
|
+
const runId = positionals[0];
|
|
638
|
+
const entryRefRaw = positionals[1];
|
|
639
|
+
if (runId === void 0 || entryRefRaw === void 0 || positionals.length > 2) throw new ConfigError(usage);
|
|
640
|
+
const entryRef = Number(entryRefRaw);
|
|
641
|
+
if (!Number.isInteger(entryRef) || entryRef < 1) throw new ConfigError(`entryRef must be a positive integer entry seq, got '${entryRefRaw}'`);
|
|
642
|
+
const approver = values.approver;
|
|
643
|
+
if (approver === void 0 || approver === "") throw new ConfigError(`--approver is required: the attestation names its human. ${usage}`);
|
|
644
|
+
const ruledOutRaw = values["ruled-out"];
|
|
645
|
+
if (ruledOutRaw === void 0 || ruledOutRaw === "") throw new ConfigError(`--ruled-out is required: the attribution attestation lists the alternative causes you ruled out (${RULED_OUT_VOCABULARY.join(", ")}). ${usage}`);
|
|
646
|
+
const ruledOut = ruledOutRaw.split(",").map((entry) => entry.trim());
|
|
647
|
+
for (const entry of ruledOut) if (!RULED_OUT_VOCABULARY.includes(entry)) throw new ConfigError(`--ruled-out '${entry}' is not in the attestation vocabulary (${RULED_OUT_VOCABULARY.join(", ")})`);
|
|
648
|
+
if (values["contrast-run"] !== void 0 && values["contrast-eval"] !== void 0) throw new ConfigError("--contrast-run and --contrast-eval are mutually exclusive");
|
|
649
|
+
let contrastEvidence;
|
|
650
|
+
if (values["contrast-run"] !== void 0) {
|
|
651
|
+
const [contrastRun, seqRaw, ...tail] = values["contrast-run"].split("#");
|
|
652
|
+
const seq = Number(seqRaw);
|
|
653
|
+
if (contrastRun === void 0 || contrastRun === "" || tail.length > 0 || !Number.isInteger(seq) || seq < 1) throw new ConfigError("--contrast-run must look like 'runId#seq'");
|
|
654
|
+
contrastEvidence = {
|
|
655
|
+
kind: "journal",
|
|
656
|
+
runId: contrastRun,
|
|
657
|
+
entryRef: seq
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
if (values["contrast-eval"] !== void 0) {
|
|
661
|
+
const [reportId, caseList, ...tail] = values["contrast-eval"].split(":");
|
|
662
|
+
const caseIds = (caseList ?? "").split(",").filter((entry) => entry !== "");
|
|
663
|
+
if (reportId === void 0 || reportId === "" || tail.length > 0 || caseIds.length === 0) throw new ConfigError("--contrast-eval must look like 'reportId:caseId[,caseId...]'");
|
|
664
|
+
contrastEvidence = {
|
|
665
|
+
kind: "eval",
|
|
666
|
+
reportId,
|
|
667
|
+
caseIds
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
const confidence = values.confidence ?? "medium";
|
|
671
|
+
if (![
|
|
672
|
+
"high",
|
|
673
|
+
"medium",
|
|
674
|
+
"low"
|
|
675
|
+
].includes(confidence)) throw new ConfigError(`--confidence must be high, medium or low, got '${String(values.confidence)}'`);
|
|
676
|
+
let plan;
|
|
677
|
+
try {
|
|
678
|
+
plan = await import("./dist-DIqBj4U-.js");
|
|
679
|
+
} catch {
|
|
680
|
+
throw new ConfigError("rulvar kb gate requires @rulvar/plan (the RunLedger fold behind the LedgerExport seam)");
|
|
681
|
+
}
|
|
682
|
+
const assembled = assembleEngine({
|
|
683
|
+
config: await loadCliConfig(context.cwd),
|
|
684
|
+
...values.store === void 0 ? {} : { storePath: values.store },
|
|
685
|
+
cwd: context.cwd
|
|
686
|
+
});
|
|
687
|
+
const meta = (await assembled.store.listRuns()).find((candidate) => candidate.runId === runId);
|
|
688
|
+
if (meta === void 0) throw new ConfigError(`run '${runId}' not found in the store`);
|
|
689
|
+
if (meta.status === "running") throw new ConfigError(`run '${runId}' is still running; proposals gate from finished runs`);
|
|
690
|
+
if (Date.parse(meta.updatedAt) < Date.now() - INBOX_PROPOSAL_TTL_DAYS * 24 * 60 * 60 * 1e3) throw new ConfigError(`the proposal expired: run '${runId}' finished ${meta.updatedAt}, and inbox entries expire after ${String(INBOX_PROPOSAL_TTL_DAYS)} days`);
|
|
691
|
+
const entries = await assembled.store.load(runId);
|
|
692
|
+
const proposal = plan.foldLedger(entries, {
|
|
693
|
+
ledgerScope: "",
|
|
694
|
+
planScope: "plan"
|
|
695
|
+
}).observations.find((row) => row.entryRef === entryRef);
|
|
696
|
+
if (proposal === void 0 || proposal.subject === void 0 || proposal.polarity === void 0 || proposal.trigger === void 0) throw new ConfigError(`run '${runId}' entry ${String(entryRef)} is not a kb_propose proposal (see rulvar kb inbox for the gateable entries)`);
|
|
697
|
+
const store = new FileModelKnowledgeStore({ path: join(context.cwd, "rulvar.models.json") });
|
|
698
|
+
const snapshot = await store.current();
|
|
699
|
+
const already = snapshot.claims.find((claim) => claim.origin?.kind === "kb-proposal" && claim.origin.runId === runId && claim.origin.entryRef === entryRef && claim.status === "active");
|
|
700
|
+
if (already !== void 0) throw new ConfigError(`this proposal is already gated as claim '${already.id}' (supersede is the edit path)`);
|
|
701
|
+
const observedAt = meta.updatedAt;
|
|
702
|
+
const evidence = proposal.evidenceRefs.length > 0 ? proposal.evidenceRefs.map((ref) => ({
|
|
703
|
+
kind: "journal",
|
|
704
|
+
runId,
|
|
705
|
+
entryRef: ref
|
|
706
|
+
})) : [{
|
|
707
|
+
kind: "journal",
|
|
708
|
+
runId,
|
|
709
|
+
entryRef
|
|
710
|
+
}];
|
|
711
|
+
const claim = {
|
|
712
|
+
id: `kb-proposal-${runId}-${String(entryRef)}`,
|
|
713
|
+
subject: {
|
|
714
|
+
model: proposal.subject.model,
|
|
715
|
+
...proposal.subject.effort === void 0 ? {} : { effort: proposal.subject.effort }
|
|
716
|
+
},
|
|
717
|
+
taskClass: proposal.taskClass,
|
|
718
|
+
polarity: proposal.polarity,
|
|
719
|
+
statement: proposalStatement({
|
|
720
|
+
taskClass: proposal.taskClass,
|
|
721
|
+
polarity: proposal.polarity,
|
|
722
|
+
trigger: proposal.trigger
|
|
723
|
+
}),
|
|
724
|
+
class: "human-editorial",
|
|
725
|
+
status: "active",
|
|
726
|
+
evidence,
|
|
727
|
+
confidence,
|
|
728
|
+
observedAt,
|
|
729
|
+
expiresAt: claimExpiry("human-editorial", proposal.polarity, observedAt),
|
|
730
|
+
author: {
|
|
731
|
+
kind: "human",
|
|
732
|
+
id: approver
|
|
733
|
+
},
|
|
734
|
+
origin: {
|
|
735
|
+
kind: "kb-proposal",
|
|
736
|
+
runId,
|
|
737
|
+
entryRef
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
const gate = {
|
|
741
|
+
kind: "human",
|
|
742
|
+
approver,
|
|
743
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
744
|
+
attribution: {
|
|
745
|
+
ruledOut,
|
|
746
|
+
...contrastEvidence === void 0 ? {} : { contrastEvidence }
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
const version = await store.commit([{
|
|
750
|
+
op: "add",
|
|
751
|
+
claim,
|
|
752
|
+
gate
|
|
753
|
+
}], snapshot.version);
|
|
754
|
+
context.io.out(`gated: ${claim.id} (store version ${String(version)}); the git review of rulvar.models.json is the authenticating gate`);
|
|
755
|
+
context.io.out(` ${claim.subject.model}${claim.subject.effort === void 0 ? "" : ` effort=${claim.subject.effort}`} :: ${claim.taskClass} ${claim.polarity}`);
|
|
756
|
+
context.io.out(` ${claim.statement}`);
|
|
757
|
+
context.io.out(` origin: kb-proposal run=${runId}#${String(entryRef)} expires=${claim.expiresAt}`);
|
|
758
|
+
return 0;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
520
761
|
* rulvar kb sweep (M11-T05):
|
|
521
762
|
* falsification sweeps, run manually, from CI, or from a user cron,
|
|
522
763
|
* NEVER engine-scheduled. The matrix is the config's FIXED pool
|
|
@@ -530,7 +771,7 @@ async function kbSweepCommand(argv, context) {
|
|
|
530
771
|
if (argv.length > 0) throw new ConfigError("usage: rulvar kb sweep (configuration lives in rulvar.config.mjs)");
|
|
531
772
|
const config = await loadCliConfig(context.cwd);
|
|
532
773
|
const sweep = config.kbSweep;
|
|
533
|
-
if (sweep === void 0) throw new ConfigError("rulvar kb sweep requires a kbSweep section in rulvar.config.mjs ({ committerId, models, cases }
|
|
774
|
+
if (sweep === void 0) throw new ConfigError("rulvar kb sweep requires a kbSweep section in rulvar.config.mjs ({ committerId, models, cases })");
|
|
534
775
|
let evals;
|
|
535
776
|
try {
|
|
536
777
|
evals = await import("./dist-M-vPRtBM.js");
|
|
@@ -609,7 +850,7 @@ async function kbSweepCommand(argv, context) {
|
|
|
609
850
|
* Command dispatch for the canonical grammar (https://docs.rulvar.com/guide/cli):
|
|
610
851
|
* no aliases in v1; unknown commands and flags fail loudly with usage.
|
|
611
852
|
*/
|
|
612
|
-
const HELP = `rulvar: durable multi-agent workflows (docs
|
|
853
|
+
const HELP = `rulvar: durable multi-agent workflows (https://docs.rulvar.com)
|
|
613
854
|
|
|
614
855
|
rulvar run <file|name> [--args JSON] [--store PATH] [--budget-usd N]
|
|
615
856
|
rulvar resume <runId> [--store PATH]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "rulvar shell: run/resume/runs/inspect/plan/kb commands, TUI progress, createServer, createWorker, OTel exporter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rulvar/core": "1.
|
|
25
|
+
"@rulvar/core": "1.3.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.20.0",
|
|
29
29
|
"tsdown": "^0.22.3",
|
|
30
30
|
"typescript": "~6.0.3",
|
|
31
|
-
"@rulvar/testing": "1.
|
|
32
|
-
"@rulvar/
|
|
33
|
-
"@rulvar/
|
|
34
|
-
"@rulvar/
|
|
35
|
-
"@rulvar/
|
|
31
|
+
"@rulvar/testing": "1.3.1",
|
|
32
|
+
"@rulvar/plan": "1.3.1",
|
|
33
|
+
"@rulvar/store-sqlite": "1.3.1",
|
|
34
|
+
"@rulvar/planner": "1.3.1",
|
|
35
|
+
"@rulvar/evals": "1.3.1"
|
|
36
36
|
},
|
|
37
37
|
"bin": {
|
|
38
38
|
"rulvar": "./dist/cli.js"
|