@openclawbrain/cli 0.4.31 → 0.4.33

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/src/cli.js CHANGED
@@ -1738,6 +1738,19 @@ function readInstallRuntimeFingerprint(openclawHome) {
1738
1738
  function runOpenClawBrainConvergePluginStep(openclawHome) {
1739
1739
  const before = readInstallRuntimeFingerprint(openclawHome);
1740
1740
  const plan = planOpenClawBrainConvergePluginAction(before);
1741
+ if (plan.action === "noop") {
1742
+ return {
1743
+ plan,
1744
+ command: null,
1745
+ changed: false,
1746
+ changeReasons: [],
1747
+ detail: "Skipped the OpenClaw plugin manager because the authoritative split-package plugin is already present and a no-op refresh would only churn volatile install metadata.",
1748
+ warning: null,
1749
+ capture: null,
1750
+ before,
1751
+ after: before
1752
+ };
1753
+ }
1741
1754
  let uninstallCapture = null;
1742
1755
  if (plan.action === "install" && shouldReplaceOpenClawBrainInstallBeforeConverge(before)) {
1743
1756
  uninstallCapture = runCapturedExternalCommand("openclaw", ["plugins", "uninstall", plan.pluginId]);
@@ -4761,13 +4774,13 @@ function resolveServeTimeLearningRuntimeInput(activationRoot, normalizedEventExp
4761
4774
  : readHistoricalServeTimeDecisions(logPath, collectServeTimeDecisionRecoveryTargets(normalizedEventExport));
4762
4775
  const serveTimeDecisions = mergeHistoricalServeTimeDecisions(historicalRecovery.decisions, boundedServeTimeDecisions);
4763
4776
  const decisionLogCount = serveTimeDecisions.length;
4764
- const pgVersion = decisionLogCount > 0 ? "v2" : "v1";
4777
+ const pgVersion = "v2";
4765
4778
  const resolvedFallbackReason = combineServeTimeLearningFallbackReasons(fallbackReason, historicalRecovery.scanFailed ? "historical_recovery_scan_failed" : null);
4766
4779
  return {
4767
4780
  pgVersion,
4768
4781
  serveTimeDecisions,
4769
4782
  decisionLogCount,
4770
- baselineState: pgVersion === "v2" ? loadOrInitBaseline(activationRoot) : undefined,
4783
+ baselineState: loadOrInitBaseline(activationRoot),
4771
4784
  fallbackReason: resolvedFallbackReason === null ? null : `serve_time_decision_log_${resolvedFallbackReason}`
4772
4785
  };
4773
4786
  }
@@ -103,10 +103,10 @@ export function planOpenClawBrainConvergePluginAction(fingerprint) {
103
103
  };
104
104
  }
105
105
  return {
106
- action: "update",
106
+ action: "noop",
107
107
  packageSpec: "@openclawbrain/openclaw",
108
108
  pluginId: "openclawbrain",
109
- reason: "refresh the existing split-package plugin install so install/upgrade/repair stays on one converge path",
109
+ reason: "the authoritative split-package plugin is already installed, so converge should preserve the current plugin-manager record instead of rewriting volatile install metadata",
110
110
  };
111
111
  }
112
112
 
@@ -384,11 +384,15 @@ function buildCoverageSnapshot({ attachedSetLine, runtimeLoadProofSnapshot, open
384
384
  profiles
385
385
  };
386
386
  }
387
- function buildHardeningSnapshot({ attachTruthLine, serveLine, routeFnLine, surfaceLine, verdict, statusSignals }) {
387
+ function buildHardeningSnapshot({ attachTruthLine, serveLine, routeFnLine, surfaceLine, brainLine, routeLine, learningLine, learningPathLine, verdict, statusSignals }) {
388
388
  const attachTruth = extractKeyValuePairs(attachTruthLine);
389
389
  const serve = extractKeyValuePairs(serveLine);
390
390
  const routeFn = extractKeyValuePairs(routeFnLine);
391
391
  const surface = extractKeyValuePairs(surfaceLine);
392
+ const brain = extractKeyValuePairs(brainLine);
393
+ const route = extractKeyValuePairs(routeLine);
394
+ const learning = extractKeyValuePairs(learningLine);
395
+ const learningPath = extractKeyValuePairs(learningPathLine);
392
396
  return {
393
397
  contract: "openclaw_operator_hardening_snapshot.v1",
394
398
  generatedAt: new Date().toISOString(),
@@ -427,6 +431,27 @@ function buildHardeningSnapshot({ attachTruthLine, serveLine, routeFnLine, surfa
427
431
  available: routeFn.available ?? null,
428
432
  freshness: routeFn.freshness ?? null,
429
433
  },
434
+ routeLayer: {
435
+ activePackId: brain.pack ?? null,
436
+ routerIdentity: brain.router ?? route.router ?? null,
437
+ routeFreshness: brain.routeFreshness ?? null,
438
+ routeFingerprint: route.freshness ?? null,
439
+ lastPromotionAt: brain.lastPromotion ?? null,
440
+ brainState: brain.state ?? null,
441
+ initMode: brain.init ?? null,
442
+ serveState: serve.state ?? null,
443
+ usedLearnedRouteFn: serve.usedRouteFn ?? null,
444
+ routeFnAvailable: routeFn.available ?? null,
445
+ routeFnFreshness: routeFn.freshness ?? null,
446
+ learningState: learning.state ?? null,
447
+ learningMode: learning.mode ?? null,
448
+ learningPathSource: learningPath.source ?? null,
449
+ learningPathPg: learningPath.pg ?? null,
450
+ learningPathMethod: learningPath.method ?? null,
451
+ learningPathTarget: learningPath.target ?? null,
452
+ learningPathConnect: learningPath.connect ?? null,
453
+ learningPathTrajectories: learningPath.trajectories ?? null,
454
+ },
430
455
  verdict: {
431
456
  verdict: verdict.verdict,
432
457
  severity: verdict.severity,
@@ -576,7 +601,7 @@ function buildVerdict({ steps, gatewayStatus, pluginInspect, statusSignals, brea
576
601
  };
577
602
  }
578
603
 
579
- function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspectText, statusSignals, breadcrumbs, runtimeLoadProofSnapshot, surfaceLine, surfacesLine, surfaceNoteLine, hotfixLine, guardLine, feedbackLine, attributionLine, attributionCoverageLine, learningPathLine, learningFlowLine, learningHealthLine, coverageSnapshot, hardeningSnapshot }) {
604
+ function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspectText, statusSignals, breadcrumbs, runtimeLoadProofSnapshot, surfaceLine, surfacesLine, surfaceNoteLine, hotfixLine, guardLine, brainLine, routeLine, learningLine, feedbackLine, attributionLine, attributionCoverageLine, learningPathLine, learningFlowLine, learningHealthLine, coverageSnapshot, hardeningSnapshot }) {
580
605
  const passed = [];
581
606
  const missing = [];
582
607
  const warnings = Array.isArray(verdict.warnings) ? verdict.warnings : [];
@@ -660,6 +685,21 @@ function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspec
660
685
  ? ["- runtime guard line not reported by detailed status"]
661
686
  : [`- ${guardLine}`]),
662
687
  "",
688
+ "## Route Layer Truth",
689
+ ...(brainLine === null
690
+ ? ["- brain line not reported by detailed status"]
691
+ : [`- ${brainLine}`]),
692
+ ...(routeLine === null
693
+ ? ["- route line not reported by detailed status"]
694
+ : [`- ${routeLine}`]),
695
+ ...(learningLine === null
696
+ ? ["- learning line not reported by detailed status"]
697
+ : [`- ${learningLine}`]),
698
+ ...(learningPathLine === null
699
+ ? ["- path line not reported by detailed status"]
700
+ : [`- ${learningPathLine}`]),
701
+ `- derived: activePack=${hardeningSnapshot.routeLayer.activePackId ?? "none"} router=${hardeningSnapshot.routeLayer.routerIdentity ?? "none"} routeFreshness=${hardeningSnapshot.routeLayer.routeFreshness ?? "none"} routeFingerprint=${hardeningSnapshot.routeLayer.routeFingerprint ?? "none"} usedLearnedRouteFn=${hardeningSnapshot.routeLayer.usedLearnedRouteFn ?? "none"}`,
702
+ "",
663
703
  "## Learning Flow",
664
704
  ...(learningPathLine === null
665
705
  ? ["- learning path line not reported by detailed status"]
@@ -988,6 +1028,9 @@ export function captureOperatorProofBundle(options) {
988
1028
  const serveLine = extractDetailedStatusLine(statusCapture.stdout, "serve");
989
1029
  const routeFnLine = extractDetailedStatusLine(statusCapture.stdout, "routeFn");
990
1030
  const guardLine = extractDetailedStatusLine(statusCapture.stdout, "guard");
1031
+ const brainLine = extractDetailedStatusLine(statusCapture.stdout, "brain");
1032
+ const routeLine = extractDetailedStatusLine(statusCapture.stdout, "route");
1033
+ const learningLine = extractDetailedStatusLine(statusCapture.stdout, "learning");
991
1034
  const learningFlowLine = extractDetailedStatusLine(statusCapture.stdout, "learnFlow");
992
1035
  const learningHealthLine = extractDetailedStatusLine(statusCapture.stdout, "health");
993
1036
  const feedbackLine = extractDetailedStatusLine(statusCapture.stdout, "feedback");
@@ -1024,6 +1067,10 @@ export function captureOperatorProofBundle(options) {
1024
1067
  serveLine,
1025
1068
  routeFnLine,
1026
1069
  surfaceLine,
1070
+ brainLine,
1071
+ routeLine,
1072
+ learningLine,
1073
+ learningPathLine,
1027
1074
  verdict,
1028
1075
  statusSignals,
1029
1076
  });
@@ -1053,6 +1100,9 @@ export function captureOperatorProofBundle(options) {
1053
1100
  surfaceNoteLine,
1054
1101
  hotfixLine,
1055
1102
  guardLine,
1103
+ brainLine,
1104
+ routeLine,
1105
+ learningLine,
1056
1106
  learningFlowLine,
1057
1107
  learningHealthLine,
1058
1108
  feedbackLine,
@@ -1075,6 +1125,9 @@ export function captureOperatorProofBundle(options) {
1075
1125
  surfaceNoteLine,
1076
1126
  hotfixLine,
1077
1127
  guardLine,
1128
+ brainLine,
1129
+ routeLine,
1130
+ learningLine,
1078
1131
  learningFlowLine,
1079
1132
  learningHealthLine,
1080
1133
  feedbackLine,
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin } from "./openclaw-plugin-install.js";
6
+ import { inspectOpenClawBrainHookStatus } from "./openclaw-hook-truth.js";
6
7
  const REQUIRED_RUNTIME_GUARD_EXPORTS = [
7
8
  "createBeforePromptBuildHandler",
8
9
  "isActivationRootPlaceholder",
@@ -70,6 +71,10 @@ export function inspectInstalledOpenClawBrainExtension(openclawHome, extensionId
70
71
  }
71
72
  export async function proveInstalledOpenClawBrainExtensionLoad(openclawHome, extensionId = "openclawbrain") {
72
73
  const inspected = inspectInstalledOpenClawBrainExtension(openclawHome, extensionId);
74
+ const hookStatus = inspectOpenClawBrainHookStatus(openclawHome);
75
+ if (hookStatus.loadability !== "loadable") {
76
+ throw new Error(`[shadow-extension-load-proof] Installed hook is not loadable enough to prove its loader: ${hookStatus.detail}`);
77
+ }
73
78
  const runtimeGuardModule = await importWithHelpfulError(inspected.runtimeGuardPath, `runtime-guard.js (${describeOpenClawBrainInstallLayout(inspected.installLayout)})`);
74
79
  const runtimeGuardExportNames = Object.keys(runtimeGuardModule).sort((left, right) => left.localeCompare(right));
75
80
  const missingRuntimeGuardExports = REQUIRED_RUNTIME_GUARD_EXPORTS.filter((exportName) => !runtimeGuardExportNames.includes(exportName));
@@ -272,13 +272,15 @@ function loadJsonFile(pathname) {
272
272
  function resolveActivePackPaths(activationRoot) {
273
273
  const pointers = toRecord(loadJsonFile(path.join(path.resolve(activationRoot), "activation-pointers.json")));
274
274
  const active = toRecord(pointers?.active);
275
+ const packId = normalizeOptionalString(active?.packId);
275
276
  const packRootDir = normalizeOptionalString(active?.packRootDir)
276
- ?? (normalizeOptionalString(active?.packId) === null
277
+ ?? (packId === null
277
278
  ? null
278
- : path.join(path.resolve(activationRoot), "packs", String(active.packId)));
279
+ : path.join(path.resolve(activationRoot), "packs", String(packId)));
279
280
  const manifestPath = normalizeOptionalString(active?.manifestPath)
280
281
  ?? (packRootDir === null ? null : path.join(packRootDir, "manifest.json"));
281
282
  return {
283
+ packId,
282
284
  packRootDir,
283
285
  manifestPath
284
286
  };
@@ -376,6 +378,38 @@ function buildWatchSnapshotAttributionCoverage(activationRoot) {
376
378
  detail: `watch sparse-feedback queue: completed_without_evaluation=0, ready=${normalizeCount(readyCount)}, delayed=${normalizeCount(delayedCount)}, budget_deferred=${normalizeCount(budgetDeferredCount)}`
377
379
  };
378
380
  }
381
+ function readWatchTeacherSnapshotPackTruth(activationRoot) {
382
+ const snapshot = toRecord(loadJsonFile(path.join(path.resolve(activationRoot), "watch", "teacher-snapshot.json")));
383
+ const learning = toRecord(snapshot?.learning);
384
+ const nestedSnapshot = toRecord(snapshot?.snapshot);
385
+ const nestedLearning = toRecord(nestedSnapshot?.learning);
386
+ return {
387
+ lastHandledMaterializationPackId: normalizeOptionalString(snapshot?.lastHandledMaterializationPackId)
388
+ ?? normalizeOptionalString(learning?.lastHandledMaterializationPackId)
389
+ ?? normalizeOptionalString(nestedLearning?.lastHandledMaterializationPackId),
390
+ lastMaterializationPackId: normalizeOptionalString(snapshot?.lastMaterializationPackId)
391
+ ?? normalizeOptionalString(learning?.lastMaterializationPackId)
392
+ ?? normalizeOptionalString(nestedLearning?.lastMaterializationPackId)
393
+ };
394
+ }
395
+ function buildActivationPackTruth(activationRoot) {
396
+ const active = resolveActivePackPaths(activationRoot);
397
+ const activePackId = active.packId;
398
+ if (activePackId === null) {
399
+ return null;
400
+ }
401
+ const watchTruth = readWatchTeacherSnapshotPackTruth(activationRoot);
402
+ const handledPackId = watchTruth.lastHandledMaterializationPackId ?? watchTruth.lastMaterializationPackId;
403
+ if (handledPackId === null) {
404
+ return null;
405
+ }
406
+ return {
407
+ activePackId,
408
+ handledPackId,
409
+ materializedPackId: handledPackId,
410
+ promoted: handledPackId === activePackId
411
+ };
412
+ }
379
413
  function shouldPreferActivationFeedbackSummary(current, fallback) {
380
414
  if (fallback === null) {
381
415
  return false;
@@ -407,18 +441,35 @@ function shouldPreferWatchAttributionCoverage(current, fallback) {
407
441
  function enrichBridgeWithActivationTruth(activationRoot, bridge) {
408
442
  const feedbackSummary = buildActivePackFeedbackSummary(activationRoot);
409
443
  const attributionCoverage = buildWatchSnapshotAttributionCoverage(activationRoot);
410
- if (!shouldPreferActivationFeedbackSummary(bridge.feedbackSummary, feedbackSummary)
411
- && !shouldPreferWatchAttributionCoverage(bridge.attributionCoverage, attributionCoverage)) {
444
+ const packTruth = buildActivationPackTruth(activationRoot);
445
+ const preferFeedbackSummary = shouldPreferActivationFeedbackSummary(bridge.feedbackSummary, feedbackSummary);
446
+ const preferAttributionCoverage = shouldPreferWatchAttributionCoverage(bridge.attributionCoverage, attributionCoverage);
447
+ const preferPackTruth = packTruth !== null
448
+ && (bridge.materializedPackId === null || (bridge.promoted !== true && packTruth.promoted === true))
449
+ && (packTruth.promoted === true || bridge.materializedPackId === null);
450
+ if (!preferFeedbackSummary && !preferAttributionCoverage && !preferPackTruth) {
412
451
  return bridge;
413
452
  }
414
453
  return normalizeBridgePayload({
415
454
  ...bridge,
416
- feedbackSummary: shouldPreferActivationFeedbackSummary(bridge.feedbackSummary, feedbackSummary)
455
+ materializedPackId: preferPackTruth ? packTruth.materializedPackId : bridge.materializedPackId,
456
+ promoted: preferPackTruth ? packTruth.promoted : bridge.promoted,
457
+ feedbackSummary: preferFeedbackSummary
417
458
  ? feedbackSummary
418
459
  : bridge.feedbackSummary,
419
- attributionCoverage: shouldPreferWatchAttributionCoverage(bridge.attributionCoverage, attributionCoverage)
460
+ attributionCoverage: preferAttributionCoverage
420
461
  ? attributionCoverage
421
- : bridge.attributionCoverage
462
+ : bridge.attributionCoverage,
463
+ source: preferPackTruth
464
+ ? {
465
+ ...(bridge.source ?? {}),
466
+ activationPackTruth: {
467
+ activePackId: packTruth.activePackId,
468
+ handledPackId: packTruth.handledPackId,
469
+ source: "active_pack_plus_watch_snapshot"
470
+ }
471
+ }
472
+ : bridge.source
422
473
  });
423
474
  }
424
475
  function normalizeLastInterruptionSummary(value) {
@@ -865,6 +916,21 @@ export function persistBrainStoreTracedLearningBridge(payload, options = {}) {
865
916
  try {
866
917
  db = new sqlite.DatabaseSync(dbPath);
867
918
  const summary = normalizePersistedStatusSurface(payload);
919
+ const existingSummaryLoaded = loadTrainingStateJson(db, TRACED_LEARNING_STATUS_SURFACE_STATE_KEY);
920
+ if (existingSummaryLoaded.value !== null) {
921
+ const existingSummary = normalizePersistedStatusSurface(existingSummaryLoaded.value);
922
+ if (JSON.stringify(existingSummary) === JSON.stringify(summary)) {
923
+ return {
924
+ path: dbPath,
925
+ bridge: buildPersistedStatusSurfaceBridge(existingSummary, {
926
+ brainRoot,
927
+ dbPath
928
+ }),
929
+ persisted: false,
930
+ error: null
931
+ };
932
+ }
933
+ }
868
934
  writeTrainingStateJson(db, TRACED_LEARNING_STATUS_SURFACE_STATE_KEY, summary);
869
935
  return {
870
936
  path: dbPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclawbrain/cli",
3
- "version": "0.4.31",
3
+ "version": "0.4.33",
4
4
  "description": "OpenClawBrain operator CLI package with install/status helpers, daemon controls, and import/export tooling.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",