@openclawbrain/cli 0.4.16 → 0.4.17

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.
@@ -15,9 +15,66 @@ function normalizeCount(value) {
15
15
  function normalizeOptionalString(value) {
16
16
  return typeof value === "string" && value.trim().length > 0 ? value : null;
17
17
  }
18
+ function normalizeUnitInterval(value) {
19
+ return Number.isFinite(value) ? Math.max(0, Math.min(1, Number(value))) : 0;
20
+ }
18
21
  function normalizeSource(value) {
19
22
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
20
23
  }
24
+ function normalizeLastInterruptionSummary(value) {
25
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
26
+ return null;
27
+ }
28
+ const normalized = {
29
+ reason: normalizeOptionalString(value.reason),
30
+ stage: normalizeOptionalString(value.stage),
31
+ servedPartial: value.servedPartial === true,
32
+ droppedFrontierCount: normalizeCount(value.droppedFrontierCount),
33
+ droppedProposalCount: normalizeCount(value.droppedProposalCount),
34
+ budgetUtilization: normalizeUnitInterval(value.budgetUtilization)
35
+ };
36
+ return normalized.reason !== null ||
37
+ normalized.stage !== null ||
38
+ normalized.servedPartial ||
39
+ normalized.droppedFrontierCount > 0 ||
40
+ normalized.droppedProposalCount > 0 ||
41
+ normalized.budgetUtilization > 0
42
+ ? normalized
43
+ : null;
44
+ }
45
+ function formatLastInterruptionDetail(value) {
46
+ const summary = normalizeLastInterruptionSummary(value);
47
+ if (summary === null) {
48
+ return null;
49
+ }
50
+ return [
51
+ `interrupt=${summary.reason ?? summary.stage ?? "unknown"}`,
52
+ `partial=${summary.servedPartial ? "yes" : "no"}`,
53
+ `frontier=${summary.droppedFrontierCount}`,
54
+ `proposals=${summary.droppedProposalCount}`,
55
+ `budget=${Math.round(summary.budgetUtilization * 100)}%`
56
+ ].join(" ");
57
+ }
58
+ function buildLastInterruptionSummaryFromAssemblyDecision(value) {
59
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
60
+ return null;
61
+ }
62
+ const accounting = value.interruptionAccounting !== null &&
63
+ typeof value.interruptionAccounting === "object" &&
64
+ !Array.isArray(value.interruptionAccounting)
65
+ ? value.interruptionAccounting
66
+ : null;
67
+ return normalizeLastInterruptionSummary({
68
+ reason: normalizeOptionalString(value.brainDropReason) ?? normalizeOptionalString(value.interruptionReason),
69
+ stage: normalizeOptionalString(value.interruptionStage),
70
+ servedPartial: value.servedPartial === true,
71
+ droppedFrontierCount: Array.isArray(accounting?.droppedFrontierNodeIds)
72
+ ? accounting.droppedFrontierNodeIds.filter((entry) => typeof entry === "string" && entry.trim().length > 0).length
73
+ : normalizeCount(accounting?.droppedFrontierCount),
74
+ droppedProposalCount: normalizeCount(accounting?.droppedProposalCount),
75
+ budgetUtilization: accounting?.budgetUtilization
76
+ });
77
+ }
21
78
  function summarizeBridgeSource(value) {
22
79
  const source = normalizeSource(value);
23
80
  if (source === null) {
@@ -53,6 +110,7 @@ function normalizeBridgePayload(payload) {
53
110
  materializedPackId: normalizeOptionalString(payload.materializedPackId),
54
111
  promoted: payload.promoted === true,
55
112
  baselinePersisted: payload.baselinePersisted === true,
113
+ lastInterruptionSummary: normalizeLastInterruptionSummary(payload.lastInterruptionSummary),
56
114
  source: normalizeSource(payload.source)
57
115
  };
58
116
  }
@@ -79,6 +137,7 @@ function normalizePersistedStatusSurface(payload) {
79
137
  materializedPackId: normalizeOptionalString(payload.materializedPackId),
80
138
  promoted: payload.promoted === true,
81
139
  baselinePersisted: payload.baselinePersisted === true,
140
+ lastInterruptionSummary: normalizeLastInterruptionSummary(payload.lastInterruptionSummary),
82
141
  source
83
142
  };
84
143
  }
@@ -97,6 +156,7 @@ function defaultSurface(pathname, detail, error = null) {
97
156
  materializedPackId: null,
98
157
  promoted: false,
99
158
  baselinePersisted: false,
159
+ lastInterruptionSummary: null,
100
160
  source: null,
101
161
  detail,
102
162
  error
@@ -145,6 +205,10 @@ function loadTrainingStateJson(db, key) {
145
205
  };
146
206
  }
147
207
  }
208
+ function loadLastAssemblyInterruptionSummary(db) {
209
+ const loaded = loadTrainingStateJson(db, "last_assembly_decision_json");
210
+ return loaded.value === null ? null : buildLastInterruptionSummaryFromAssemblyDecision(loaded.value);
211
+ }
148
212
  function writeTrainingStateJson(db, key, value) {
149
213
  db.prepare(`INSERT OR REPLACE INTO brain_training_state (key, value) VALUES (?, ?)`).run(key, JSON.stringify(value));
150
214
  }
@@ -187,6 +251,7 @@ export function buildTracedLearningBridgePayloadFromRuntime(input) {
187
251
  materializedPackId: input?.materializedPackId ?? lastMaterialization?.candidate?.summary?.packId ?? null,
188
252
  promoted: input?.promoted === true,
189
253
  baselinePersisted: input?.baselinePersisted === true,
254
+ lastInterruptionSummary: input?.lastInterruptionSummary ?? null,
190
255
  source: input?.source
191
256
  });
192
257
  }
@@ -212,6 +277,7 @@ function buildPersistedStatusSurfaceBridge(summary, context) {
212
277
  materializedPackId: summary.materializedPackId,
213
278
  promoted: summary.promoted,
214
279
  baselinePersisted: summary.baselinePersisted,
280
+ lastInterruptionSummary: summary.lastInterruptionSummary,
215
281
  source: {
216
282
  command: "brain-store",
217
283
  bridge: TRACED_LEARNING_STATUS_SURFACE_BRIDGE,
@@ -246,7 +312,7 @@ function loadPersistedStatusSurface(db, context) {
246
312
  };
247
313
  }
248
314
  }
249
- function buildDerivedBrainStoreBridge(db, context) {
315
+ function buildDerivedBrainStoreBridge(db, context, lastInterruptionSummary = null) {
250
316
  const routeTraceCount = countRows(db, "brain_traces");
251
317
  const supervisionCount = countRows(db, "brain_trace_supervision");
252
318
  const candidateUpdateRaw = loadTrainingStateValue(db, "last_pg_candidate_update_json");
@@ -269,6 +335,7 @@ function buildDerivedBrainStoreBridge(db, context) {
269
335
  materializedPackId: null,
270
336
  promoted: false,
271
337
  baselinePersisted: false,
338
+ lastInterruptionSummary,
272
339
  source: {
273
340
  command: "brain-store",
274
341
  bridge: "brain_store_state",
@@ -288,6 +355,7 @@ function hasMeaningfulTracedLearningSignal(bridge) {
288
355
  bridge.materializedPackId !== null ||
289
356
  bridge.promoted ||
290
357
  bridge.baselinePersisted ||
358
+ bridge.lastInterruptionSummary !== null ||
291
359
  bridge.pgVersionRequested !== null ||
292
360
  bridge.pgVersionUsed !== null ||
293
361
  bridge.fallbackReason !== null ||
@@ -410,21 +478,28 @@ export function loadBrainStoreTracedLearningBridge(options = {}) {
410
478
  let db;
411
479
  try {
412
480
  db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
481
+ const lastInterruptionSummary = loadLastAssemblyInterruptionSummary(db);
413
482
  const persisted = loadPersistedStatusSurface(db, {
414
483
  brainRoot,
415
484
  dbPath
416
485
  });
417
486
  if (persisted.bridge !== null) {
487
+ const bridge = lastInterruptionSummary === null
488
+ ? persisted.bridge
489
+ : normalizeBridgePayload({
490
+ ...persisted.bridge,
491
+ lastInterruptionSummary
492
+ });
418
493
  return {
419
494
  path: dbPath,
420
- bridge: persisted.bridge,
495
+ bridge,
421
496
  error: null
422
497
  };
423
498
  }
424
499
  const bridge = buildDerivedBrainStoreBridge(db, {
425
500
  brainRoot,
426
501
  dbPath
427
- });
502
+ }, lastInterruptionSummary);
428
503
  if (!hasMeaningfulTracedLearningSignal(bridge)) {
429
504
  return {
430
505
  path: dbPath,
@@ -471,6 +546,10 @@ function buildStatusSurface(pathname, bridge, options = {}) {
471
546
  if (bridge.routerNoOpReason !== null) {
472
547
  detailParts.push(`noOp=${bridge.routerNoOpReason}`);
473
548
  }
549
+ const interruptionDetail = formatLastInterruptionDetail(bridge.lastInterruptionSummary);
550
+ if (interruptionDetail !== null) {
551
+ detailParts.push(interruptionDetail);
552
+ }
474
553
  return {
475
554
  path: pathname,
476
555
  present: true,
@@ -485,6 +564,7 @@ function buildStatusSurface(pathname, bridge, options = {}) {
485
564
  materializedPackId: bridge.materializedPackId,
486
565
  promoted: bridge.promoted,
487
566
  baselinePersisted: bridge.baselinePersisted,
567
+ lastInterruptionSummary: bridge.lastInterruptionSummary,
488
568
  source: bridge.source,
489
569
  detail: detailParts.join(" "),
490
570
  error: options.error ?? null
@@ -507,6 +587,7 @@ function buildRuntimeMaterializationMetadata(loaded) {
507
587
  materializedPackId: loaded.bridge.materializedPackId,
508
588
  promoted: loaded.bridge.promoted,
509
589
  baselinePersisted: loaded.bridge.baselinePersisted,
590
+ lastInterruptionSummary: loaded.bridge.lastInterruptionSummary,
510
591
  fallbackReason: loaded.bridge.fallbackReason,
511
592
  routerNoOpReason: loaded.bridge.routerNoOpReason,
512
593
  source: loaded.bridge.source
@@ -529,6 +610,7 @@ function mergeCanonicalStatusBridge(canonicalBridge, runtimeLoaded) {
529
610
  materializedPackId: canonicalBridge.materializedPackId,
530
611
  promoted: canonicalBridge.promoted,
531
612
  baselinePersisted: canonicalBridge.baselinePersisted,
613
+ lastInterruptionSummary: canonicalBridge.lastInterruptionSummary ?? runtimeBridge?.lastInterruptionSummary ?? null,
532
614
  fallbackReason: canonicalBridge.fallbackReason,
533
615
  routerNoOpReason: canonicalBridge.routerNoOpReason,
534
616
  source: runtimeMaterialized === null
@@ -551,6 +633,7 @@ function mergeCanonicalStatusBridge(canonicalBridge, runtimeLoaded) {
551
633
  materializedPackId: runtimeBridge?.materializedPackId ?? canonicalBridge.materializedPackId ?? null,
552
634
  promoted: runtimeBridge?.promoted ?? canonicalBridge.promoted,
553
635
  baselinePersisted: runtimeBridge?.baselinePersisted ?? canonicalBridge.baselinePersisted,
636
+ lastInterruptionSummary: canonicalBridge.lastInterruptionSummary ?? runtimeBridge?.lastInterruptionSummary ?? null,
554
637
  fallbackReason: runtimeBridge?.fallbackReason ?? canonicalBridge.fallbackReason ?? null,
555
638
  routerNoOpReason: runtimeBridge?.routerNoOpReason ?? canonicalBridge.routerNoOpReason ?? null,
556
639
  source: runtimeMaterialized === null
@@ -571,10 +654,12 @@ export function mergeTracedLearningBridgePayload(payload, persisted) {
571
654
  const supervisionCount = Math.max(current.supervisionCount, persistedBridge.supervisionCount);
572
655
  const routerUpdateCount = Math.max(current.routerUpdateCount, persistedBridge.routerUpdateCount);
573
656
  const teacherArtifactCount = Math.max(current.teacherArtifactCount, persistedBridge.teacherArtifactCount);
657
+ const lastInterruptionSummary = current.lastInterruptionSummary ?? persistedBridge.lastInterruptionSummary ?? null;
574
658
  const usedBridge = routeTraceCount !== current.routeTraceCount ||
575
659
  supervisionCount !== current.supervisionCount ||
576
660
  routerUpdateCount !== current.routerUpdateCount ||
577
- teacherArtifactCount !== current.teacherArtifactCount;
661
+ teacherArtifactCount !== current.teacherArtifactCount ||
662
+ lastInterruptionSummary !== current.lastInterruptionSummary;
578
663
  if (!usedBridge) {
579
664
  return current;
580
665
  }
@@ -584,6 +669,7 @@ export function mergeTracedLearningBridgePayload(payload, persisted) {
584
669
  supervisionCount,
585
670
  routerUpdateCount,
586
671
  teacherArtifactCount,
672
+ lastInterruptionSummary,
587
673
  routerNoOpReason: supervisionCount > 0 || routerUpdateCount > 0 ? null : current.routerNoOpReason,
588
674
  source: {
589
675
  ...(current.source ?? {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclawbrain/cli",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
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",