@openclawbrain/cli 0.4.14 → 0.4.16
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/extension/index.js +30 -4
- package/dist/extension/runtime-guard.d.ts +6 -0
- package/dist/extension/runtime-guard.js +71 -15
- package/dist/src/cli.js +163 -23
- package/dist/src/index.d.ts +248 -4
- package/dist/src/index.js +1199 -44
- package/dist/src/install-converge.js +4 -3
- package/dist/src/learning-spine.js +43 -1
- package/dist/src/local-learner.d.ts +6 -0
- package/dist/src/local-learner.js +86 -152
- package/dist/src/local-session-passive-learning.js +28 -2
- package/dist/src/materialization-embedder.js +11 -0
- package/dist/src/openclaw-hook-truth.d.ts +6 -0
- package/dist/src/openclaw-hook-truth.js +27 -0
- package/dist/src/proof-command.js +251 -4
- package/dist/src/runtime-core.js +87 -7
- package/dist/src/status-learning-path.js +32 -2
- package/dist/src/teacher-decision-match.js +47 -10
- package/dist/src/teacher-labeler.d.ts +12 -0
- package/dist/src/teacher-labeler.js +42 -0
- package/dist/src/traced-learning-bridge.js +17 -1
- package/extension/index.ts +35 -4
- package/extension/runtime-guard.ts +92 -14
- package/package.json +1 -1
|
@@ -255,6 +255,134 @@ function extractStatusSignals(statusText) {
|
|
|
255
255
|
proofError: statusText.match(/proofError=([^\s]+)/)?.[1] ?? null,
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
|
+
function extractDetailedStatusLine(statusText, prefix) {
|
|
259
|
+
const normalizedPrefix = `${prefix} `;
|
|
260
|
+
return statusText.split(/\r?\n/).find((line) => line.startsWith(normalizedPrefix)) ?? null;
|
|
261
|
+
}
|
|
262
|
+
function extractKeyValuePairs(line) {
|
|
263
|
+
if (typeof line !== "string") {
|
|
264
|
+
return {};
|
|
265
|
+
}
|
|
266
|
+
const pairs = {};
|
|
267
|
+
for (const match of line.matchAll(/([A-Za-z][A-Za-z0-9]*)=([^\s]+)/g)) {
|
|
268
|
+
pairs[match[1]] = match[2];
|
|
269
|
+
}
|
|
270
|
+
return pairs;
|
|
271
|
+
}
|
|
272
|
+
function extractAttachedProfileCoverageEntries(line) {
|
|
273
|
+
if (typeof line !== "string") {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
const normalized = line.replace(/^attachedSet\s+/, "");
|
|
277
|
+
const proofPathIndex = normalized.indexOf(" proofPath=");
|
|
278
|
+
const entriesText = (proofPathIndex === -1 ? normalized : normalized.slice(0, proofPathIndex)).trim();
|
|
279
|
+
if (entriesText.length === 0 || entriesText === "none") {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
const entries = [];
|
|
283
|
+
let index = 0;
|
|
284
|
+
while (index < entriesText.length) {
|
|
285
|
+
while (index < entriesText.length && entriesText[index] === " ") {
|
|
286
|
+
index += 1;
|
|
287
|
+
}
|
|
288
|
+
if (index >= entriesText.length) {
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
const bracketStart = entriesText.indexOf("[", index);
|
|
292
|
+
if (bracketStart === -1) {
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
const bracketEnd = entriesText.indexOf("]", bracketStart + 1);
|
|
296
|
+
if (bracketEnd === -1) {
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
const rawLabel = entriesText.slice(index, bracketStart).trim();
|
|
300
|
+
const fields = extractKeyValuePairs(entriesText.slice(bracketStart + 1, bracketEnd));
|
|
301
|
+
entries.push({
|
|
302
|
+
label: rawLabel.replace(/^\*/, "").trim(),
|
|
303
|
+
current: rawLabel.startsWith("*"),
|
|
304
|
+
hookFiles: fields.hook ?? "unknown",
|
|
305
|
+
configLoad: fields.config ?? "unknown",
|
|
306
|
+
runtimeLoad: fields.runtime ?? "unknown",
|
|
307
|
+
loadedAt: fields.loadedAt ?? null,
|
|
308
|
+
coverageState: fields.hook === "present" && fields.config === "allows_load" && fields.runtime === "proven"
|
|
309
|
+
? "covered"
|
|
310
|
+
: "attention"
|
|
311
|
+
});
|
|
312
|
+
index = bracketEnd + 1;
|
|
313
|
+
}
|
|
314
|
+
return entries;
|
|
315
|
+
}
|
|
316
|
+
function buildCoverageSnapshot({ attachedSetLine, runtimeLoadProofSnapshot, openclawHome }) {
|
|
317
|
+
const parsedEntries = extractAttachedProfileCoverageEntries(attachedSetLine);
|
|
318
|
+
const proofProfiles = Array.isArray(runtimeLoadProofSnapshot?.value?.profiles)
|
|
319
|
+
? runtimeLoadProofSnapshot.value.profiles
|
|
320
|
+
: [];
|
|
321
|
+
const profiles = parsedEntries.length > 0
|
|
322
|
+
? parsedEntries
|
|
323
|
+
: proofProfiles.map((profile) => ({
|
|
324
|
+
label: `${profile?.profileId ?? "current_profile"}@${canonicalizeExistingProofPath(profile?.openclawHome ?? "")}`,
|
|
325
|
+
current: canonicalizeExistingProofPath(profile?.openclawHome ?? "") === canonicalizeExistingProofPath(openclawHome),
|
|
326
|
+
hookFiles: "unknown",
|
|
327
|
+
configLoad: "unknown",
|
|
328
|
+
runtimeLoad: "proven",
|
|
329
|
+
loadedAt: profile?.loadedAt ?? null,
|
|
330
|
+
coverageState: "covered"
|
|
331
|
+
}));
|
|
332
|
+
const runtimeProvenCount = profiles.filter((entry) => entry.runtimeLoad === "proven").length;
|
|
333
|
+
return {
|
|
334
|
+
contract: "openclaw_operator_profile_coverage_snapshot.v1",
|
|
335
|
+
generatedAt: new Date().toISOString(),
|
|
336
|
+
openclawHome: canonicalizeExistingProofPath(openclawHome),
|
|
337
|
+
attachedProfileCount: profiles.length,
|
|
338
|
+
runtimeProofProfileCount: proofProfiles.length,
|
|
339
|
+
hookReadyCount: profiles.filter((entry) => entry.hookFiles === "present").length,
|
|
340
|
+
configReadyCount: profiles.filter((entry) => entry.configLoad === "allows_load").length,
|
|
341
|
+
runtimeProvenCount,
|
|
342
|
+
coverageRate: profiles.length === 0 ? null : runtimeProvenCount / profiles.length,
|
|
343
|
+
profiles
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function buildHardeningSnapshot({ attachTruthLine, serveLine, routeFnLine, verdict, statusSignals }) {
|
|
347
|
+
const attachTruth = extractKeyValuePairs(attachTruthLine);
|
|
348
|
+
const serve = extractKeyValuePairs(serveLine);
|
|
349
|
+
const routeFn = extractKeyValuePairs(routeFnLine);
|
|
350
|
+
return {
|
|
351
|
+
contract: "openclaw_operator_hardening_snapshot.v1",
|
|
352
|
+
generatedAt: new Date().toISOString(),
|
|
353
|
+
statusSignals: {
|
|
354
|
+
statusOk: statusSignals.statusOk,
|
|
355
|
+
loadProofReady: statusSignals.loadProofReady,
|
|
356
|
+
runtimeProven: statusSignals.runtimeProven,
|
|
357
|
+
serveActivePack: statusSignals.serveActivePack,
|
|
358
|
+
routeFnAvailable: statusSignals.routeFnAvailable,
|
|
359
|
+
},
|
|
360
|
+
attachTruth: {
|
|
361
|
+
current: attachTruth.current ?? null,
|
|
362
|
+
hook: attachTruth.hook ?? null,
|
|
363
|
+
config: attachTruth.config ?? null,
|
|
364
|
+
runtime: attachTruth.runtime ?? null,
|
|
365
|
+
watcher: attachTruth.watcher ?? null,
|
|
366
|
+
},
|
|
367
|
+
serve: {
|
|
368
|
+
state: serve.state ?? null,
|
|
369
|
+
failOpen: serve.failOpen ?? null,
|
|
370
|
+
hardFail: serve.hardFail ?? null,
|
|
371
|
+
usedRouteFn: serve.usedRouteFn ?? null,
|
|
372
|
+
awaitingFirstExport: serve.awaitingFirstExport ?? null,
|
|
373
|
+
},
|
|
374
|
+
routeFn: {
|
|
375
|
+
available: routeFn.available ?? null,
|
|
376
|
+
freshness: routeFn.freshness ?? null,
|
|
377
|
+
},
|
|
378
|
+
verdict: {
|
|
379
|
+
verdict: verdict.verdict,
|
|
380
|
+
severity: verdict.severity,
|
|
381
|
+
missingProofCount: Array.isArray(verdict.missingProofs) ? verdict.missingProofs.length : 0,
|
|
382
|
+
warningCount: Array.isArray(verdict.warnings) ? verdict.warnings.length : 0,
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
258
386
|
|
|
259
387
|
function hasPackagedHookSource(pluginInspectText) {
|
|
260
388
|
return /Source:\s+.*(?:@openclawbrain[\\/]+openclaw|openclawbrain)[\\/]+dist[\\/]+extension[\\/]+index\.js/m.test(pluginInspectText);
|
|
@@ -270,8 +398,10 @@ function buildVerdict({ steps, gatewayStatus, pluginInspect, statusSignals, brea
|
|
|
270
398
|
const runtimeProofMatched = Array.isArray(runtimeLoadProofSnapshot?.value?.profiles)
|
|
271
399
|
&& runtimeLoadProofSnapshot.value.profiles.some((profile) => canonicalizeExistingProofPath(profile?.openclawHome ?? "") === canonicalizeExistingProofPath(openclawHome));
|
|
272
400
|
const runtimeTruthGaps = [];
|
|
273
|
-
|
|
274
|
-
|
|
401
|
+
const strongRuntimeTruth = statusSignals.loadProofReady
|
|
402
|
+
&& statusSignals.runtimeProven
|
|
403
|
+
&& statusSignals.serveActivePack
|
|
404
|
+
&& statusSignals.routeFnAvailable;
|
|
275
405
|
if (!statusSignals.loadProofReady)
|
|
276
406
|
runtimeTruthGaps.push("load_proof");
|
|
277
407
|
if (!statusSignals.runtimeProven)
|
|
@@ -282,6 +412,15 @@ function buildVerdict({ steps, gatewayStatus, pluginInspect, statusSignals, brea
|
|
|
282
412
|
runtimeTruthGaps.push("route_fn");
|
|
283
413
|
const warningCodes = [];
|
|
284
414
|
const warnings = [];
|
|
415
|
+
if (!statusSignals.statusOk) {
|
|
416
|
+
if (strongRuntimeTruth) {
|
|
417
|
+
warningCodes.push("status_warn");
|
|
418
|
+
warnings.push("detailed status did not return STATUS ok, but loadProof/runtime/serve/routeFn proofs stayed healthy");
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
runtimeTruthGaps.push("status_ok");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
285
424
|
if (!gatewayHealthy) {
|
|
286
425
|
warningCodes.push("gateway_health");
|
|
287
426
|
warnings.push("gateway status did not confirm runtime running and RPC probe ok");
|
|
@@ -357,7 +496,7 @@ function buildVerdict({ steps, gatewayStatus, pluginInspect, statusSignals, brea
|
|
|
357
496
|
};
|
|
358
497
|
}
|
|
359
498
|
|
|
360
|
-
function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspectText, statusSignals, breadcrumbs, runtimeLoadProofSnapshot }) {
|
|
499
|
+
function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspectText, statusSignals, breadcrumbs, runtimeLoadProofSnapshot, guardLine, attributionLine, learningPathLine, coverageSnapshot, hardeningSnapshot }) {
|
|
361
500
|
const passed = [];
|
|
362
501
|
const missing = [];
|
|
363
502
|
const warnings = Array.isArray(verdict.warnings) ? verdict.warnings : [];
|
|
@@ -413,6 +552,33 @@ function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspec
|
|
|
413
552
|
"## Warnings",
|
|
414
553
|
...(warnings.length === 0 ? ["- none"] : warnings.map((item) => `- ${item}`)),
|
|
415
554
|
"",
|
|
555
|
+
"## Runtime Guard",
|
|
556
|
+
...(guardLine === null
|
|
557
|
+
? ["- runtime guard line not reported by detailed status"]
|
|
558
|
+
: [`- ${guardLine}`]),
|
|
559
|
+
"",
|
|
560
|
+
"## Learning Attribution",
|
|
561
|
+
...(attributionLine === null
|
|
562
|
+
? ["- attribution line not reported by detailed status"]
|
|
563
|
+
: [`- ${attributionLine}`]),
|
|
564
|
+
...(learningPathLine === null
|
|
565
|
+
? []
|
|
566
|
+
: [`- ${learningPathLine}`]),
|
|
567
|
+
"",
|
|
568
|
+
"## Coverage snapshot",
|
|
569
|
+
`- attached profiles: ${coverageSnapshot.attachedProfileCount}`,
|
|
570
|
+
`- runtime-proven profiles: ${coverageSnapshot.runtimeProvenCount}/${coverageSnapshot.attachedProfileCount}`,
|
|
571
|
+
`- coverage rate: ${coverageSnapshot.coverageRate === null ? "none" : coverageSnapshot.coverageRate.toFixed(3)}`,
|
|
572
|
+
...(coverageSnapshot.profiles.length === 0
|
|
573
|
+
? ["- per-profile: none"]
|
|
574
|
+
: coverageSnapshot.profiles.map((entry) => `- ${entry.current ? "*" : ""}${entry.label} coverage=${entry.coverageState} hook=${entry.hookFiles} config=${entry.configLoad} runtime=${entry.runtimeLoad} loadedAt=${entry.loadedAt ?? "none"}`)),
|
|
575
|
+
"",
|
|
576
|
+
"## Hardening snapshot",
|
|
577
|
+
`- status signals: statusOk=${hardeningSnapshot.statusSignals.statusOk} loadProofReady=${hardeningSnapshot.statusSignals.loadProofReady} runtimeProven=${hardeningSnapshot.statusSignals.runtimeProven} serveActivePack=${hardeningSnapshot.statusSignals.serveActivePack} routeFnAvailable=${hardeningSnapshot.statusSignals.routeFnAvailable}`,
|
|
578
|
+
`- serve: state=${hardeningSnapshot.serve.state ?? "none"} failOpen=${hardeningSnapshot.serve.failOpen ?? "none"} hardFail=${hardeningSnapshot.serve.hardFail ?? "none"} usedRouteFn=${hardeningSnapshot.serve.usedRouteFn ?? "none"}`,
|
|
579
|
+
`- attachTruth: current=${hardeningSnapshot.attachTruth.current ?? "none"} hook=${hardeningSnapshot.attachTruth.hook ?? "none"} config=${hardeningSnapshot.attachTruth.config ?? "none"} runtime=${hardeningSnapshot.attachTruth.runtime ?? "none"}`,
|
|
580
|
+
`- proof verdict: ${hardeningSnapshot.verdict.verdict} severity=${hardeningSnapshot.verdict.severity} warnings=${hardeningSnapshot.verdict.warningCount}`,
|
|
581
|
+
"",
|
|
416
582
|
"## Step ledger",
|
|
417
583
|
...steps.map((step) => `- ${step.stepId}: ${step.skipped ? "skipped" : `${step.resultClass} (${step.captureState})`} - ${step.summary}`),
|
|
418
584
|
];
|
|
@@ -442,6 +608,17 @@ function buildGatewayArgs(action, profileName) {
|
|
|
442
608
|
: ["gateway", action, "--profile", profileName];
|
|
443
609
|
}
|
|
444
610
|
|
|
611
|
+
function buildGatewayStatusArgs(profileName, gatewayUrl, gatewayToken) {
|
|
612
|
+
const args = buildGatewayArgs("status", profileName);
|
|
613
|
+
if (gatewayUrl !== null) {
|
|
614
|
+
args.push("--url", gatewayUrl);
|
|
615
|
+
}
|
|
616
|
+
if (gatewayToken !== null) {
|
|
617
|
+
args.push("--token", gatewayToken);
|
|
618
|
+
}
|
|
619
|
+
return args;
|
|
620
|
+
}
|
|
621
|
+
|
|
445
622
|
export function buildProofCommandForOpenClawHome(openclawHome) {
|
|
446
623
|
return `openclawbrain proof --openclaw-home ${quoteShellArg(path.resolve(openclawHome))}`;
|
|
447
624
|
}
|
|
@@ -454,6 +631,8 @@ export function buildProofCommandHelpSection() {
|
|
|
454
631
|
" --skip-install Capture proof without rerunning install first (proof only).",
|
|
455
632
|
" --skip-restart Capture proof without restarting OpenClaw first (proof only).",
|
|
456
633
|
` --plugin-id <id> Plugin id for \`openclaw plugins inspect\` (proof only; default: ${DEFAULT_OPERATOR_PROOF_PLUGIN_ID}).`,
|
|
634
|
+
" --gateway-url <url> Override the gateway-status probe target for proof capture (proof only).",
|
|
635
|
+
" --gateway-token <token> Gateway token to use with --gateway-url or other non-default proof probes.",
|
|
457
636
|
` --timeout-ms <ms> Per-step timeout in ms for proof capture (proof only; default: ${DEFAULT_OPERATOR_PROOF_TIMEOUT_MS}).`,
|
|
458
637
|
],
|
|
459
638
|
lifecycle: " 5. proof openclawbrain proof --openclaw-home <path> - capture one durable operator proof bundle after install/restart/status",
|
|
@@ -469,6 +648,8 @@ export function parseProofCliArgs(argv, options = {}) {
|
|
|
469
648
|
let skipInstall = false;
|
|
470
649
|
let skipRestart = false;
|
|
471
650
|
let pluginId = DEFAULT_OPERATOR_PROOF_PLUGIN_ID;
|
|
651
|
+
let gatewayUrl = null;
|
|
652
|
+
let gatewayToken = null;
|
|
472
653
|
let timeoutMs = DEFAULT_OPERATOR_PROOF_TIMEOUT_MS;
|
|
473
654
|
let json = false;
|
|
474
655
|
let help = false;
|
|
@@ -526,6 +707,24 @@ export function parseProofCliArgs(argv, options = {}) {
|
|
|
526
707
|
index += 1;
|
|
527
708
|
continue;
|
|
528
709
|
}
|
|
710
|
+
if (arg === "--gateway-url") {
|
|
711
|
+
const next = argv[index + 1];
|
|
712
|
+
if (next === undefined) {
|
|
713
|
+
throw new Error("--gateway-url requires a value");
|
|
714
|
+
}
|
|
715
|
+
gatewayUrl = next;
|
|
716
|
+
index += 1;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
if (arg === "--gateway-token") {
|
|
720
|
+
const next = argv[index + 1];
|
|
721
|
+
if (next === undefined) {
|
|
722
|
+
throw new Error("--gateway-token requires a value");
|
|
723
|
+
}
|
|
724
|
+
gatewayToken = next;
|
|
725
|
+
index += 1;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
529
728
|
if (arg === "--timeout-ms") {
|
|
530
729
|
const next = argv[index + 1];
|
|
531
730
|
if (next === undefined) {
|
|
@@ -550,6 +749,8 @@ export function parseProofCliArgs(argv, options = {}) {
|
|
|
550
749
|
skipInstall,
|
|
551
750
|
skipRestart,
|
|
552
751
|
pluginId,
|
|
752
|
+
gatewayUrl,
|
|
753
|
+
gatewayToken,
|
|
553
754
|
timeoutMs,
|
|
554
755
|
json,
|
|
555
756
|
help
|
|
@@ -570,6 +771,8 @@ export function parseProofCliArgs(argv, options = {}) {
|
|
|
570
771
|
skipInstall,
|
|
571
772
|
skipRestart,
|
|
572
773
|
pluginId,
|
|
774
|
+
gatewayUrl,
|
|
775
|
+
gatewayToken,
|
|
573
776
|
timeoutMs,
|
|
574
777
|
json,
|
|
575
778
|
help
|
|
@@ -628,21 +831,38 @@ export function captureOperatorProofBundle(options) {
|
|
|
628
831
|
}
|
|
629
832
|
addStep("01-install", "install", cliInvocation.command, [...cliInvocation.args, "install", "--openclaw-home", options.openclawHome], { skipped: options.skipInstall === true });
|
|
630
833
|
addStep("02-restart", "gateway restart", "openclaw", buildGatewayArgs("restart", gatewayProfile), { skipped: options.skipRestart === true });
|
|
631
|
-
const gatewayStatusCapture = addStep("03-gateway-status", "gateway status", "openclaw",
|
|
834
|
+
const gatewayStatusCapture = addStep("03-gateway-status", "gateway status", "openclaw", buildGatewayStatusArgs(
|
|
835
|
+
gatewayProfile,
|
|
836
|
+
normalizeOptionalCliString(options.gatewayUrl ?? null),
|
|
837
|
+
normalizeOptionalCliString(options.gatewayToken ?? null),
|
|
838
|
+
));
|
|
632
839
|
const pluginInspectCapture = addStep("04-plugin-inspect", "plugin inspect", "openclaw", ["plugins", "inspect", options.pluginId]);
|
|
633
840
|
const statusCapture = addStep("05-detailed-status", "detailed status", cliInvocation.command, [...cliInvocation.args, "status", "--openclaw-home", options.openclawHome, "--detailed"]);
|
|
634
841
|
const gatewayLogPath = extractGatewayLogPath(gatewayStatusCapture.stdout);
|
|
635
842
|
const activationRoot = extractActivationRoot(statusCapture.stdout, options.activationRoot ?? null);
|
|
636
843
|
const statusSignals = extractStatusSignals(statusCapture.stdout);
|
|
844
|
+
const attachTruthLine = extractDetailedStatusLine(statusCapture.stdout, "attachTruth");
|
|
845
|
+
const attachedSetLine = extractDetailedStatusLine(statusCapture.stdout, "attachedSet");
|
|
846
|
+
const serveLine = extractDetailedStatusLine(statusCapture.stdout, "serve");
|
|
847
|
+
const routeFnLine = extractDetailedStatusLine(statusCapture.stdout, "routeFn");
|
|
848
|
+
const guardLine = extractDetailedStatusLine(statusCapture.stdout, "guard");
|
|
849
|
+
const attributionLine = extractDetailedStatusLine(statusCapture.stdout, "attribution");
|
|
850
|
+
const learningPathLine = extractDetailedStatusLine(statusCapture.stdout, "path");
|
|
637
851
|
const runtimeLoadProofPath = normalizeReportedProofPath(statusSignals.proofPath)
|
|
638
852
|
?? path.join(activationRoot, "attachment-truth", "runtime-load-proofs.json");
|
|
639
853
|
const runtimeLoadProofSnapshot = readJsonSnapshot(runtimeLoadProofPath);
|
|
640
854
|
const gatewayLogText = readTextIfExists(gatewayLogPath);
|
|
641
855
|
const breadcrumbs = extractStartupBreadcrumbs(gatewayLogText, bundleStartedAt);
|
|
856
|
+
const coverageSnapshot = buildCoverageSnapshot({
|
|
857
|
+
attachedSetLine,
|
|
858
|
+
runtimeLoadProofSnapshot,
|
|
859
|
+
openclawHome: options.openclawHome,
|
|
860
|
+
});
|
|
642
861
|
writeText(path.join(bundleDir, "extracted-startup-breadcrumbs.log"), breadcrumbs.all.length === 0
|
|
643
862
|
? "<no matching breadcrumbs found>\n"
|
|
644
863
|
: `${breadcrumbs.all.map((entry) => entry.line).join("\n")}\n`);
|
|
645
864
|
writeJson(path.join(bundleDir, "runtime-load-proof.json"), runtimeLoadProofSnapshot);
|
|
865
|
+
writeJson(path.join(bundleDir, "coverage-snapshot.json"), coverageSnapshot);
|
|
646
866
|
const verdict = buildVerdict({
|
|
647
867
|
steps,
|
|
648
868
|
gatewayStatus: gatewayStatusCapture.stdout,
|
|
@@ -652,6 +872,13 @@ export function captureOperatorProofBundle(options) {
|
|
|
652
872
|
runtimeLoadProofSnapshot,
|
|
653
873
|
openclawHome: options.openclawHome,
|
|
654
874
|
});
|
|
875
|
+
const hardeningSnapshot = buildHardeningSnapshot({
|
|
876
|
+
attachTruthLine,
|
|
877
|
+
serveLine,
|
|
878
|
+
routeFnLine,
|
|
879
|
+
verdict,
|
|
880
|
+
statusSignals,
|
|
881
|
+
});
|
|
655
882
|
writeJson(path.join(bundleDir, "steps.json"), {
|
|
656
883
|
bundleStartedAt,
|
|
657
884
|
openclawHome: canonicalizeExistingProofPath(options.openclawHome),
|
|
@@ -664,6 +891,8 @@ export function captureOperatorProofBundle(options) {
|
|
|
664
891
|
bundleStartedAt,
|
|
665
892
|
verdict,
|
|
666
893
|
statusSignals,
|
|
894
|
+
coverageSnapshot,
|
|
895
|
+
hardeningSnapshot,
|
|
667
896
|
breadcrumbs: {
|
|
668
897
|
allCount: breadcrumbs.all.length,
|
|
669
898
|
postBundleCount: breadcrumbs.afterBundleStart.length,
|
|
@@ -671,7 +900,11 @@ export function captureOperatorProofBundle(options) {
|
|
|
671
900
|
},
|
|
672
901
|
runtimeLoadProofPath,
|
|
673
902
|
runtimeLoadProofError: runtimeLoadProofSnapshot.error,
|
|
903
|
+
guardLine,
|
|
904
|
+
attributionLine,
|
|
905
|
+
learningPathLine,
|
|
674
906
|
});
|
|
907
|
+
writeJson(path.join(bundleDir, "hardening-snapshot.json"), hardeningSnapshot);
|
|
675
908
|
writeText(path.join(bundleDir, "summary.md"), buildSummary({
|
|
676
909
|
options,
|
|
677
910
|
steps,
|
|
@@ -681,6 +914,11 @@ export function captureOperatorProofBundle(options) {
|
|
|
681
914
|
statusSignals,
|
|
682
915
|
breadcrumbs,
|
|
683
916
|
runtimeLoadProofSnapshot,
|
|
917
|
+
guardLine,
|
|
918
|
+
attributionLine,
|
|
919
|
+
learningPathLine,
|
|
920
|
+
coverageSnapshot,
|
|
921
|
+
hardeningSnapshot,
|
|
684
922
|
}));
|
|
685
923
|
return {
|
|
686
924
|
ok: true,
|
|
@@ -691,14 +929,21 @@ export function captureOperatorProofBundle(options) {
|
|
|
691
929
|
gatewayLogPath,
|
|
692
930
|
runtimeLoadProofPath,
|
|
693
931
|
runtimeLoadProofSnapshot,
|
|
932
|
+
coverageSnapshot,
|
|
933
|
+
hardeningSnapshot,
|
|
694
934
|
verdict,
|
|
695
935
|
statusSignals,
|
|
936
|
+
guardLine,
|
|
937
|
+
attributionLine,
|
|
938
|
+
learningPathLine,
|
|
696
939
|
steps,
|
|
697
940
|
summaryPath: path.join(bundleDir, "summary.md"),
|
|
698
941
|
stepsPath: path.join(bundleDir, "steps.json"),
|
|
699
942
|
verdictPath: path.join(bundleDir, "verdict.json"),
|
|
700
943
|
breadcrumbPath: path.join(bundleDir, "extracted-startup-breadcrumbs.log"),
|
|
701
944
|
runtimeLoadProofSnapshotPath: path.join(bundleDir, "runtime-load-proof.json"),
|
|
945
|
+
coverageSnapshotPath: path.join(bundleDir, "coverage-snapshot.json"),
|
|
946
|
+
hardeningSnapshotPath: path.join(bundleDir, "hardening-snapshot.json"),
|
|
702
947
|
};
|
|
703
948
|
}
|
|
704
949
|
|
|
@@ -713,6 +958,8 @@ export function formatOperatorProofResult(result) {
|
|
|
713
958
|
` Verdict: ${result.verdictPath}`,
|
|
714
959
|
` Breadcrumbs: ${result.breadcrumbPath}`,
|
|
715
960
|
` Runtime proof: ${result.runtimeLoadProofSnapshotPath}`,
|
|
961
|
+
` Coverage snapshot: ${result.coverageSnapshotPath}`,
|
|
962
|
+
` Hardening snapshot: ${result.hardeningSnapshotPath}`,
|
|
716
963
|
];
|
|
717
964
|
return lines.join("\n");
|
|
718
965
|
}
|
package/dist/src/runtime-core.js
CHANGED
|
@@ -39,8 +39,75 @@ function normalizeNonNegativeInteger(value, fieldName, fallbackValue) {
|
|
|
39
39
|
return value;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG = {
|
|
43
|
+
vector_only: {
|
|
44
|
+
routeMode: "heuristic",
|
|
45
|
+
selectionMode: "flat_rank_v1",
|
|
46
|
+
},
|
|
47
|
+
graph_prior_only: {
|
|
48
|
+
routeMode: "heuristic",
|
|
49
|
+
selectionMode: "graph_walk_v1",
|
|
50
|
+
},
|
|
51
|
+
learned_route: {
|
|
52
|
+
routeMode: "learned",
|
|
53
|
+
selectionMode: "graph_walk_v1",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function resolveRuntimeComparativeReplayMode(value) {
|
|
58
|
+
return Object.prototype.hasOwnProperty.call(RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG, value)
|
|
59
|
+
? value
|
|
60
|
+
: null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function resolveCompileModePlan(modeValue, selectionModeValue) {
|
|
64
|
+
const requestedSelectionMode = normalizeCompileSelectionMode(selectionModeValue);
|
|
65
|
+
const comparativeMode = resolveRuntimeComparativeReplayMode(modeValue);
|
|
66
|
+
|
|
67
|
+
if (comparativeMode === null) {
|
|
68
|
+
if (modeValue === undefined) {
|
|
69
|
+
return {
|
|
70
|
+
comparativeMode: null,
|
|
71
|
+
routeMode: "heuristic",
|
|
72
|
+
selectionMode: requestedSelectionMode,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (modeValue === "heuristic" || modeValue === "learned") {
|
|
77
|
+
return {
|
|
78
|
+
comparativeMode: null,
|
|
79
|
+
routeMode: modeValue,
|
|
80
|
+
selectionMode: requestedSelectionMode,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new Error(
|
|
85
|
+
"mode must be heuristic, learned, vector_only, graph_prior_only, or learned_route",
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const plan = RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG[comparativeMode];
|
|
90
|
+
|
|
91
|
+
if (requestedSelectionMode !== undefined && requestedSelectionMode !== plan.selectionMode) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`selectionMode ${requestedSelectionMode} conflicts with comparative mode ${comparativeMode}, expected ${plan.selectionMode}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
comparativeMode,
|
|
99
|
+
routeMode: plan.routeMode,
|
|
100
|
+
selectionMode: plan.selectionMode,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function resolveSyntheticTurnMode(value) {
|
|
105
|
+
const comparativeMode = resolveRuntimeComparativeReplayMode(value);
|
|
106
|
+
if (comparativeMode !== null) {
|
|
107
|
+
return RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG[comparativeMode].routeMode;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return value === "heuristic" || value === "learned" ? value : undefined;
|
|
44
111
|
}
|
|
45
112
|
|
|
46
113
|
function normalizeCompileSelectionMode(value) {
|
|
@@ -83,7 +150,7 @@ function formatPromptContext(compileResponse) {
|
|
|
83
150
|
}
|
|
84
151
|
|
|
85
152
|
for (const block of compileResponse.selectedContext) {
|
|
86
|
-
lines.push(`
|
|
153
|
+
lines.push(`PROVENANCE_REF: ctx_${block.id}`);
|
|
87
154
|
lines.push(`BLOCK_ID: ${block.id}`);
|
|
88
155
|
lines.push(block.text.trim());
|
|
89
156
|
lines.push("");
|
|
@@ -273,8 +340,10 @@ function appendCompileServeRouteDecisionLog(input) {
|
|
|
273
340
|
syntheticTurn.budgetStrategy = input.compileInput.budgetStrategy;
|
|
274
341
|
}
|
|
275
342
|
|
|
276
|
-
|
|
277
|
-
|
|
343
|
+
const syntheticTurnMode = resolveSyntheticTurnMode(input.compileInput.mode);
|
|
344
|
+
|
|
345
|
+
if (syntheticTurnMode !== undefined) {
|
|
346
|
+
syntheticTurn.mode = syntheticTurnMode;
|
|
278
347
|
}
|
|
279
348
|
|
|
280
349
|
if (input.compileInput.runtimeHints !== undefined) {
|
|
@@ -456,6 +525,7 @@ export function compileRuntimeContext(input) {
|
|
|
456
525
|
let activationRoot = fallbackActivationRoot;
|
|
457
526
|
let agentId = process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
|
|
458
527
|
let runtimeHints = [];
|
|
528
|
+
let comparativeMode = null;
|
|
459
529
|
let selectionMode;
|
|
460
530
|
let userMessage = "";
|
|
461
531
|
let maxContextChars;
|
|
@@ -470,13 +540,15 @@ export function compileRuntimeContext(input) {
|
|
|
470
540
|
activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
|
|
471
541
|
agentId = normalizeOptionalString(input.agentId) ?? process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
|
|
472
542
|
runtimeHints = normalizeRuntimeHints(input.runtimeHints);
|
|
473
|
-
selectionMode = normalizeCompileSelectionMode(input.selectionMode);
|
|
474
543
|
userMessage = normalizeNonEmptyString(input.message, "message");
|
|
475
544
|
maxContextChars =
|
|
476
545
|
input.maxContextChars !== undefined
|
|
477
546
|
? normalizeNonNegativeInteger(input.maxContextChars, "maxContextChars", input.maxContextChars)
|
|
478
547
|
: undefined;
|
|
479
|
-
|
|
548
|
+
const compileModePlan = resolveCompileModePlan(input.mode, input.selectionMode);
|
|
549
|
+
comparativeMode = compileModePlan.comparativeMode;
|
|
550
|
+
mode = compileModePlan.routeMode;
|
|
551
|
+
selectionMode = compileModePlan.selectionMode;
|
|
480
552
|
} catch (error) {
|
|
481
553
|
result = failOpenCompileResult(
|
|
482
554
|
error,
|
|
@@ -518,6 +590,7 @@ export function compileRuntimeContext(input) {
|
|
|
518
590
|
},
|
|
519
591
|
);
|
|
520
592
|
routeSelectionMs = elapsedMsFrom(routeSelectionStartedAtNs);
|
|
593
|
+
const selectionEngine = selectionMode ?? "flat_rank_v1";
|
|
521
594
|
const compileResponse = {
|
|
522
595
|
...compile.response,
|
|
523
596
|
diagnostics: {
|
|
@@ -525,6 +598,13 @@ export function compileRuntimeContext(input) {
|
|
|
525
598
|
notes: uniqueNotes([
|
|
526
599
|
...compile.response.diagnostics.notes,
|
|
527
600
|
...resolvedBudget.notes,
|
|
601
|
+
`selection_engine=${selectionEngine}`,
|
|
602
|
+
...(comparativeMode === null
|
|
603
|
+
? []
|
|
604
|
+
: [
|
|
605
|
+
`comparative_mode=${comparativeMode}`,
|
|
606
|
+
`comparative_mode_plan=${mode}+${selectionEngine}`,
|
|
607
|
+
]),
|
|
528
608
|
"OpenClaw remains the runtime owner",
|
|
529
609
|
]),
|
|
530
610
|
},
|
|
@@ -7,14 +7,43 @@ function isSeedAwaitingFirstPromotion(status) {
|
|
|
7
7
|
function normalizeOptionalString(value) {
|
|
8
8
|
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
9
9
|
}
|
|
10
|
+
function formatOperatorLearningAttributionSummary({ status }) {
|
|
11
|
+
const attribution = status?.learningAttribution ?? null;
|
|
12
|
+
if (!attribution) {
|
|
13
|
+
return "quality=unavailable source=unavailable detail=no_learning_attribution_surface";
|
|
14
|
+
}
|
|
15
|
+
const source = [normalizeOptionalString(attribution.source), normalizeOptionalString(attribution.snapshotKind)]
|
|
16
|
+
.filter((value) => value !== null)
|
|
17
|
+
.join("/");
|
|
18
|
+
if (attribution.available !== true) {
|
|
19
|
+
return `quality=${normalizeOptionalString(attribution.quality) ?? "unavailable"} source=${source || "unavailable"} detail=${normalizeOptionalString(attribution.detail) ?? "unavailable"}`;
|
|
20
|
+
}
|
|
21
|
+
const matchedByMode = attribution.matchedByMode ?? {};
|
|
22
|
+
return [
|
|
23
|
+
`quality=${normalizeOptionalString(attribution.quality) ?? "unavailable"}`,
|
|
24
|
+
`source=${source || "latest_materialization"}`,
|
|
25
|
+
`nonZero=${attribution.nonZeroObservationCount ?? 0}`,
|
|
26
|
+
`exact=${attribution.exactMatchCount ?? 0}`,
|
|
27
|
+
`heuristic=${attribution.heuristicMatchCount ?? 0}`,
|
|
28
|
+
`unmatched=${attribution.unmatchedCount ?? 0}`,
|
|
29
|
+
`ambiguous=${attribution.ambiguousCount ?? 0}`,
|
|
30
|
+
`modes=decision:${matchedByMode.exactDecisionId ?? 0}|digest:${matchedByMode.exactSelectionDigest ?? 0}|compile:${matchedByMode.turnCompileEventId ?? 0}|heuristic:${matchedByMode.legacyHeuristic ?? 0}`
|
|
31
|
+
].join(" ");
|
|
32
|
+
}
|
|
10
33
|
export function formatOperatorLearningPathSummary({ status, learningPath, tracedLearning }) {
|
|
34
|
+
const attribution = status?.learningAttribution ?? null;
|
|
11
35
|
if (!isSeedAwaitingFirstPromotion(status)) {
|
|
12
|
-
|
|
36
|
+
const rawSummary = formatRawLearningPathSummary(learningPath);
|
|
37
|
+
const bindingQuality = normalizeOptionalString(attribution?.quality);
|
|
38
|
+
return bindingQuality === null || bindingQuality === "unavailable"
|
|
39
|
+
? rawSummary
|
|
40
|
+
: `${rawSummary} bindingQuality=${bindingQuality}`;
|
|
13
41
|
}
|
|
14
42
|
const detailParts = [
|
|
15
43
|
"detail=seed_state_awaiting_first_promotion",
|
|
16
44
|
`tracedPg=${normalizeOptionalString(tracedLearning?.pgVersionUsed) ?? "none"}`,
|
|
17
|
-
`tracedPack=${normalizeOptionalString(tracedLearning?.materializedPackId) ?? "none"}
|
|
45
|
+
`tracedPack=${normalizeOptionalString(tracedLearning?.materializedPackId) ?? "none"}`,
|
|
46
|
+
`bindingQuality=${normalizeOptionalString(attribution?.quality) ?? "unavailable"}`
|
|
18
47
|
];
|
|
19
48
|
return [
|
|
20
49
|
"source=seed_state",
|
|
@@ -26,3 +55,4 @@ export function formatOperatorLearningPathSummary({ status, learningPath, traced
|
|
|
26
55
|
...detailParts
|
|
27
56
|
].join(" ");
|
|
28
57
|
}
|
|
58
|
+
export { formatOperatorLearningAttributionSummary };
|