@openclawbrain/openclaw 0.3.1 → 0.3.3

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.
Files changed (35) hide show
  1. package/dist/extension/index.js +17 -1
  2. package/dist/extension/index.js.map +1 -1
  3. package/dist/extension/runtime-guard.js +5 -0
  4. package/dist/extension/runtime-guard.js.map +1 -1
  5. package/dist/src/attachment-truth.d.ts +34 -0
  6. package/dist/src/attachment-truth.js +215 -0
  7. package/dist/src/attachment-truth.js.map +1 -0
  8. package/dist/src/cli.d.ts +7 -1
  9. package/dist/src/cli.js +1414 -67
  10. package/dist/src/cli.js.map +1 -1
  11. package/dist/src/daemon.d.ts +42 -1
  12. package/dist/src/daemon.js +360 -50
  13. package/dist/src/daemon.js.map +1 -1
  14. package/dist/src/index.d.ts +89 -1
  15. package/dist/src/index.js +764 -105
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/learning-spine.d.ts +3 -1
  18. package/dist/src/learning-spine.js +1 -0
  19. package/dist/src/learning-spine.js.map +1 -1
  20. package/dist/src/local-session-passive-learning.js +6 -1
  21. package/dist/src/local-session-passive-learning.js.map +1 -1
  22. package/dist/src/openclaw-hook-truth.d.ts +25 -0
  23. package/dist/src/openclaw-hook-truth.js +154 -0
  24. package/dist/src/openclaw-hook-truth.js.map +1 -0
  25. package/dist/src/semantic-metadata.d.ts +4 -0
  26. package/dist/src/semantic-metadata.js +41 -0
  27. package/dist/src/semantic-metadata.js.map +1 -0
  28. package/dist/src/session-tail.d.ts +2 -0
  29. package/dist/src/session-tail.js +54 -14
  30. package/dist/src/session-tail.js.map +1 -1
  31. package/dist/src/shadow-extension-proof.js +4 -0
  32. package/dist/src/shadow-extension-proof.js.map +1 -1
  33. package/extension/index.ts +19 -1
  34. package/extension/runtime-guard.ts +6 -0
  35. package/package.json +7 -7
package/dist/src/index.js CHANGED
@@ -3,11 +3,14 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, wri
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { compileRuntimeFromActivation } from "@openclawbrain/compiler";
6
- import { CONTRACT_IDS, buildNormalizedEventExport, canonicalJson, checksumJsonPayload, createFeedbackEvent, createInteractionEvent, sortNormalizedEvents, validateKernelSurface, validateNormalizedEventExport } from "@openclawbrain/contracts";
6
+ import { CONTRACT_IDS, buildEventSemanticSurface, buildNormalizedEventExport, canonicalJson, checksumJsonPayload, createFeedbackEvent, createInteractionEvent, sortNormalizedEvents, validateKernelSurface, validateNormalizedEventExport } from "@openclawbrain/contracts";
7
7
  import { classifyFeedbackSignalContent, describeNormalizedEventExportObservability } from "@openclawbrain/event-export";
8
8
  import { DEFAULT_TEACHER_SUPERVISION_STALE_AFTER_MS, advanceAlwaysOnLearningRuntime, buildTeacherSupervisionArtifactsFromNormalizedEventExport, createAlwaysOnLearningRuntimeState, describeAlwaysOnLearningRuntimeState, materializeAlwaysOnLearningCandidatePack, materializeCandidatePackFromNormalizedEventExport } from "@openclawbrain/learner";
9
9
  import { LEARNING_SPINE_LOG_LAYOUT, activatePack, describeActivationObservability, describeActivationTarget, describePackCompileTarget, inspectActivationState, loadPackFromActivation, promoteCandidatePack, readLearningSpineLogEntries, rollbackActivePack, stageCandidatePack } from "@openclawbrain/pack-format";
10
+ import { inspectOpenClawBrainHookStatus, summarizeOpenClawBrainHookLoad } from "./openclaw-hook-truth.js";
10
11
  import { appendLearningUpdateLogs, appendServeTimeRouteDecisionLog } from "./learning-spine.js";
12
+ import { buildFeedbackSemanticMetadata, buildInteractionSemanticMetadata } from "./semantic-metadata.js";
13
+ export { clearOpenClawProfileRuntimeLoadProof, listOpenClawProfileRuntimeLoadProofs, recordOpenClawProfileRuntimeLoadProof, resolveAttachmentRuntimeLoadProofsPath } from "./attachment-truth.js";
11
14
  import { createTeacherLabeler } from "./teacher-labeler.js";
12
15
  export { createHttpOllamaTeacherLabelerClient, createOllamaTeacherLabeler, createTeacherLabeler } from "./teacher-labeler.js";
13
16
  const DEFAULT_AGENT_ID = "openclaw-runtime";
@@ -19,6 +22,7 @@ const RECORDED_SESSION_BUNDLE_CONTRACT = "recorded_session_replay_bundle.v1";
19
22
  const RUNTIME_EVENT_EXPORT_BUNDLE_CONTRACT = "normalized_event_export_bundle.v1";
20
23
  const DEFAULT_ATTACH_STATUS_MESSAGE = "openclaw attach status probe";
21
24
  const DEFAULT_ATTACH_STATUS_RUNTIME_HINTS = ["attach", "status", "probe"];
25
+ const BRAIN_SERVE_HOT_PATH_TIMING_DETAIL = "Measured inside compileRuntimeContext before serve-route logging; includes serve-path normalization, active-pack lookup, structural-budget resolution, route/candidate selection, and prompt assembly when run; excludes background scanner/embedder/teacher work, promotion, and runtime event-export writes.";
22
26
  export const RUNTIME_EVENT_EXPORT_BUNDLE_LAYOUT = {
23
27
  manifest: "manifest.json",
24
28
  payload: "normalized-event-export.json"
@@ -1058,6 +1062,7 @@ export function resolveAsyncTeacherLiveLoopSnapshotPath(activationRoot) {
1058
1062
  export const WATCH_STATE_DIRNAME = "watch";
1059
1063
  export const WATCH_SESSION_TAIL_CURSOR_BASENAME = "session-tail-cursor.json";
1060
1064
  export const WATCH_TEACHER_SNAPSHOT_BASENAME = "teacher-snapshot.json";
1065
+ export const DEFAULT_WATCH_POLL_INTERVAL_SECONDS = 30;
1061
1066
  function isAsyncTeacherLiveLoopSnapshot(value) {
1062
1067
  if (value === null || typeof value !== "object") {
1063
1068
  return false;
@@ -1092,6 +1097,94 @@ function cloneWatchTeacherSnapshotFailure(value) {
1092
1097
  at: value.at
1093
1098
  };
1094
1099
  }
1100
+ function buildUnavailableLastObservedDelta(explanation) {
1101
+ return {
1102
+ available: false,
1103
+ observedAt: null,
1104
+ exported: null,
1105
+ labeled: null,
1106
+ promoted: null,
1107
+ served: null,
1108
+ latestPackTransition: null,
1109
+ explanation
1110
+ };
1111
+ }
1112
+ function cloneLastObservedDelta(value) {
1113
+ if (value === null || value === undefined || typeof value !== "object") {
1114
+ return buildUnavailableLastObservedDelta("last observed delta is unavailable");
1115
+ }
1116
+ const transition = value.latestPackTransition;
1117
+ const latestPackTransition = transition !== null &&
1118
+ transition !== undefined &&
1119
+ (transition.kind === "staged_candidate" || transition.kind === "promoted_active") &&
1120
+ typeof transition.toPackId === "string" &&
1121
+ transition.toPackId.length > 0 &&
1122
+ (transition.fromPackId === null || (typeof transition.fromPackId === "string" && transition.fromPackId.length > 0))
1123
+ ? {
1124
+ kind: transition.kind,
1125
+ fromPackId: transition.fromPackId,
1126
+ toPackId: transition.toPackId
1127
+ }
1128
+ : null;
1129
+ const available = value.available === true;
1130
+ return {
1131
+ available,
1132
+ observedAt: typeof value.observedAt === "string" ? value.observedAt : null,
1133
+ exported: available && typeof value.exported === "boolean" ? value.exported : null,
1134
+ labeled: available && typeof value.labeled === "boolean" ? value.labeled : null,
1135
+ promoted: available && typeof value.promoted === "boolean" ? value.promoted : null,
1136
+ served: available && typeof value.served === "boolean" ? value.served : null,
1137
+ latestPackTransition,
1138
+ explanation: typeof value.explanation === "string" && value.explanation.trim().length > 0
1139
+ ? value.explanation
1140
+ : "last observed delta is unavailable"
1141
+ };
1142
+ }
1143
+ function cloneWatchEmbedInstrumentationPoint(value) {
1144
+ if (value === null || value === undefined || typeof value !== "object") {
1145
+ return null;
1146
+ }
1147
+ const candidate = value;
1148
+ if (candidate.slot !== null &&
1149
+ candidate.slot !== "candidate" &&
1150
+ candidate.slot !== "active") {
1151
+ return null;
1152
+ }
1153
+ return {
1154
+ slot: candidate.slot ?? null,
1155
+ packId: typeof candidate.packId === "string" ? candidate.packId : null,
1156
+ runtimeEmbedderPresent: candidate.runtimeEmbedderPresent === true,
1157
+ runtimeEmbedderModel: typeof candidate.runtimeEmbedderModel === "string" ? candidate.runtimeEmbedderModel : null,
1158
+ vectorEntryCount: typeof candidate.vectorEntryCount === "number" ? candidate.vectorEntryCount : null,
1159
+ numericEmbeddingEntryCount: typeof candidate.numericEmbeddingEntryCount === "number" ? candidate.numericEmbeddingEntryCount : null,
1160
+ embeddingModels: Array.isArray(candidate.embeddingModels)
1161
+ ? candidate.embeddingModels.filter((model) => typeof model === "string")
1162
+ : [],
1163
+ error: typeof candidate.error === "string" ? candidate.error : null
1164
+ };
1165
+ }
1166
+ function cloneWatchEmbedInstrumentationTrace(value) {
1167
+ if (value === null || value === undefined || typeof value !== "object") {
1168
+ return null;
1169
+ }
1170
+ const candidate = value;
1171
+ const beforeCandidateMaterialization = cloneWatchEmbedInstrumentationPoint(candidate.beforeCandidateMaterialization);
1172
+ if (beforeCandidateMaterialization === null || typeof candidate.observedAt !== "string") {
1173
+ return null;
1174
+ }
1175
+ return {
1176
+ observedAt: candidate.observedAt,
1177
+ candidatePackId: typeof candidate.candidatePackId === "string" ? candidate.candidatePackId : null,
1178
+ promotionAllowed: typeof candidate.promotionAllowed === "boolean" ? candidate.promotionAllowed : null,
1179
+ promotionFindings: Array.isArray(candidate.promotionFindings)
1180
+ ? candidate.promotionFindings.filter((finding) => typeof finding === "string")
1181
+ : [],
1182
+ beforeCandidateMaterialization,
1183
+ afterCandidateMaterialization: cloneWatchEmbedInstrumentationPoint(candidate.afterCandidateMaterialization),
1184
+ afterStage: cloneWatchEmbedInstrumentationPoint(candidate.afterStage),
1185
+ afterPromote: cloneWatchEmbedInstrumentationPoint(candidate.afterPromote)
1186
+ };
1187
+ }
1095
1188
  function cloneRuntimeEventExportScannerCheckpoint(value) {
1096
1189
  return structuredClone(value);
1097
1190
  }
@@ -1159,6 +1252,11 @@ function normalizeWatchTeacherSnapshotFromValue(value, snapshot, sourcePath) {
1159
1252
  : typeof value.updatedAt === "string"
1160
1253
  ? value.updatedAt
1161
1254
  : snapshot.diagnostics.lastProcessedAt ?? new Date(0).toISOString(),
1255
+ pollIntervalSeconds: typeof value.pollIntervalSeconds === "number" &&
1256
+ Number.isInteger(value.pollIntervalSeconds) &&
1257
+ value.pollIntervalSeconds > 0
1258
+ ? value.pollIntervalSeconds
1259
+ : DEFAULT_WATCH_POLL_INTERVAL_SECONDS,
1162
1260
  scanRoot: typeof value.scanRoot === "string" ? value.scanRoot : defaultScanRoot,
1163
1261
  sessionTailCursorPath: typeof value.sessionTailCursorPath === "string"
1164
1262
  ? value.sessionTailCursorPath
@@ -1193,6 +1291,8 @@ function normalizeWatchTeacherSnapshotFromValue(value, snapshot, sourcePath) {
1193
1291
  teacher: value.teacher ?? buildWatchTeacherSnapshotTeacherSummary(snapshot),
1194
1292
  learning: value.learning ?? buildWatchTeacherSnapshotLearningSummary(snapshot, typeof value.lastHandledMaterializationPackId === "string" ? value.lastHandledMaterializationPackId : null),
1195
1293
  labeling: value.labeling ?? buildWatchTeacherSnapshotLabelingSummary(snapshot),
1294
+ lastObservedDelta: cloneLastObservedDelta(value.lastObservedDelta),
1295
+ embedInstrumentation: cloneWatchEmbedInstrumentationTrace(value.embedInstrumentation),
1196
1296
  failure: cloneWatchTeacherSnapshotFailure(value.failure),
1197
1297
  snapshot
1198
1298
  };
@@ -1250,6 +1350,8 @@ export function loadWatchTeacherSnapshotState(snapshotPath) {
1250
1350
  if (!existsSync(resolvedPath)) {
1251
1351
  return {
1252
1352
  lastHandledMaterializationPackId: null,
1353
+ lastObservedDelta: buildUnavailableLastObservedDelta("no watch teacher snapshot is visible for the latest observed cycle"),
1354
+ embedInstrumentation: null,
1253
1355
  snapshot: null,
1254
1356
  error: null
1255
1357
  };
@@ -1261,6 +1363,8 @@ export function loadWatchTeacherSnapshotState(snapshotPath) {
1261
1363
  catch (error) {
1262
1364
  return {
1263
1365
  lastHandledMaterializationPackId: null,
1366
+ lastObservedDelta: buildUnavailableLastObservedDelta("watch teacher snapshot could not be loaded"),
1367
+ embedInstrumentation: null,
1264
1368
  snapshot: null,
1265
1369
  error: error instanceof Error ? error.message : String(error)
1266
1370
  };
@@ -1268,6 +1372,8 @@ export function loadWatchTeacherSnapshotState(snapshotPath) {
1268
1372
  if (isWatchTeacherSnapshot(parsed)) {
1269
1373
  return {
1270
1374
  lastHandledMaterializationPackId: parsed.lastHandledMaterializationPackId,
1375
+ lastObservedDelta: cloneLastObservedDelta(parsed.lastObservedDelta),
1376
+ embedInstrumentation: cloneWatchEmbedInstrumentationTrace(parsed.embedInstrumentation),
1271
1377
  snapshot: loadAsyncTeacherLiveLoopSnapshotFromValue(parsed.snapshot),
1272
1378
  error: null
1273
1379
  };
@@ -1275,12 +1381,16 @@ export function loadWatchTeacherSnapshotState(snapshotPath) {
1275
1381
  if (isAsyncTeacherLiveLoopSnapshot(parsed)) {
1276
1382
  return {
1277
1383
  lastHandledMaterializationPackId: null,
1384
+ lastObservedDelta: buildUnavailableLastObservedDelta("raw async teacher snapshots do not record the last observed export/label/promotion delta"),
1385
+ embedInstrumentation: null,
1278
1386
  snapshot: loadAsyncTeacherLiveLoopSnapshotFromValue(parsed),
1279
1387
  error: null
1280
1388
  };
1281
1389
  }
1282
1390
  return {
1283
1391
  lastHandledMaterializationPackId: null,
1392
+ lastObservedDelta: buildUnavailableLastObservedDelta("watch teacher snapshot is invalid"),
1393
+ embedInstrumentation: null,
1284
1394
  snapshot: null,
1285
1395
  error: `watch teacher snapshot is invalid: ${resolvedPath}`
1286
1396
  };
@@ -1293,6 +1403,7 @@ export function persistWatchTeacherSnapshot(snapshotPath, input) {
1293
1403
  runtimeOwner: "openclaw",
1294
1404
  updatedAt: persistedAt,
1295
1405
  lastRunAt: input.lastRunAt,
1406
+ pollIntervalSeconds: input.pollIntervalSeconds,
1296
1407
  scanRoot: path.resolve(input.scanRoot),
1297
1408
  sessionTailCursorPath: path.resolve(input.sessionTailCursorPath),
1298
1409
  sessionTailCursorUpdatedAt: input.sessionTailCursorUpdatedAt,
@@ -1311,6 +1422,8 @@ export function persistWatchTeacherSnapshot(snapshotPath, input) {
1311
1422
  teacher: buildWatchTeacherSnapshotTeacherSummary(canonicalSnapshot),
1312
1423
  learning: buildWatchTeacherSnapshotLearningSummary(canonicalSnapshot, input.lastHandledMaterializationPackId),
1313
1424
  labeling: buildWatchTeacherSnapshotLabelingSummary(canonicalSnapshot),
1425
+ lastObservedDelta: cloneLastObservedDelta(input.lastObservedDelta),
1426
+ embedInstrumentation: cloneWatchEmbedInstrumentationTrace(input.embedInstrumentation),
1314
1427
  failure: cloneWatchTeacherSnapshotFailure(input.failure),
1315
1428
  snapshot: canonicalSnapshot
1316
1429
  };
@@ -1383,7 +1496,12 @@ export function buildRuntimeEventExportBundleManifest(input) {
1383
1496
  interactionCount: input.normalizedEventExport.provenance.interactionCount,
1384
1497
  feedbackCount: input.normalizedEventExport.provenance.feedbackCount,
1385
1498
  sourceStreams: [...input.normalizedEventExport.provenance.sourceStreams],
1386
- contracts: [...input.normalizedEventExport.provenance.contracts]
1499
+ contracts: [...input.normalizedEventExport.provenance.contracts],
1500
+ semanticSurface: structuredClone(input.normalizedEventExport.provenance.semanticSurface ??
1501
+ buildEventSemanticSurface([
1502
+ ...input.normalizedEventExport.interactionEvents,
1503
+ ...input.normalizedEventExport.feedbackEvents
1504
+ ]))
1387
1505
  },
1388
1506
  ...(input.scanner !== undefined ? { scanner: cloneScannerExportManifestOrNull(input.scanner) } : {})
1389
1507
  };
@@ -1426,7 +1544,8 @@ export function validateRuntimeEventExportBundleManifest(value, normalizedEventE
1426
1544
  if (rebuilt.payloadDigest !== value.payloadDigest) {
1427
1545
  errors.push("event export bundle payloadDigest does not match the supplied normalized event export");
1428
1546
  }
1429
- if (canonicalJson(rebuilt.summary) !== canonicalJson(value.summary)) {
1547
+ const expectedSummary = value.summary.semanticSurface === undefined ? { ...rebuilt.summary, semanticSurface: undefined } : rebuilt.summary;
1548
+ if (canonicalJson(expectedSummary) !== canonicalJson(value.summary)) {
1430
1549
  errors.push("event export bundle summary does not match the supplied normalized event export");
1431
1550
  }
1432
1551
  if (canonicalJson(rebuilt.scanner ?? null) !== canonicalJson(value.scanner ?? null)) {
@@ -1542,8 +1661,14 @@ export function buildNormalizedEventExportFromScannedEvents(input) {
1542
1661
  };
1543
1662
  }
1544
1663
  const normalizedEventExport = buildNormalizedEventExport({
1545
- interactionEvents: [...input.interactionEvents],
1546
- feedbackEvents: [...input.feedbackEvents]
1664
+ interactionEvents: input.interactionEvents.map((event) => ({
1665
+ ...event,
1666
+ semantic: event.semantic ?? buildInteractionSemanticMetadata("scanner_export", event.kind)
1667
+ })),
1668
+ feedbackEvents: input.feedbackEvents.map((event) => ({
1669
+ ...event,
1670
+ semantic: event.semantic ?? buildFeedbackSemanticMetadata("scanner_export", event.kind)
1671
+ }))
1547
1672
  });
1548
1673
  const exportErrors = validateNormalizedEventExport(normalizedEventExport);
1549
1674
  if (exportErrors.length > 0) {
@@ -2396,17 +2521,53 @@ export function formatPromptContext(compileResponse) {
2396
2521
  function resolveActivationRootForFailure(value) {
2397
2522
  return path.resolve(normalizeOptionalString(value) ?? ".");
2398
2523
  }
2399
- function failOpenCompileResult(error, activationRoot) {
2524
+ function monotonicClockNs() {
2525
+ return process.hrtime.bigint();
2526
+ }
2527
+ function elapsedMsFrom(startedAtNs, endedAtNs = monotonicClockNs()) {
2528
+ return Number(endedAtNs - startedAtNs) / 1_000_000;
2529
+ }
2530
+ function roundHotPathTimingMs(value) {
2531
+ return Math.round(value * 1_000) / 1_000;
2532
+ }
2533
+ function buildUnavailableBrainServeHotPathTiming(detail) {
2534
+ return {
2535
+ scope: "brain_serve_hot_path_only",
2536
+ totalMs: null,
2537
+ routeSelectionMs: null,
2538
+ promptAssemblyMs: null,
2539
+ otherMs: null,
2540
+ backgroundWorkIncluded: false,
2541
+ detail
2542
+ };
2543
+ }
2544
+ function buildBrainServeHotPathTiming(input) {
2545
+ const totalMs = roundHotPathTimingMs(input.totalMs);
2546
+ const routeSelectionMs = input.routeSelectionMs === null ? null : roundHotPathTimingMs(input.routeSelectionMs);
2547
+ const promptAssemblyMs = input.promptAssemblyMs === null ? null : roundHotPathTimingMs(input.promptAssemblyMs);
2548
+ const otherMs = roundHotPathTimingMs(Math.max(0, input.totalMs - (input.routeSelectionMs ?? 0) - (input.promptAssemblyMs ?? 0)));
2549
+ return {
2550
+ scope: "brain_serve_hot_path_only",
2551
+ totalMs,
2552
+ routeSelectionMs,
2553
+ promptAssemblyMs,
2554
+ otherMs,
2555
+ backgroundWorkIncluded: false,
2556
+ detail: BRAIN_SERVE_HOT_PATH_TIMING_DETAIL
2557
+ };
2558
+ }
2559
+ function failOpenCompileResult(error, activationRoot, timing = buildUnavailableBrainServeHotPathTiming("serve-path timing was unavailable")) {
2400
2560
  return {
2401
2561
  ok: false,
2402
2562
  fallbackToStaticContext: true,
2403
2563
  hardRequirementViolated: false,
2404
2564
  activationRoot: path.resolve(activationRoot),
2405
2565
  error: toErrorMessage(error),
2406
- brainContext: ""
2566
+ brainContext: "",
2567
+ timing
2407
2568
  };
2408
2569
  }
2409
- function classifyCompileFailure(error, activationRoot) {
2570
+ function classifyCompileFailure(error, activationRoot, timing = buildUnavailableBrainServeHotPathTiming("serve-path timing was unavailable")) {
2410
2571
  const resolvedActivationRoot = path.resolve(activationRoot);
2411
2572
  try {
2412
2573
  const inspection = inspectActivationState(resolvedActivationRoot);
@@ -2419,14 +2580,15 @@ function classifyCompileFailure(error, activationRoot) {
2419
2580
  hardRequirementViolated: true,
2420
2581
  activationRoot: resolvedActivationRoot,
2421
2582
  error: `Learned-routing hotpath hard requirement violated for active pack ${active.packId} (routerIdentity=${active.routerIdentity ?? "null"}): ${failureReason}`,
2422
- brainContext: ""
2583
+ brainContext: "",
2584
+ timing
2423
2585
  };
2424
2586
  }
2425
2587
  }
2426
2588
  catch {
2427
- return failOpenCompileResult(error, resolvedActivationRoot);
2589
+ return failOpenCompileResult(error, resolvedActivationRoot, timing);
2428
2590
  }
2429
- return failOpenCompileResult(error, resolvedActivationRoot);
2591
+ return failOpenCompileResult(error, resolvedActivationRoot, timing);
2430
2592
  }
2431
2593
  function uniqueNotes(notes) {
2432
2594
  return [...new Set(notes.filter((note) => note.length > 0))];
@@ -2480,6 +2642,11 @@ function appendCompileServeRouteDecisionLog(input) {
2480
2642
  if (input.compileInput._suppressServeLog) {
2481
2643
  return;
2482
2644
  }
2645
+ if (!input.compileResult.ok && input.userMessage.length === 0) {
2646
+ // Invalid/partial invocation envelopes should fail open without paying for
2647
+ // serve-time route log writes on the live compile hot path.
2648
+ return;
2649
+ }
2483
2650
  const recordedAt = new Date().toISOString();
2484
2651
  const sessionId = normalizeServeRouteChannel(input.compileInput.sessionId) ?? `ext-compile-${Date.now()}`;
2485
2652
  const channel = normalizeServeRouteChannel(input.compileInput.channel) ?? "extension";
@@ -2751,6 +2918,7 @@ export function resolveActivePackForCompile(activationRoot) {
2751
2918
  };
2752
2919
  }
2753
2920
  export function compileRuntimeContext(input) {
2921
+ const totalStartedAtNs = monotonicClockNs();
2754
2922
  const fallbackActivationRoot = resolveActivationRootForFailure(input.activationRoot);
2755
2923
  let activationRoot = fallbackActivationRoot;
2756
2924
  let agentId = process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
@@ -2759,6 +2927,10 @@ export function compileRuntimeContext(input) {
2759
2927
  let userMessage = "";
2760
2928
  let maxContextChars;
2761
2929
  let mode = "heuristic";
2930
+ let routeSelectionStartedAtNs = null;
2931
+ let routeSelectionMs = null;
2932
+ let promptAssemblyStartedAtNs = null;
2933
+ let promptAssemblyMs = null;
2762
2934
  let result;
2763
2935
  try {
2764
2936
  activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
@@ -2773,7 +2945,11 @@ export function compileRuntimeContext(input) {
2773
2945
  mode = normalizeMode(input.mode);
2774
2946
  }
2775
2947
  catch (error) {
2776
- result = failOpenCompileResult(error, fallbackActivationRoot);
2948
+ result = failOpenCompileResult(error, fallbackActivationRoot, buildBrainServeHotPathTiming({
2949
+ totalMs: elapsedMsFrom(totalStartedAtNs),
2950
+ routeSelectionMs,
2951
+ promptAssemblyMs
2952
+ }));
2777
2953
  appendCompileServeRouteDecisionLog({
2778
2954
  compileInput: input,
2779
2955
  activationRoot: result.activationRoot,
@@ -2785,6 +2961,7 @@ export function compileRuntimeContext(input) {
2785
2961
  try {
2786
2962
  const target = resolveActivePackForCompile(activationRoot);
2787
2963
  const resolvedBudget = resolveCompileBudget(target, input);
2964
+ routeSelectionStartedAtNs = monotonicClockNs();
2788
2965
  const compile = compileRuntimeFromActivation(activationRoot, {
2789
2966
  contract: CONTRACT_IDS.runtimeCompile,
2790
2967
  agentId,
@@ -2798,6 +2975,7 @@ export function compileRuntimeContext(input) {
2798
2975
  }, {
2799
2976
  ...(selectionMode !== undefined ? { selectionMode } : {})
2800
2977
  });
2978
+ routeSelectionMs = elapsedMsFrom(routeSelectionStartedAtNs);
2801
2979
  const compileResponse = {
2802
2980
  ...compile.response,
2803
2981
  diagnostics: {
@@ -2805,6 +2983,9 @@ export function compileRuntimeContext(input) {
2805
2983
  notes: uniqueNotes([...compile.response.diagnostics.notes, ...resolvedBudget.notes, "OpenClaw remains the runtime owner"])
2806
2984
  }
2807
2985
  };
2986
+ promptAssemblyStartedAtNs = monotonicClockNs();
2987
+ const brainContext = formatPromptContext(compileResponse);
2988
+ promptAssemblyMs = elapsedMsFrom(promptAssemblyStartedAtNs);
2808
2989
  result = {
2809
2990
  ok: true,
2810
2991
  fallbackToStaticContext: false,
@@ -2813,11 +2994,26 @@ export function compileRuntimeContext(input) {
2813
2994
  activePackId: compile.target.packId,
2814
2995
  packRootDir: path.resolve(target.activePointer.packRootDir),
2815
2996
  compileResponse,
2816
- brainContext: formatPromptContext(compileResponse)
2997
+ brainContext,
2998
+ timing: buildBrainServeHotPathTiming({
2999
+ totalMs: elapsedMsFrom(totalStartedAtNs),
3000
+ routeSelectionMs,
3001
+ promptAssemblyMs
3002
+ })
2817
3003
  };
2818
3004
  }
2819
3005
  catch (error) {
2820
- result = classifyCompileFailure(error, activationRoot);
3006
+ if (routeSelectionStartedAtNs !== null && routeSelectionMs === null) {
3007
+ routeSelectionMs = elapsedMsFrom(routeSelectionStartedAtNs);
3008
+ }
3009
+ if (promptAssemblyStartedAtNs !== null && promptAssemblyMs === null) {
3010
+ promptAssemblyMs = elapsedMsFrom(promptAssemblyStartedAtNs);
3011
+ }
3012
+ result = classifyCompileFailure(error, activationRoot, buildBrainServeHotPathTiming({
3013
+ totalMs: elapsedMsFrom(totalStartedAtNs),
3014
+ routeSelectionMs,
3015
+ promptAssemblyMs
3016
+ }));
2821
3017
  }
2822
3018
  appendCompileServeRouteDecisionLog({
2823
3019
  compileInput: input,
@@ -3020,6 +3216,7 @@ function buildAttachCompileStatus(result, observability, activePackId) {
3020
3216
  activePackId,
3021
3217
  usedLearnedRouteFn: null,
3022
3218
  routerIdentity: observability?.learnedRouteFn.routerIdentity ?? null,
3219
+ selectionDigest: null,
3023
3220
  initMode: observability?.initHandoff.initMode ?? null,
3024
3221
  handoffState: observability?.initHandoff.handoffState ?? null,
3025
3222
  seedSources: observability?.initHandoff.seedSources ?? [],
@@ -3028,6 +3225,7 @@ function buildAttachCompileStatus(result, observability, activePackId) {
3028
3225
  hardRequirementViolated: result.hardRequirementViolated,
3029
3226
  usedLearnedRouteFn: null
3030
3227
  }),
3228
+ timing: result.timing,
3031
3229
  notes: [],
3032
3230
  error: result.error
3033
3231
  };
@@ -3047,12 +3245,14 @@ function buildAttachCompileStatus(result, observability, activePackId) {
3047
3245
  activePackId: result.activePackId,
3048
3246
  usedLearnedRouteFn: result.compileResponse.diagnostics.usedLearnedRouteFn,
3049
3247
  routerIdentity: result.compileResponse.diagnostics.routerIdentity,
3248
+ selectionDigest: result.compileResponse.diagnostics.selectionDigest,
3050
3249
  initMode: readDiagnosticNoteValue(notes, "init_mode=") ?? observability?.initHandoff.initMode ?? null,
3051
3250
  handoffState: readDiagnosticNoteValue(notes, "handoff_state=") ?? observability?.initHandoff.handoffState ?? null,
3052
3251
  seedSources: readDiagnosticNoteList(notes, "seed_sources=").length > 0
3053
3252
  ? readDiagnosticNoteList(notes, "seed_sources=")
3054
3253
  : observability?.initHandoff.seedSources ?? [],
3055
3254
  contextAttribution,
3255
+ timing: result.timing,
3056
3256
  notes,
3057
3257
  error: null
3058
3258
  };
@@ -3447,6 +3647,7 @@ function buildCompileInteractionEvent(input) {
3447
3647
  runtimeOwner: "openclaw",
3448
3648
  stream: input.sourceStream
3449
3649
  },
3650
+ semantic: buildInteractionSemanticMetadata("runtime_turn", "memory_compiled"),
3450
3651
  packId: input.compileResult.compileResponse.packId,
3451
3652
  principal: buildRuntimeSelfPrincipal(input.turn),
3452
3653
  attribution: input.attribution
@@ -3482,6 +3683,7 @@ function buildDeliveryInteractionEvent(input) {
3482
3683
  runtimeOwner: "openclaw",
3483
3684
  stream: input.sourceStream
3484
3685
  },
3686
+ semantic: buildInteractionSemanticMetadata("runtime_turn", "message_delivered"),
3485
3687
  attribution: input.attribution,
3486
3688
  ...(input.compileResult.ok ? { packId: input.compileResult.compileResponse.packId } : {}),
3487
3689
  ...(messageId !== undefined ? { messageId } : {})
@@ -3531,6 +3733,7 @@ function buildFeedbackEvents(input) {
3531
3733
  stream: input.sourceStream
3532
3734
  },
3533
3735
  content,
3736
+ semantic: buildFeedbackSemanticMetadata("runtime_turn", kind),
3534
3737
  attribution: input.attribution,
3535
3738
  ...(messageId !== undefined ? { messageId } : {}),
3536
3739
  ...(principal === undefined ? {} : { principal }),
@@ -3980,6 +4183,7 @@ function buildRecordedSessionSeedExport(trace) {
3980
4183
  runtimeOwner: "openclaw",
3981
4184
  stream: sourceStream
3982
4185
  },
4186
+ semantic: buildInteractionSemanticMetadata("recorded_session_seed", "operator_override"),
3983
4187
  messageId: `${cue.cueId}-seed-message`
3984
4188
  });
3985
4189
  sequence += 1;
@@ -3996,6 +4200,7 @@ function buildRecordedSessionSeedExport(trace) {
3996
4200
  stream: sourceStream
3997
4201
  },
3998
4202
  content: cue.content,
4203
+ semantic: buildFeedbackSemanticMetadata("recorded_session_seed", cue.kind ?? "teaching"),
3999
4204
  relatedInteractionId: interaction.eventId
4000
4205
  });
4001
4206
  sequence += 1;
@@ -5005,18 +5210,22 @@ function summarizeBrainStateWithoutObservability(active, activation) {
5005
5210
  detail: activation.detail
5006
5211
  };
5007
5212
  }
5008
- function summarizeGraphWithoutObservability(active, activation) {
5213
+ function summarizeGraphWithoutObservability(input, active, activation) {
5214
+ const latestMaterialization = summarizeLatestGraphMaterialization(input, active, null);
5009
5215
  return {
5010
5216
  available: false,
5011
5217
  runtimePlasticitySource: null,
5012
5218
  structuralOps: null,
5219
+ connectDiagnostics: null,
5013
5220
  changed: null,
5221
+ blockCount: null,
5014
5222
  operationsApplied: [],
5015
5223
  liveBlockCount: null,
5016
5224
  prunedBlockCount: null,
5017
5225
  prePruneBlockCount: null,
5018
5226
  strongestBlockId: null,
5019
5227
  operatorSummary: null,
5228
+ latestMaterialization,
5020
5229
  detail: active === null
5021
5230
  ? activation.detail
5022
5231
  : `active pack ${active.packId} is pinned, but graph observability is unavailable`
@@ -5077,19 +5286,59 @@ function summarizeBrainState(active, observability) {
5077
5286
  detail
5078
5287
  };
5079
5288
  }
5080
- function summarizeGraphObservability(active, observability) {
5289
+ function summarizeLatestGraphMaterialization(input, active, activeGraphEvolution) {
5290
+ const loadedTeacherSurface = loadTeacherSurfaceFromInput(input);
5291
+ const latestMaterialization = loadedTeacherSurface?.snapshot.learner.lastMaterialization ?? null;
5292
+ const latestGraph = latestMaterialization?.candidate.summary.graphEvolutionLog ?? null;
5293
+ if (latestGraph !== null) {
5294
+ const packId = latestMaterialization?.candidate.summary.packId ?? latestGraph.packId;
5295
+ return {
5296
+ known: true,
5297
+ packId,
5298
+ changed: latestGraph.structuralEvolutionSummary.changed,
5299
+ connectDiagnostics: latestGraph.connectDiagnostics === null ? null : { ...latestGraph.connectDiagnostics },
5300
+ operatorSummary: latestGraph.structuralEvolutionSummary.operatorSummary,
5301
+ detail: `latest known materialization is ${packId} from the teacher snapshot`
5302
+ };
5303
+ }
5304
+ if (active !== null && activeGraphEvolution !== null) {
5305
+ return {
5306
+ known: true,
5307
+ packId: active.packId,
5308
+ changed: activeGraphEvolution.structuralEvolutionSummary.changed,
5309
+ connectDiagnostics: activeGraphEvolution.connectDiagnostics === null ? null : { ...activeGraphEvolution.connectDiagnostics },
5310
+ operatorSummary: activeGraphEvolution.structuralEvolutionSummary.operatorSummary,
5311
+ detail: `latest known materialization is the active pack ${active.packId}`
5312
+ };
5313
+ }
5314
+ return {
5315
+ known: false,
5316
+ packId: null,
5317
+ changed: null,
5318
+ connectDiagnostics: null,
5319
+ operatorSummary: null,
5320
+ detail: active === null
5321
+ ? "no active pack or materialized learner snapshot is visible"
5322
+ : `active pack ${active.packId} is pinned, but no latest materialization snapshot is visible`
5323
+ };
5324
+ }
5325
+ function summarizeGraphObservability(input, active, observability) {
5326
+ const latestMaterialization = summarizeLatestGraphMaterialization(input, active, observability.graphEvolutionLog);
5081
5327
  if (active === null) {
5082
5328
  return {
5083
5329
  available: false,
5084
5330
  runtimePlasticitySource: null,
5085
5331
  structuralOps: null,
5332
+ connectDiagnostics: null,
5086
5333
  changed: null,
5334
+ blockCount: null,
5087
5335
  operationsApplied: [],
5088
5336
  liveBlockCount: null,
5089
5337
  prunedBlockCount: null,
5090
5338
  prePruneBlockCount: null,
5091
5339
  strongestBlockId: null,
5092
5340
  operatorSummary: null,
5341
+ latestMaterialization,
5093
5342
  detail: "no active pack is pinned, so there is no structural graph surface to inspect"
5094
5343
  };
5095
5344
  }
@@ -5099,13 +5348,16 @@ function summarizeGraphObservability(active, observability) {
5099
5348
  available: false,
5100
5349
  runtimePlasticitySource: observability.graphDynamics.runtimePlasticitySource,
5101
5350
  structuralOps: null,
5351
+ connectDiagnostics: null,
5102
5352
  changed: null,
5353
+ blockCount: null,
5103
5354
  operationsApplied: [],
5104
5355
  liveBlockCount: null,
5105
5356
  prunedBlockCount: null,
5106
5357
  prePruneBlockCount: null,
5107
5358
  strongestBlockId: null,
5108
5359
  operatorSummary: null,
5360
+ latestMaterialization,
5109
5361
  detail: "active pack is present, but the graph evolution log is missing from activation observability"
5110
5362
  };
5111
5363
  }
@@ -5113,13 +5365,16 @@ function summarizeGraphObservability(active, observability) {
5113
5365
  available: true,
5114
5366
  runtimePlasticitySource: observability.graphDynamics.runtimePlasticitySource,
5115
5367
  structuralOps: { ...graphEvolution.structuralOps },
5368
+ connectDiagnostics: graphEvolution.connectDiagnostics === null ? null : { ...graphEvolution.connectDiagnostics },
5116
5369
  changed: graphEvolution.structuralEvolutionSummary.changed,
5370
+ blockCount: graphEvolution.blockCount,
5117
5371
  operationsApplied: [...graphEvolution.structuralEvolutionSummary.operationsApplied],
5118
5372
  liveBlockCount: graphEvolution.structuralEvolutionSummary.liveBlockCount,
5119
5373
  prunedBlockCount: graphEvolution.structuralEvolutionSummary.prunedBlockCount,
5120
5374
  prePruneBlockCount: graphEvolution.structuralEvolutionSummary.prePruneBlockCount,
5121
5375
  strongestBlockId: graphEvolution.strongestBlockId,
5122
5376
  operatorSummary: graphEvolution.structuralEvolutionSummary.operatorSummary,
5377
+ latestMaterialization,
5123
5378
  detail: graphEvolution.structuralEvolutionSummary.changed
5124
5379
  ? graphEvolution.structuralEvolutionSummary.operatorSummary
5125
5380
  : "active pack graph is stable with no structural evolution beyond the current promoted artifact"
@@ -5136,6 +5391,7 @@ function summarizeServePath(compile) {
5136
5391
  usedLearnedRouteFn: null,
5137
5392
  routerIdentity: null,
5138
5393
  selectionMode: null,
5394
+ selectionDigest: null,
5139
5395
  refreshStatus: null,
5140
5396
  freshnessChecksum: null,
5141
5397
  requestedBudgetStrategy: null,
@@ -5151,6 +5407,7 @@ function summarizeServePath(compile) {
5151
5407
  usedLearnedRouteFn: null,
5152
5408
  unprobed: true
5153
5409
  }),
5410
+ timing: buildUnavailableBrainServeHotPathTiming("serve-path timing is unavailable because the hot path was not probed"),
5154
5411
  error: null
5155
5412
  };
5156
5413
  }
@@ -5163,6 +5420,7 @@ function summarizeServePath(compile) {
5163
5420
  usedLearnedRouteFn: compile.usedLearnedRouteFn,
5164
5421
  routerIdentity: compile.routerIdentity,
5165
5422
  selectionMode: null,
5423
+ selectionDigest: null,
5166
5424
  refreshStatus: null,
5167
5425
  freshnessChecksum: null,
5168
5426
  requestedBudgetStrategy: null,
@@ -5173,6 +5431,7 @@ function summarizeServePath(compile) {
5173
5431
  structuralBudgetPressures: null,
5174
5432
  structuralDecision,
5175
5433
  contextAttribution: compile.contextAttribution,
5434
+ timing: compile.timing,
5176
5435
  error: compile.error
5177
5436
  };
5178
5437
  }
@@ -5184,6 +5443,7 @@ function summarizeServePath(compile) {
5184
5443
  usedLearnedRouteFn: compile.usedLearnedRouteFn,
5185
5444
  routerIdentity: compile.routerIdentity,
5186
5445
  selectionMode: readDiagnosticNoteValue(compile.notes, "selection_mode="),
5446
+ selectionDigest: compile.selectionDigest,
5187
5447
  refreshStatus: readDiagnosticNoteValue(compile.notes, "router_refresh_status="),
5188
5448
  freshnessChecksum: readDiagnosticNoteValue(compile.notes, "router_freshness_checksum="),
5189
5449
  requestedBudgetStrategy: readDiagnosticNoteValue(compile.notes, "requested_budget_strategy="),
@@ -5195,6 +5455,7 @@ function summarizeServePath(compile) {
5195
5455
  structuralBudgetPressures: readDiagnosticNoteValue(compile.notes, "structural_budget_pressures="),
5196
5456
  structuralDecision,
5197
5457
  contextAttribution: compile.contextAttribution,
5458
+ timing: compile.timing,
5198
5459
  error: compile.error
5199
5460
  };
5200
5461
  }
@@ -5203,12 +5464,7 @@ function probeOperatorServePath(activationRoot, observability, activePackId) {
5203
5464
  const compile = compileInput === null ? null : buildAttachCompileStatus(compileRuntimeContext(compileInput), observability, activePackId);
5204
5465
  return summarizeServePath(compile);
5205
5466
  }
5206
- function loadOperatorEventExport(input) {
5207
- const eventExportPath = normalizeOptionalString(input.eventExportPath);
5208
- if (eventExportPath === undefined) {
5209
- return null;
5210
- }
5211
- const resolvedPath = path.resolve(eventExportPath);
5467
+ function loadOperatorEventExportFromPath(resolvedPath) {
5212
5468
  const stats = statSync(resolvedPath);
5213
5469
  if (stats.isDirectory()) {
5214
5470
  const bundle = loadRuntimeEventExportBundle(resolvedPath);
@@ -5231,6 +5487,44 @@ function loadOperatorEventExport(input) {
5231
5487
  exportedAt: null
5232
5488
  };
5233
5489
  }
5490
+ function resolveOperatorEventExportScanRoots(input) {
5491
+ const activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
5492
+ const teacherSurface = loadTeacherSurfaceFromInput(input);
5493
+ const teacherScanRoot = normalizeOptionalString(teacherSurface?.watchSnapshot?.scanRoot);
5494
+ return uniqueStringsInOrder([
5495
+ teacherScanRoot === undefined ? undefined : path.resolve(teacherScanRoot),
5496
+ path.join(activationRoot, "event-exports")
5497
+ ].filter((value) => value !== undefined));
5498
+ }
5499
+ function loadLatestOperatorEventExportFromScanRoots(scanRoots) {
5500
+ let latest = null;
5501
+ for (const scanRoot of scanRoots) {
5502
+ const discovered = discoverRuntimeEventExportBundles(scanRoot);
5503
+ const candidate = discovered.bundles[discovered.bundles.length - 1] ?? null;
5504
+ if (candidate === null) {
5505
+ continue;
5506
+ }
5507
+ if (latest === null || compareRuntimeEventExportScannerBundleCursor(latest.cursor, candidate.cursor) < 0) {
5508
+ latest = candidate;
5509
+ }
5510
+ }
5511
+ if (latest === null) {
5512
+ return null;
5513
+ }
5514
+ return {
5515
+ sourcePath: latest.descriptor.rootDir,
5516
+ sourceKind: "bundle_root",
5517
+ normalizedEventExport: latest.descriptor.normalizedEventExport,
5518
+ exportedAt: latest.descriptor.manifest.exportedAt
5519
+ };
5520
+ }
5521
+ function loadOperatorEventExport(input) {
5522
+ const eventExportPath = normalizeOptionalString(input.eventExportPath);
5523
+ if (eventExportPath !== undefined) {
5524
+ return loadOperatorEventExportFromPath(path.resolve(eventExportPath));
5525
+ }
5526
+ return loadLatestOperatorEventExportFromScanRoots(resolveOperatorEventExportScanRoots(input));
5527
+ }
5234
5528
  function summarizePrincipalItem(event) {
5235
5529
  if (event.principal === undefined) {
5236
5530
  return null;
@@ -5392,25 +5686,59 @@ function summarizeSupervision(input) {
5392
5686
  };
5393
5687
  }
5394
5688
  function loadTeacherSurfaceFromInput(input) {
5395
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
5396
- if (teacherSnapshotPath === undefined) {
5689
+ const teacherSnapshotPath = resolveOperatorTeacherSnapshotPath(input.activationRoot, normalizeOptionalString(input.teacherSnapshotPath) ?? null);
5690
+ if (teacherSnapshotPath === null) {
5397
5691
  return null;
5398
5692
  }
5399
5693
  return loadTeacherSurface(teacherSnapshotPath);
5400
5694
  }
5695
+ function summarizeTeacherLoopWatchState(input) {
5696
+ if (input.sourceKind !== "watch_snapshot" || input.watchSnapshot === null) {
5697
+ return {
5698
+ snapshotUpdatedAt: null,
5699
+ lastWatchHeartbeatAt: null,
5700
+ pollIntervalSeconds: null,
5701
+ watchState: input.sourceKind === "async_snapshot" ? "snapshot_only" : "not_visible"
5702
+ };
5703
+ }
5704
+ const lastWatchHeartbeatAt = input.watchSnapshot.snapshot.runtime?.lastHeartbeatAt ?? input.watchSnapshot.lastRunAt;
5705
+ const pollIntervalSeconds = input.watchSnapshot.pollIntervalSeconds;
5706
+ if (lastWatchHeartbeatAt === null) {
5707
+ return {
5708
+ snapshotUpdatedAt: input.watchSnapshot.updatedAt,
5709
+ lastWatchHeartbeatAt: null,
5710
+ pollIntervalSeconds,
5711
+ watchState: "snapshot_only"
5712
+ };
5713
+ }
5714
+ const lagMs = Date.parse(input.observedAt) - Date.parse(lastWatchHeartbeatAt);
5715
+ const staleAfterMs = pollIntervalSeconds * 2000 + 15_000;
5716
+ return {
5717
+ snapshotUpdatedAt: input.watchSnapshot.updatedAt,
5718
+ lastWatchHeartbeatAt,
5719
+ pollIntervalSeconds,
5720
+ watchState: Number.isFinite(lagMs) && lagMs >= 0 && lagMs <= staleAfterMs ? "watching" : "stale_snapshot"
5721
+ };
5722
+ }
5401
5723
  function summarizeTeacherLoop(input) {
5402
5724
  const loaded = loadTeacherSurfaceFromInput(input);
5403
- if (loaded === null && normalizeOptionalString(input.teacherSnapshotPath) === undefined) {
5725
+ const teacherSnapshotPath = resolveOperatorTeacherSnapshotPath(input.activationRoot, normalizeOptionalString(input.teacherSnapshotPath) ?? null);
5726
+ const unavailableFromMissing = buildUnavailableLastObservedDelta("no watch teacher snapshot is visible for the latest observed cycle");
5727
+ const unavailableFromAsync = buildUnavailableLastObservedDelta("raw async teacher snapshots do not record the last observed export/label/promotion delta");
5728
+ if (loaded === null && teacherSnapshotPath === null) {
5404
5729
  return {
5405
5730
  available: false,
5406
5731
  sourcePath: null,
5407
5732
  sourceKind: "missing",
5733
+ snapshotUpdatedAt: null,
5408
5734
  lastRunAt: null,
5409
5735
  lastNoOpReason: "unavailable",
5410
5736
  latestFreshness: "unavailable",
5411
5737
  startedAt: null,
5412
5738
  lastHeartbeatAt: null,
5413
5739
  lastScanAt: null,
5740
+ pollIntervalSeconds: null,
5741
+ watchState: "not_visible",
5414
5742
  lastProcessedAt: null,
5415
5743
  artifactCount: null,
5416
5744
  queueDepth: null,
@@ -5431,22 +5759,25 @@ function summarizeTeacherLoop(input) {
5431
5759
  failureDetail: null,
5432
5760
  lastAppliedMaterializationJobId: null,
5433
5761
  lastMaterializedPackId: null,
5762
+ lastObservedDelta: unavailableFromMissing,
5434
5763
  notes: [],
5435
5764
  detail: "no teacher snapshot path supplied"
5436
5765
  };
5437
5766
  }
5438
5767
  if (loaded === null) {
5439
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
5440
5768
  return {
5441
5769
  available: false,
5442
- sourcePath: teacherSnapshotPath === undefined ? null : path.resolve(teacherSnapshotPath),
5770
+ sourcePath: path.resolve(teacherSnapshotPath),
5443
5771
  sourceKind: "missing",
5772
+ snapshotUpdatedAt: null,
5444
5773
  lastRunAt: null,
5445
5774
  lastNoOpReason: "unavailable",
5446
5775
  latestFreshness: "unavailable",
5447
5776
  startedAt: null,
5448
5777
  lastHeartbeatAt: null,
5449
5778
  lastScanAt: null,
5779
+ pollIntervalSeconds: null,
5780
+ watchState: "not_visible",
5450
5781
  lastProcessedAt: null,
5451
5782
  artifactCount: null,
5452
5783
  queueDepth: null,
@@ -5467,22 +5798,31 @@ function summarizeTeacherLoop(input) {
5467
5798
  failureDetail: null,
5468
5799
  lastAppliedMaterializationJobId: null,
5469
5800
  lastMaterializedPackId: null,
5801
+ lastObservedDelta: unavailableFromMissing,
5470
5802
  notes: [],
5471
5803
  detail: "teacher snapshot could not be loaded"
5472
5804
  };
5473
5805
  }
5474
5806
  const snapshot = loaded.snapshot;
5475
5807
  const watchSnapshot = loaded.watchSnapshot;
5808
+ const watchState = summarizeTeacherLoopWatchState({
5809
+ observedAt: normalizeIsoTimestamp(input.updatedAt, "updatedAt", new Date().toISOString()),
5810
+ sourceKind: loaded.sourceKind,
5811
+ watchSnapshot
5812
+ });
5476
5813
  return {
5477
5814
  available: true,
5478
5815
  sourcePath: loaded.sourcePath,
5479
5816
  sourceKind: loaded.sourceKind,
5817
+ snapshotUpdatedAt: watchState.snapshotUpdatedAt,
5480
5818
  lastRunAt: watchSnapshot?.lastRunAt ?? snapshot.runtime?.lastHeartbeatAt ?? snapshot.diagnostics.lastProcessedAt ?? null,
5481
5819
  lastNoOpReason: snapshot.diagnostics.lastNoOpReason,
5482
5820
  latestFreshness: snapshot.diagnostics.latestFreshness,
5483
5821
  startedAt: snapshot.runtime?.startedAt ?? null,
5484
- lastHeartbeatAt: snapshot.runtime?.lastHeartbeatAt ?? null,
5822
+ lastHeartbeatAt: watchState.lastWatchHeartbeatAt ?? snapshot.runtime?.lastHeartbeatAt ?? null,
5485
5823
  lastScanAt: snapshot.runtime?.lastScanAt ?? null,
5824
+ pollIntervalSeconds: watchState.pollIntervalSeconds,
5825
+ watchState: watchState.watchState,
5486
5826
  lastProcessedAt: snapshot.diagnostics.lastProcessedAt,
5487
5827
  artifactCount: watchSnapshot?.teacher.artifactCount ?? snapshot.teacher.artifactCount,
5488
5828
  queueDepth: snapshot.queue.depth,
@@ -5506,12 +5846,95 @@ function summarizeTeacherLoop(input) {
5506
5846
  snapshot.learner.lastMaterialization?.jobId ??
5507
5847
  null,
5508
5848
  lastMaterializedPackId: snapshot.learner.lastMaterialization?.candidate.summary.packId ?? null,
5849
+ lastObservedDelta: loaded.sourceKind === "watch_snapshot" && watchSnapshot !== null
5850
+ ? cloneLastObservedDelta(watchSnapshot.lastObservedDelta)
5851
+ : unavailableFromAsync,
5509
5852
  notes: [...snapshot.diagnostics.notes],
5510
5853
  detail: loaded.sourceKind === "watch_snapshot"
5511
5854
  ? "canonical watch teacher snapshot loaded"
5512
5855
  : "raw async teacher snapshot loaded"
5513
5856
  };
5514
5857
  }
5858
+ function matchesActiveRouteFnLog(input) {
5859
+ if (input.activePackId !== null && input.entryPackId === input.activePackId) {
5860
+ return true;
5861
+ }
5862
+ if (input.routerChecksum !== null && input.entryRouterChecksum === input.routerChecksum) {
5863
+ return true;
5864
+ }
5865
+ return false;
5866
+ }
5867
+ function summarizeRouteFnFreshness(input) {
5868
+ if (input.activePackId === null) {
5869
+ return {
5870
+ available: false,
5871
+ activePackId: null,
5872
+ routerIdentity: input.learnedRouting.routerIdentity,
5873
+ routerChecksum: input.learnedRouting.routerChecksum,
5874
+ trainedAt: null,
5875
+ updatedAt: null,
5876
+ usedAt: null,
5877
+ lastDecisionAt: null,
5878
+ lastDecisionUsedLearnedRouteFn: null,
5879
+ detail: "no active pack is pinned, so there is no current route_fn to time"
5880
+ };
5881
+ }
5882
+ if (!input.learnedRouting.available) {
5883
+ return {
5884
+ available: false,
5885
+ activePackId: input.activePackId,
5886
+ routerIdentity: input.learnedRouting.routerIdentity,
5887
+ routerChecksum: input.learnedRouting.routerChecksum,
5888
+ trainedAt: input.learnedRouting.routerTrainedAt,
5889
+ updatedAt: null,
5890
+ usedAt: null,
5891
+ lastDecisionAt: null,
5892
+ lastDecisionUsedLearnedRouteFn: null,
5893
+ detail: input.learnedRouting.required
5894
+ ? `active pack ${input.activePackId} requires learned routing, but no route_fn artifact is available`
5895
+ : `active pack ${input.activePackId} does not require a learned route_fn`
5896
+ };
5897
+ }
5898
+ const updates = readLearningSpineLogEntries(input.activationRoot, "pgRouteUpdates");
5899
+ const decisions = readLearningSpineLogEntries(input.activationRoot, "serveTimeRouteDecisions");
5900
+ const updated = [...updates].reverse().find((entry) => matchesActiveRouteFnLog({
5901
+ activePackId: input.activePackId,
5902
+ routerChecksum: input.learnedRouting.routerChecksum,
5903
+ entryPackId: entry.nextPackId,
5904
+ entryRouterChecksum: entry.nextRouterChecksum
5905
+ }));
5906
+ const learnedUse = [...decisions].reverse().find((entry) => entry.usedLearnedRouteFn === true &&
5907
+ matchesActiveRouteFnLog({
5908
+ activePackId: input.activePackId,
5909
+ routerChecksum: input.learnedRouting.routerChecksum,
5910
+ entryPackId: entry.activePackId,
5911
+ entryRouterChecksum: entry.activePackRouterChecksum
5912
+ }));
5913
+ const lastDecision = [...decisions].reverse().find((entry) => matchesActiveRouteFnLog({
5914
+ activePackId: input.activePackId,
5915
+ routerChecksum: input.learnedRouting.routerChecksum,
5916
+ entryPackId: entry.activePackId,
5917
+ entryRouterChecksum: entry.activePackRouterChecksum
5918
+ }));
5919
+ return {
5920
+ available: true,
5921
+ activePackId: input.activePackId,
5922
+ routerIdentity: input.learnedRouting.routerIdentity,
5923
+ routerChecksum: input.learnedRouting.routerChecksum,
5924
+ trainedAt: input.learnedRouting.routerTrainedAt,
5925
+ updatedAt: updated?.recordedAt ?? null,
5926
+ usedAt: learnedUse?.recordedAt ?? null,
5927
+ lastDecisionAt: lastDecision?.recordedAt ?? null,
5928
+ lastDecisionUsedLearnedRouteFn: lastDecision?.usedLearnedRouteFn ?? null,
5929
+ detail: learnedUse !== undefined
5930
+ ? `active route_fn last served a learned turn at ${learnedUse.recordedAt}`
5931
+ : updated !== undefined
5932
+ ? `active route_fn was updated at ${updated.recordedAt}, but no learned serve decision for the current pack is visible yet`
5933
+ : input.learnedRouting.routerTrainedAt !== null
5934
+ ? `active route_fn was trained at ${input.learnedRouting.routerTrainedAt}, but no matching update or serve-use log is visible yet`
5935
+ : `active pack ${input.activePackId} exposes a route_fn, but the train/update timestamps are not visible`
5936
+ };
5937
+ }
5515
5938
  function summarizeLearningBacklogState(plan, principalLagStatus) {
5516
5939
  if (!plan.bootstrapped && plan.pending.total === 0) {
5517
5940
  return "awaiting_first_export";
@@ -5567,7 +5990,8 @@ function summarizeAlwaysOnLearning(input, active) {
5567
5990
  status: "unavailable"
5568
5991
  };
5569
5992
  const loadedTeacherSurface = loadTeacherSurfaceFromInput(input);
5570
- if (loadedTeacherSurface === null && normalizeOptionalString(input.teacherSnapshotPath) === undefined) {
5993
+ const teacherSnapshotPath = resolveOperatorTeacherSnapshotPath(input.activationRoot, normalizeOptionalString(input.teacherSnapshotPath) ?? null);
5994
+ if (loadedTeacherSurface === null && teacherSnapshotPath === null) {
5571
5995
  return {
5572
5996
  available: false,
5573
5997
  sourcePath: null,
@@ -5600,10 +6024,9 @@ function summarizeAlwaysOnLearning(input, active) {
5600
6024
  };
5601
6025
  }
5602
6026
  if (loadedTeacherSurface === null) {
5603
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
5604
6027
  return {
5605
6028
  available: false,
5606
- sourcePath: teacherSnapshotPath === undefined ? null : path.resolve(teacherSnapshotPath),
6029
+ sourcePath: path.resolve(teacherSnapshotPath),
5607
6030
  bootstrapped: null,
5608
6031
  mode: "unavailable",
5609
6032
  nextPriorityLane: "unavailable",
@@ -5707,6 +6130,18 @@ function buildOperatorFindings(report) {
5707
6130
  const push = (severity, code, summary, detail) => {
5708
6131
  findings.push({ severity, code, summary, detail });
5709
6132
  };
6133
+ if (report.hook.desynced) {
6134
+ push("fail", "hook_desynced", "profile hook is blocked by OpenClaw config desync", report.hook.detail);
6135
+ }
6136
+ else if (report.attachmentTruth.state === "not_attached") {
6137
+ push("fail", "current_profile_not_attached", "current profile is not attached", report.attachmentTruth.detail);
6138
+ }
6139
+ else if (report.attachmentTruth.state === "unknown") {
6140
+ push("warn", "attachment_scope_partial", "current-profile attachment is not self-proven", report.attachmentTruth.detail);
6141
+ }
6142
+ else {
6143
+ push("pass", "current_profile_attached", "current profile hook is present", report.attachmentTruth.detail);
6144
+ }
5710
6145
  if (report.activation.state === "broken_install") {
5711
6146
  push("fail", "activation_broken_install", "activation root is broken", report.activation.detail);
5712
6147
  }
@@ -5858,6 +6293,105 @@ function summarizeCurrentProfileLastLearningUpdateAt(activationRoot, learning, t
5858
6293
  const updates = readLearningSpineLogEntries(activationRoot, "pgRouteUpdates");
5859
6294
  return updates.at(-1)?.recordedAt ?? teacherLoop.lastRunAt ?? learning.lastMaterializedAt ?? null;
5860
6295
  }
6296
+ function didCurrentProfileFirstExportOccur(report) {
6297
+ if ((report.active?.eventRange.count ?? 0) > 0) {
6298
+ return true;
6299
+ }
6300
+ if (report.supervision.exportedAt !== null) {
6301
+ return true;
6302
+ }
6303
+ if (report.learning.available && (report.learning.learnedRange?.count ?? 0) > 0) {
6304
+ return true;
6305
+ }
6306
+ if (!report.teacherLoop.available) {
6307
+ return false;
6308
+ }
6309
+ return ((report.teacherLoop.replayedBundleCount ?? 0) > 0 ||
6310
+ (report.teacherLoop.exportedBundleCount ?? 0) > 0 ||
6311
+ report.teacherLoop.lastProcessedAt !== null ||
6312
+ report.teacherLoop.lastMaterializedPackId !== null);
6313
+ }
6314
+ function summarizeCurrentProfilePassiveLearning(report, activePackId) {
6315
+ const firstExportOccurred = didCurrentProfileFirstExportOccur(report);
6316
+ const exportState = !firstExportOccurred
6317
+ ? "awaiting_first_export"
6318
+ : report.supervision.exportedAt !== null
6319
+ ? "latest_export_visible"
6320
+ : "history_only";
6321
+ const backlogState = report.learning.available && report.learning.backlogState !== "unavailable"
6322
+ ? report.learning.backlogState
6323
+ : "unknown";
6324
+ const detail = !firstExportOccurred
6325
+ ? report.teacherLoop.watchState === "watching"
6326
+ ? "watch heartbeat is fresh, but this activation root has not observed its first export yet"
6327
+ : "this activation root is still waiting for the first export before passive learning can advance"
6328
+ : backlogState === "unknown"
6329
+ ? "first export is proven, but passive backlog state is not visible from the current local artifacts"
6330
+ : report.teacherLoop.watchState === "watching"
6331
+ ? `watch heartbeat is fresh; passive backlog is ${backlogState} with live=${report.learning.pendingLive ?? 0} and backfill=${report.learning.pendingBackfill ?? 0}`
6332
+ : report.teacherLoop.watchState === "stale_snapshot"
6333
+ ? `last saved watch snapshot is stale; latest known passive backlog is ${backlogState}`
6334
+ : report.teacherLoop.watchState === "snapshot_only"
6335
+ ? `passive backlog is visible from the last saved snapshot: ${backlogState}`
6336
+ : `passive backlog is visible from the last known learner state: ${backlogState}`;
6337
+ return {
6338
+ learnerRunning: report.teacherLoop.watchState === "watching",
6339
+ firstExportOccurred,
6340
+ watchState: report.teacherLoop.watchState,
6341
+ exportState,
6342
+ backlogState,
6343
+ pendingLive: report.learning.available ? report.learning.pendingLive : null,
6344
+ pendingBackfill: report.learning.available ? report.learning.pendingBackfill : null,
6345
+ lastWatchHeartbeatAt: report.teacherLoop.lastHeartbeatAt,
6346
+ watchIntervalSeconds: report.teacherLoop.pollIntervalSeconds,
6347
+ lastExportAt: report.supervision.exportedAt,
6348
+ lastPromotionAt: report.promotion.lastPromotion.at,
6349
+ currentServingPackId: activePackId,
6350
+ lastMaterializedPackId: report.learning.lastMaterializedPackId ?? report.teacherLoop.lastMaterializedPackId,
6351
+ lastObservedDelta: cloneLastObservedDelta(report.teacherLoop.lastObservedDelta),
6352
+ detail
6353
+ };
6354
+ }
6355
+ function summarizeOperatorHook(report) {
6356
+ return summarizeOpenClawBrainHookLoad(inspectOpenClawBrainHookStatus(report.openclawHome ?? null), report.servePath.state === "serving_active_pack");
6357
+ }
6358
+ function summarizeOperatorAttachmentTruth(report) {
6359
+ const watchOnly = report.active !== null || report.teacherLoop.watchState !== "not_visible";
6360
+ if (report.hook.scope === "exact_openclaw_home") {
6361
+ if (report.hook.installState === "not_installed") {
6362
+ return {
6363
+ state: "not_attached",
6364
+ proofState: "self_proving",
6365
+ watchOnly,
6366
+ activationRoot: null,
6367
+ servingSlot: "none",
6368
+ detail: watchOnly
6369
+ ? "the selected OpenClaw home has no OpenClawBrain hook, even though this activation root still shows serve/watch activity"
6370
+ : "the selected OpenClaw home has no OpenClawBrain hook"
6371
+ };
6372
+ }
6373
+ return {
6374
+ state: "attached",
6375
+ proofState: "self_proving",
6376
+ watchOnly: false,
6377
+ activationRoot: report.activationRoot,
6378
+ servingSlot: report.active === null ? "none" : "active",
6379
+ detail: report.hook.installState === "blocked_by_allowlist"
6380
+ ? "current profile hook files exist, but OpenClaw will not load them until the plugin allowlist/config desync is repaired"
6381
+ : "current profile hook is present on the selected OpenClaw home"
6382
+ };
6383
+ }
6384
+ return {
6385
+ state: "unknown",
6386
+ proofState: "activation_root_only",
6387
+ watchOnly,
6388
+ activationRoot: watchOnly ? report.activationRoot : null,
6389
+ servingSlot: report.active === null ? "none" : "active",
6390
+ detail: watchOnly
6391
+ ? "activation-root-only status can see this root serving and/or being watched, but it does not prove that the current profile is attached"
6392
+ : "activation-root-only status cannot prove whether the current profile is attached"
6393
+ };
6394
+ }
5861
6395
  function summarizeCurrentProfileBrainSummary(input) {
5862
6396
  const packId = input.activePackId ?? "unknown";
5863
6397
  if (input.activationState === "broken_install") {
@@ -5866,9 +6400,17 @@ function summarizeCurrentProfileBrainSummary(input) {
5866
6400
  if (input.activationState === "stale_incomplete") {
5867
6401
  return "Brain activation has retained stale/incomplete state and no serving active pack.";
5868
6402
  }
5869
- if (!input.attached) {
6403
+ if (input.attachmentState === "not_attached") {
5870
6404
  return "Brain is not attached to the current Profile.";
5871
6405
  }
6406
+ if (input.hookDesynced) {
6407
+ return "Brain hook files exist, but OpenClaw config currently blocks them from loading.";
6408
+ }
6409
+ if (input.attachmentState === "unknown") {
6410
+ return input.watchOnly
6411
+ ? "This activation root is visible, but current-profile attachment is only watch/serve scoped and not self-proven."
6412
+ : "Current-profile attachment is unknown because this read is activation-root-only.";
6413
+ }
5872
6414
  if (input.serveState === "fail_open_static_context") {
5873
6415
  return "Brain is attached but would fail open to static context.";
5874
6416
  }
@@ -5887,19 +6429,98 @@ function summarizeCurrentProfileBrainSummary(input) {
5887
6429
  return "Brain is attached but has not been compile-probed yet.";
5888
6430
  }
5889
6431
  function summarizeCurrentProfileBrainStatusLevel(input) {
5890
- if (!input.attached) {
6432
+ if (input.hookDesynced || input.attachmentState === "not_attached") {
5891
6433
  return "fail";
5892
6434
  }
5893
6435
  if (input.serveState === "fail_open_static_context" || input.serveState === "hard_fail") {
5894
6436
  return "fail";
5895
6437
  }
5896
- if (input.awaitingFirstExport || input.serveState === "unprobed") {
6438
+ if (input.attachmentState === "unknown" || input.awaitingFirstExport || input.serveState === "unprobed") {
5897
6439
  return "warn";
5898
6440
  }
5899
6441
  return input.routeFreshness === "updated" ? "ok" : "warn";
5900
6442
  }
6443
+ function buildCurrentProfileTurnAttributionFromReport(report, policyMode, profileId) {
6444
+ if (report.servePath.state === "unprobed" || report.attachmentTruth.state !== "attached") {
6445
+ return null;
6446
+ }
6447
+ const brainStatus = report.servePath.state;
6448
+ const sessionId = "current-profile-status-probe";
6449
+ const channel = "operator_status";
6450
+ const createdAt = report.generatedAt;
6451
+ const sourceStream = "openclaw/operator/status";
6452
+ const packId = brainStatus === "serving_active_pack"
6453
+ ? report.servePath.activePackId ?? report.brain.activePackId ?? report.active?.packId ?? null
6454
+ : null;
6455
+ const routerIdentity = brainStatus === "serving_active_pack"
6456
+ ? report.servePath.routerIdentity ?? report.learnedRouting.routerIdentity ?? report.active?.routerIdentity ?? null
6457
+ : null;
6458
+ const usedLearnedRouteFn = brainStatus === "serving_active_pack" ? report.servePath.usedLearnedRouteFn : null;
6459
+ const selectionMode = brainStatus === "serving_active_pack" ? report.servePath.selectionMode : null;
6460
+ const selectionDigest = brainStatus === "serving_active_pack" ? report.servePath.selectionDigest : null;
6461
+ const contextAttribution = report.servePath.contextAttribution;
6462
+ const probeTurn = {
6463
+ sessionId,
6464
+ channel,
6465
+ sourceStream,
6466
+ userMessage: DEFAULT_ATTACH_STATUS_MESSAGE,
6467
+ createdAt,
6468
+ runtimeHints: [...DEFAULT_ATTACH_STATUS_RUNTIME_HINTS],
6469
+ profileSelector: "current_profile",
6470
+ profileId,
6471
+ brainAttachmentPolicy: policyMode
6472
+ };
6473
+ return {
6474
+ contract: CONTRACT_IDS.profileTurnAttribution,
6475
+ hostRuntimeOwner: "openclaw",
6476
+ profileSelector: "current_profile",
6477
+ profileId,
6478
+ brainAttachmentPolicy: policyMode,
6479
+ brainStatus,
6480
+ sessionId,
6481
+ channel,
6482
+ interactionEventId: deterministicEventId({
6483
+ source: sourceStream,
6484
+ activationRoot: report.activationRoot,
6485
+ createdAt,
6486
+ brainStatus,
6487
+ packId,
6488
+ routerIdentity,
6489
+ selectionDigest
6490
+ }),
6491
+ createdAt,
6492
+ packId,
6493
+ routerIdentity,
6494
+ usedLearnedRouteFn,
6495
+ selectionMode,
6496
+ selectionTiers: contextAttribution.selectionTiers,
6497
+ selectionDigest,
6498
+ contextFingerprint: buildRuntimeContextFingerprint({
6499
+ turn: probeTurn,
6500
+ sourceStream,
6501
+ profileSelector: "current_profile",
6502
+ brainAttachmentPolicy: policyMode,
6503
+ brainStatus,
6504
+ activePackId: packId,
6505
+ usedLearnedRouteFn,
6506
+ routerIdentity,
6507
+ selectionDigest
6508
+ }),
6509
+ selectedContextCount: contextAttribution.selectedContextCount,
6510
+ stableKernelBlockCount: contextAttribution.stableKernelBlockCount,
6511
+ brainCompiledBlockCount: contextAttribution.brainCompiledBlockCount,
6512
+ stableKernelSources: [...contextAttribution.stableKernelSources],
6513
+ brainCompiledSources: [...contextAttribution.brainCompiledSources],
6514
+ contextEvidence: contextAttribution.evidence === "unprobed" ? "stable_kernel_only" : contextAttribution.evidence,
6515
+ detail: brainStatus === "serving_active_pack"
6516
+ ? "current profile status probe compiled from the active pack with explicit route-function and block-source attribution"
6517
+ : brainStatus === "hard_fail"
6518
+ ? "current profile status probe hit a learned-route hard fail before serving context could compile"
6519
+ : "current profile status probe fail-opened to static context because no serving pack was available"
6520
+ };
6521
+ }
5901
6522
  function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId) {
5902
- const attached = report.active !== null;
6523
+ const attachmentState = report.attachmentTruth.state;
5903
6524
  const awaitingFirstExport = isAwaitingFirstExportSlot(report.active);
5904
6525
  const activationState = report.activation.state;
5905
6526
  const routerIdentity = report.servePath.routerIdentity ?? report.learnedRouting.routerIdentity ?? report.active?.routerIdentity ?? null;
@@ -5907,8 +6528,10 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5907
6528
  ? report.servePath.refreshStatus
5908
6529
  : "unknown";
5909
6530
  const activePackId = report.brain.activePackId ?? report.servePath.activePackId ?? report.active?.packId ?? null;
6531
+ const passiveLearning = summarizeCurrentProfilePassiveLearning(report, activePackId);
5910
6532
  const status = summarizeCurrentProfileBrainStatusLevel({
5911
- attached,
6533
+ attachmentState,
6534
+ hookDesynced: report.hook.desynced,
5912
6535
  serveState: report.servePath.state,
5913
6536
  routeFreshness,
5914
6537
  awaitingFirstExport
@@ -5925,13 +6548,15 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5925
6548
  noun: "Profile",
5926
6549
  selector: "current_profile",
5927
6550
  profileId,
5928
- detail: attached
5929
- ? "The Host resolves the current Profile through the active Attachment boundary only."
5930
- : "The current Profile has no active Brain attachment visible at the Host boundary."
6551
+ detail: attachmentState === "attached"
6552
+ ? "The Host resolves the current Profile through the selected OpenClaw home and its active Attachment boundary."
6553
+ : attachmentState === "not_attached"
6554
+ ? "The selected OpenClaw home has no OpenClawBrain hook for the current Profile."
6555
+ : "This read is activation-root-only, so current-profile attachment is not self-proven."
5931
6556
  },
5932
6557
  brain: {
5933
6558
  noun: "Brain",
5934
- activationRoot: attached ? report.activationRoot : null,
6559
+ activationRoot: attachmentState === "not_attached" ? null : report.activationRoot,
5935
6560
  logRoot: summarizeCurrentProfileLogRoot(report.activationRoot),
5936
6561
  activePackId,
5937
6562
  initMode: report.learnedRouting.initMode,
@@ -5944,7 +6569,9 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5944
6569
  lastPromotionAt: report.promotion.lastPromotion.at,
5945
6570
  summary: summarizeCurrentProfileBrainSummary({
5946
6571
  activationState,
5947
- attached,
6572
+ attachmentState,
6573
+ watchOnly: report.attachmentTruth.watchOnly,
6574
+ hookDesynced: report.hook.desynced,
5948
6575
  serveState: report.servePath.state,
5949
6576
  brainState: report.brain.state,
5950
6577
  awaitingFirstExport,
@@ -5955,29 +6582,30 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5955
6582
  ? report.activation.detail
5956
6583
  : report.brain.detail
5957
6584
  },
5958
- attachment: attached
5959
- ? {
5960
- noun: "Attachment",
5961
- state: "attached",
5962
- activationRoot: report.activationRoot,
5963
- servingSlot: "active",
5964
- policyMode,
5965
- policy: buildCurrentProfileAttachmentPolicy(policyMode),
5966
- detail: policyMode === "shared"
5967
- ? "current profile is attached to a shared OpenClawBrain activation boundary"
5968
- : policyMode === "dedicated"
5969
- ? "current profile is attached to a dedicated OpenClawBrain activation boundary"
5970
- : "current profile is attached to an OpenClawBrain activation boundary, but shared-vs-dedicated policy has not been declared"
5971
- }
5972
- : {
5973
- noun: "Attachment",
5974
- state: "not_attached",
5975
- activationRoot: null,
5976
- servingSlot: "none",
5977
- policyMode,
5978
- policy: buildCurrentProfileAttachmentPolicy(policyMode),
5979
- detail: "current profile is not attached to an OpenClawBrain activation boundary"
5980
- },
6585
+ hook: {
6586
+ noun: "Hook",
6587
+ scope: report.hook.scope,
6588
+ openclawHome: report.hook.openclawHome,
6589
+ hookPath: report.hook.hookPath,
6590
+ runtimeGuardPath: report.hook.runtimeGuardPath,
6591
+ manifestPath: report.hook.manifestPath,
6592
+ installState: report.hook.installState,
6593
+ loadability: report.hook.loadability,
6594
+ loadProof: report.hook.loadProof,
6595
+ desynced: report.hook.desynced,
6596
+ detail: report.hook.detail
6597
+ },
6598
+ attachment: {
6599
+ noun: "Attachment",
6600
+ state: attachmentState,
6601
+ activationRoot: report.attachmentTruth.activationRoot,
6602
+ servingSlot: report.attachmentTruth.servingSlot,
6603
+ policyMode,
6604
+ policy: buildCurrentProfileAttachmentPolicy(policyMode),
6605
+ proofState: report.attachmentTruth.proofState,
6606
+ watchOnly: report.attachmentTruth.watchOnly,
6607
+ detail: report.attachmentTruth.detail
6608
+ },
5981
6609
  brainStatus: {
5982
6610
  status,
5983
6611
  brainState: report.brain.state,
@@ -5987,25 +6615,33 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5987
6615
  failOpen: report.servePath.fallbackToStaticContext,
5988
6616
  awaitingFirstExport,
5989
6617
  structuralDecision: report.servePath.structuralDecision,
6618
+ timing: report.servePath.timing,
5990
6619
  detail: activationState === "broken_install"
5991
6620
  ? `current profile activation is broken: ${report.activation.detail}`
5992
- : activationState === "stale_incomplete"
5993
- ? `current profile activation is stale/incomplete: ${report.activation.detail}`
5994
- : activationState === "detached"
5995
- ? "current profile has no attached active pack at the activation boundary"
5996
- : report.servePath.state === "serving_active_pack"
5997
- ? awaitingFirstExport
5998
- ? `current profile is serving seed-state pack ${activePackId ?? "unknown"} while awaiting the first exported turn`
5999
- : report.brain.state === "pg_promoted_pack_authoritative"
6000
- ? `current profile is serving promoted pack ${activePackId ?? "unknown"}; serve-visible change came from activation promotion, not hot-path mutation`
6001
- : `current profile is serving active pack ${activePackId ?? "unknown"}; learned routing is active, but authority is still seed-state`
6002
- : report.servePath.state === "fail_open_static_context"
6003
- ? "current profile would fail open to static context because no serving pack is available"
6004
- : report.servePath.state === "hard_fail"
6005
- ? "current profile cannot serve because the learned-route or activation requirement hard-failed"
6006
- : "current profile serve state has not been compile-probed yet"
6621
+ : report.hook.desynced
6622
+ ? `current profile hook is blocked: ${report.hook.detail}`
6623
+ : activationState === "stale_incomplete"
6624
+ ? `current profile activation is stale/incomplete: ${report.activation.detail}`
6625
+ : attachmentState === "not_attached"
6626
+ ? "current profile is not attached to this Brain activation root"
6627
+ : attachmentState === "unknown"
6628
+ ? "current profile attachment is not self-proven from this activation-root-only read"
6629
+ : activationState === "detached"
6630
+ ? "current profile has no attached active pack at the activation boundary"
6631
+ : report.servePath.state === "serving_active_pack"
6632
+ ? awaitingFirstExport
6633
+ ? `current profile is serving seed-state pack ${activePackId ?? "unknown"} while awaiting the first exported turn`
6634
+ : report.brain.state === "pg_promoted_pack_authoritative"
6635
+ ? `current profile is serving promoted pack ${activePackId ?? "unknown"}; serve-visible change came from activation promotion, not hot-path mutation`
6636
+ : `current profile is serving active pack ${activePackId ?? "unknown"}; learned routing is active, but authority is still seed-state`
6637
+ : report.servePath.state === "fail_open_static_context"
6638
+ ? "current profile would fail open to static context because no serving pack is available"
6639
+ : report.servePath.state === "hard_fail"
6640
+ ? "current profile cannot serve because the learned-route or activation requirement hard-failed"
6641
+ : "current profile serve state has not been compile-probed yet"
6007
6642
  },
6008
- currentTurnAttribution: null
6643
+ passiveLearning,
6644
+ currentTurnAttribution: buildCurrentProfileTurnAttributionFromReport(report, policyMode, profileId)
6009
6645
  };
6010
6646
  }
6011
6647
  export function buildOperatorSurfaceReport(input) {
@@ -6049,6 +6685,46 @@ export function buildOperatorSurfaceReport(input) {
6049
6685
  learningPath: buildMissingLearningPathSummary(`activation observability is unavailable: ${inspectionError}`)
6050
6686
  };
6051
6687
  const servePath = probeOperatorServePath(activationRoot, observability, active?.packId ?? null);
6688
+ const learnedRouting = observability === null
6689
+ ? summarizeLearnedRoutingWithoutObservability(active)
6690
+ : {
6691
+ required: observability.learnedRouteFn.required,
6692
+ available: observability.learnedRouteFn.available,
6693
+ routerIdentity: observability.learnedRouteFn.routerIdentity,
6694
+ routeFnVersion: observability.learnedRouteFn.routeFnVersion,
6695
+ trainingMethod: observability.learnedRouteFn.trainingMethod,
6696
+ routerTrainedAt: observability.learnedRouteFn.routerTrainedAt,
6697
+ objective: observability.learnedRouteFn.objective,
6698
+ pgProfile: observability.learnedRouteFn.pgProfile,
6699
+ routerChecksum: observability.learnedRouteFn.routerChecksum,
6700
+ objectiveChecksum: observability.learnedRouteFn.objectiveChecksum,
6701
+ updateMechanism: observability.learnedRouteFn.updateMechanism,
6702
+ updateVersion: observability.learnedRouteFn.updateVersion,
6703
+ updateCount: observability.learnedRouteFn.updateCount,
6704
+ supervisionCount: observability.learnedRouteFn.supervisionCount,
6705
+ collectedLabelsTotal: observability.learnedRouteFn.collectedLabels?.total ?? null,
6706
+ freshnessChecksum: observability.learnedRouteFn.freshnessChecksum,
6707
+ handoffState: observability.initHandoff.handoffState,
6708
+ initMode: observability.initHandoff.initMode,
6709
+ seedStateVisible: observability.initHandoff.seedStateVisible
6710
+ };
6711
+ const routeFn = summarizeRouteFnFreshness({
6712
+ activationRoot,
6713
+ activePackId: active?.packId ?? null,
6714
+ learnedRouting
6715
+ });
6716
+ const teacherLoop = summarizeTeacherLoop(input);
6717
+ const hook = summarizeOperatorHook({
6718
+ activationRoot,
6719
+ openclawHome: normalizeOptionalString(input.openclawHome) ?? null,
6720
+ servePath
6721
+ });
6722
+ const attachmentTruth = summarizeOperatorAttachmentTruth({
6723
+ activationRoot,
6724
+ active,
6725
+ teacherLoop,
6726
+ hook
6727
+ });
6052
6728
  const reportBase = {
6053
6729
  generatedAt: updatedAt,
6054
6730
  activationRoot,
@@ -6061,32 +6737,12 @@ export function buildOperatorSurfaceReport(input) {
6061
6737
  candidateAheadBy: summarizeCandidateAheadBy(observability?.promotionFreshness.candidateAheadBy ?? null)
6062
6738
  },
6063
6739
  brain: observability === null ? summarizeBrainStateWithoutObservability(active, activation) : summarizeBrainState(active, observability),
6064
- graph: observability === null ? summarizeGraphWithoutObservability(active, activation) : summarizeGraphObservability(active, observability),
6740
+ graph: observability === null
6741
+ ? summarizeGraphWithoutObservability(input, active, activation)
6742
+ : summarizeGraphObservability(input, active, observability),
6065
6743
  labelFlow: activeObservability.labelFlow,
6066
6744
  learningPath: activeObservability.learningPath,
6067
- learnedRouting: observability === null
6068
- ? summarizeLearnedRoutingWithoutObservability(active)
6069
- : {
6070
- required: observability.learnedRouteFn.required,
6071
- available: observability.learnedRouteFn.available,
6072
- routerIdentity: observability.learnedRouteFn.routerIdentity,
6073
- routeFnVersion: observability.learnedRouteFn.routeFnVersion,
6074
- trainingMethod: observability.learnedRouteFn.trainingMethod,
6075
- routerTrainedAt: observability.learnedRouteFn.routerTrainedAt,
6076
- objective: observability.learnedRouteFn.objective,
6077
- pgProfile: observability.learnedRouteFn.pgProfile,
6078
- routerChecksum: observability.learnedRouteFn.routerChecksum,
6079
- objectiveChecksum: observability.learnedRouteFn.objectiveChecksum,
6080
- updateMechanism: observability.learnedRouteFn.updateMechanism,
6081
- updateVersion: observability.learnedRouteFn.updateVersion,
6082
- updateCount: observability.learnedRouteFn.updateCount,
6083
- supervisionCount: observability.learnedRouteFn.supervisionCount,
6084
- collectedLabelsTotal: observability.learnedRouteFn.collectedLabels?.total ?? null,
6085
- freshnessChecksum: observability.learnedRouteFn.freshnessChecksum,
6086
- handoffState: observability.initHandoff.handoffState,
6087
- initMode: observability.initHandoff.initMode,
6088
- seedStateVisible: observability.initHandoff.seedStateVisible
6089
- },
6745
+ learnedRouting,
6090
6746
  servePath,
6091
6747
  promotion: {
6092
6748
  allowed: inspection?.promotion.allowed ?? false,
@@ -6109,7 +6765,10 @@ export function buildOperatorSurfaceReport(input) {
6109
6765
  },
6110
6766
  supervision: summarizeSupervision(input),
6111
6767
  learning: summarizeAlwaysOnLearning(input, active),
6112
- teacherLoop: summarizeTeacherLoop(input),
6768
+ teacherLoop,
6769
+ routeFn,
6770
+ hook,
6771
+ attachmentTruth,
6113
6772
  principal: summarizePrincipalObservability(input, active),
6114
6773
  manyProfile: summarizeManyProfileSupport(brainAttachmentPolicy)
6115
6774
  };