@openclawbrain/openclaw 0.2.3 → 0.3.0

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 (36) hide show
  1. package/dist/extension/index.d.ts +1 -0
  2. package/dist/extension/index.js +73 -0
  3. package/dist/extension/index.js.map +1 -0
  4. package/dist/extension/runtime-guard.d.ts +61 -0
  5. package/dist/extension/runtime-guard.js +230 -0
  6. package/dist/extension/runtime-guard.js.map +1 -0
  7. package/dist/src/cli.d.ts +8 -4
  8. package/dist/src/cli.js +501 -154
  9. package/dist/src/cli.js.map +1 -1
  10. package/dist/src/daemon.d.ts +7 -4
  11. package/dist/src/daemon.js +275 -47
  12. package/dist/src/daemon.js.map +1 -1
  13. package/dist/src/index.d.ts +150 -2
  14. package/dist/src/index.js +769 -139
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/learning-spine.d.ts +2 -1
  17. package/dist/src/learning-spine.js +8 -0
  18. package/dist/src/learning-spine.js.map +1 -1
  19. package/dist/src/ollama-client.d.ts +46 -0
  20. package/dist/src/ollama-client.js +231 -0
  21. package/dist/src/ollama-client.js.map +1 -0
  22. package/dist/src/provider-config.d.ts +28 -0
  23. package/dist/src/provider-config.js +150 -0
  24. package/dist/src/provider-config.js.map +1 -0
  25. package/dist/src/resolve-activation-root.d.ts +3 -3
  26. package/dist/src/resolve-activation-root.js +68 -21
  27. package/dist/src/resolve-activation-root.js.map +1 -1
  28. package/dist/src/shadow-extension-proof.d.ts +40 -0
  29. package/dist/src/shadow-extension-proof.js +214 -0
  30. package/dist/src/shadow-extension-proof.js.map +1 -0
  31. package/dist/src/teacher-labeler.d.ts +50 -0
  32. package/dist/src/teacher-labeler.js +424 -0
  33. package/dist/src/teacher-labeler.js.map +1 -0
  34. package/extension/index.ts +5 -1
  35. package/extension/runtime-guard.ts +17 -2
  36. package/package.json +8 -7
package/dist/src/index.js CHANGED
@@ -8,6 +8,8 @@ import { classifyFeedbackSignalContent, describeNormalizedEventExportObservabili
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
10
  import { appendLearningUpdateLogs, appendServeTimeRouteDecisionLog } from "./learning-spine.js";
11
+ import { createTeacherLabeler } from "./teacher-labeler.js";
12
+ export { createHttpOllamaTeacherLabelerClient, createOllamaTeacherLabeler, createTeacherLabeler } from "./teacher-labeler.js";
11
13
  const DEFAULT_AGENT_ID = "openclaw-runtime";
12
14
  const FEEDBACK_KINDS = new Set(["correction", "teaching", "approval", "suppression"]);
13
15
  export const DEFAULT_ASYNC_TEACHER_QUEUE_CAPACITY = 8;
@@ -220,6 +222,8 @@ function buildAsyncTeacherLoopNotes(input) {
220
222
  `teacher_feedback_budgeted_out=${input.sparseFeedback.budgetedOutFeedbackCount}`,
221
223
  `teacher_background_amplified=${input.sparseFeedback.amplifiedBackgroundLabelCount}`,
222
224
  `teacher_noop=${input.noOpReason}`,
225
+ `teacher_labeler=${input.teacherLabeler?.status ?? "disabled"}`,
226
+ `teacher_labeler_detail=${input.teacherLabeler?.detail ?? "disabled"}`,
223
227
  input.materialization === null ? "teacher_materialization=noop" : `teacher_materialized_pack=${input.materialization.candidate.summary.packId}`
224
228
  ];
225
229
  }
@@ -233,10 +237,13 @@ function cloneAsyncTeacherSnapshotState(value) {
233
237
  if (value === undefined) {
234
238
  return undefined;
235
239
  }
240
+ const interactionEvents = Array.isArray(value.interactionEvents) ? value.interactionEvents : [];
241
+ const feedbackEvents = Array.isArray(value.feedbackEvents) ? value.feedbackEvents : [];
242
+ const seenExportDigests = Array.isArray(value.seenExportDigests) ? value.seenExportDigests : [];
236
243
  return {
237
- interactionEvents: [...structuredClone(value.interactionEvents)],
238
- feedbackEvents: [...structuredClone(value.feedbackEvents)],
239
- seenExportDigests: [...value.seenExportDigests]
244
+ interactionEvents: [...structuredClone(interactionEvents)],
245
+ feedbackEvents: [...structuredClone(feedbackEvents)],
246
+ seenExportDigests: [...seenExportDigests]
240
247
  };
241
248
  }
242
249
  function cloneAsyncTeacherSnapshotRuntime(value) {
@@ -521,6 +528,7 @@ export class AsyncTeacherLiveLoop {
521
528
  input;
522
529
  queueCapacity;
523
530
  staleAfterMs;
531
+ teacherLabeler;
524
532
  queuedExportDigests = new Set();
525
533
  seenExportDigests = new Set();
526
534
  queue = [];
@@ -530,6 +538,7 @@ export class AsyncTeacherLiveLoop {
530
538
  teacherArtifacts = [];
531
539
  learnerState = createAlwaysOnLearningRuntimeState();
532
540
  lastMaterialization = null;
541
+ lastTeacherLabelerResult = null;
533
542
  diagnostics = {
534
543
  acceptedExportCount: 0,
535
544
  processedExportCount: 0,
@@ -548,13 +557,15 @@ export class AsyncTeacherLiveLoop {
548
557
  dedupedArtifactCount: 0,
549
558
  sparseFeedback: this.learnerState.sparseFeedback,
550
559
  noOpReason: "none",
551
- materialization: null
560
+ materialization: null,
561
+ teacherLabeler: null
552
562
  })
553
563
  };
554
564
  constructor(input) {
555
565
  this.input = input;
556
566
  this.queueCapacity = input.maxQueuedExports ?? DEFAULT_ASYNC_TEACHER_QUEUE_CAPACITY;
557
567
  this.staleAfterMs = input.staleAfterMs ?? DEFAULT_TEACHER_SUPERVISION_STALE_AFTER_MS;
568
+ this.teacherLabeler = createTeacherLabeler(input.teacherLabeler);
558
569
  if (!Number.isInteger(this.queueCapacity) || this.queueCapacity <= 0) {
559
570
  throw new Error("maxQueuedExports must be a positive integer");
560
571
  }
@@ -621,7 +632,9 @@ export class AsyncTeacherLiveLoop {
621
632
  this.queuedExportDigests.add(exportDigest);
622
633
  this.diagnostics.acceptedExportCount += 1;
623
634
  this.refreshNotes();
624
- void this.ensureDrain();
635
+ void this.ensureDrain().catch(() => {
636
+ // Explicit flush()/ingest callers observe the failure; the background kickoff must not leak an unhandled rejection.
637
+ });
625
638
  return {
626
639
  accepted: true,
627
640
  exportDigest,
@@ -800,57 +813,106 @@ export class AsyncTeacherLiveLoop {
800
813
  while (this.queue.length > 0) {
801
814
  const job = this.queue.shift();
802
815
  this.queuedExportDigests.delete(job.exportDigest);
803
- this.seenExportDigests.add(job.exportDigest);
804
- this.interactionEvents = mergeUniqueEvents(this.interactionEvents, job.normalizedEventExport.interactionEvents);
805
- this.feedbackEvents = mergeUniqueEvents(this.feedbackEvents, job.normalizedEventExport.feedbackEvents);
806
- const mergedNormalizedEventExport = buildNormalizedEventExport({
807
- interactionEvents: this.interactionEvents,
808
- feedbackEvents: this.feedbackEvents
809
- });
810
- const builtArtifacts = buildTeacherSupervisionArtifactsFromNormalizedEventExport({
811
- normalizedEventExport: mergedNormalizedEventExport,
812
- observedAt: job.observedAt,
813
- staleAfterMs: this.staleAfterMs,
814
- ...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {})
815
- });
816
- const currentDedupIds = new Set(this.teacherArtifacts.map((artifact) => artifact.dedupId));
817
- const nextTeacherArtifacts = mergeTeacherArtifacts(this.teacherArtifacts, builtArtifacts);
818
- const emittedArtifactCount = builtArtifacts.filter((artifact) => !currentDedupIds.has(artifact.dedupId)).length;
819
- const dedupedArtifactCount = builtArtifacts.length - emittedArtifactCount;
820
- this.teacherArtifacts = nextTeacherArtifacts;
821
- const learnedRoutingState = this.input.resolveLearnedRoutingState?.() ?? {};
822
- const learnerResult = advanceAlwaysOnLearningRuntime({
823
- packLabel: this.input.packLabel,
824
- workspace: this.input.workspace,
825
- interactionEvents: this.interactionEvents,
826
- feedbackEvents: this.feedbackEvents,
827
- teacherSupervisionArtifacts: this.teacherArtifacts,
828
- learnedRouting: this.input.learnedRouting,
829
- state: this.learnerState,
830
- builtAt: this.input.builtAt ?? job.observedAt,
831
- ...(this.input.offlineArtifacts !== undefined ? { offlineArtifacts: this.input.offlineArtifacts } : {}),
832
- ...(this.input.structuralOps !== undefined ? { structuralOps: this.input.structuralOps } : {}),
833
- ...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {}),
834
- ...(this.input.liveSliceSize !== undefined ? { liveSliceSize: this.input.liveSliceSize } : {}),
835
- ...(this.input.backfillSliceSize !== undefined ? { backfillSliceSize: this.input.backfillSliceSize } : {}),
836
- ...(this.input.cadence !== undefined ? { cadence: this.input.cadence } : {}),
837
- ...(learnedRoutingState.pgVersion !== undefined ? { pgVersion: learnedRoutingState.pgVersion } : {}),
838
- ...(learnedRoutingState.serveTimeDecisions !== undefined ? { serveTimeDecisions: learnedRoutingState.serveTimeDecisions } : {}),
839
- ...(learnedRoutingState.baselineState !== undefined ? { baselineState: learnedRoutingState.baselineState } : {})
840
- });
841
- this.learnerState = structuredClone(learnerResult.state);
842
- this.lastMaterialization = cloneAlwaysOnLearningMaterializationJobOrNull(learnerResult.materialization);
843
- const updatedBaseline = learnerResult.materialization?.candidate.routingBuild.updatedBaseline ?? null;
844
- if (updatedBaseline !== null) {
845
- this.input.persistUpdatedBaseline?.(structuredClone(updatedBaseline));
816
+ const previousInteractionEvents = [...structuredClone(this.interactionEvents)];
817
+ const previousFeedbackEvents = [...structuredClone(this.feedbackEvents)];
818
+ const previousTeacherArtifacts = cloneTeacherSupervisionArtifacts(this.teacherArtifacts);
819
+ const previousLearnerState = structuredClone(this.learnerState);
820
+ const previousLastMaterialization = cloneAlwaysOnLearningMaterializationJobOrNull(this.lastMaterialization);
821
+ const previousDiagnostics = {
822
+ ...structuredClone(this.diagnostics),
823
+ notes: [...this.diagnostics.notes]
824
+ };
825
+ const previousSeenExportDigests = [...this.seenExportDigests];
826
+ try {
827
+ this.seenExportDigests.add(job.exportDigest);
828
+ this.interactionEvents = mergeUniqueEvents(this.interactionEvents, job.normalizedEventExport.interactionEvents);
829
+ this.feedbackEvents = mergeUniqueEvents(this.feedbackEvents, job.normalizedEventExport.feedbackEvents);
830
+ const mergedNormalizedEventExport = buildNormalizedEventExport({
831
+ interactionEvents: this.interactionEvents,
832
+ feedbackEvents: this.feedbackEvents
833
+ });
834
+ const learnedRoutingState = this.input.resolveLearnedRoutingState?.() ?? {};
835
+ const builtArtifacts = buildTeacherSupervisionArtifactsFromNormalizedEventExport({
836
+ normalizedEventExport: mergedNormalizedEventExport,
837
+ observedAt: job.observedAt,
838
+ staleAfterMs: this.staleAfterMs,
839
+ ...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {})
840
+ });
841
+ let generatedTeacherArtifacts = [];
842
+ if (this.teacherLabeler !== null) {
843
+ try {
844
+ this.lastTeacherLabelerResult = await this.teacherLabeler.label({
845
+ normalizedEventExport: mergedNormalizedEventExport,
846
+ observedAt: job.observedAt,
847
+ staleAfterMs: this.staleAfterMs,
848
+ existingArtifacts: [...this.teacherArtifacts, ...builtArtifacts],
849
+ ...(learnedRoutingState.serveTimeDecisions !== undefined
850
+ ? { serveTimeDecisions: learnedRoutingState.serveTimeDecisions }
851
+ : {})
852
+ });
853
+ generatedTeacherArtifacts = this.lastTeacherLabelerResult.artifacts;
854
+ }
855
+ catch (error) {
856
+ this.lastTeacherLabelerResult = {
857
+ artifacts: [],
858
+ status: "fail_open",
859
+ detail: toErrorMessage(error)
860
+ };
861
+ }
862
+ }
863
+ const nextBuiltArtifacts = mergeTeacherArtifacts([], [...builtArtifacts, ...generatedTeacherArtifacts]);
864
+ const currentDedupIds = new Set(this.teacherArtifacts.map((artifact) => artifact.dedupId));
865
+ const nextTeacherArtifacts = mergeTeacherArtifacts(this.teacherArtifacts, nextBuiltArtifacts);
866
+ const emittedArtifactCount = nextBuiltArtifacts.filter((artifact) => !currentDedupIds.has(artifact.dedupId)).length;
867
+ const dedupedArtifactCount = nextBuiltArtifacts.length - emittedArtifactCount;
868
+ this.teacherArtifacts = nextTeacherArtifacts;
869
+ const learnerResult = advanceAlwaysOnLearningRuntime({
870
+ packLabel: this.input.packLabel,
871
+ workspace: this.input.workspace,
872
+ interactionEvents: this.interactionEvents,
873
+ feedbackEvents: this.feedbackEvents,
874
+ teacherSupervisionArtifacts: this.teacherArtifacts,
875
+ learnedRouting: this.input.learnedRouting,
876
+ state: this.learnerState,
877
+ builtAt: this.input.builtAt ?? job.observedAt,
878
+ ...(this.input.offlineArtifacts !== undefined ? { offlineArtifacts: this.input.offlineArtifacts } : {}),
879
+ ...(this.input.structuralOps !== undefined ? { structuralOps: this.input.structuralOps } : {}),
880
+ ...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {}),
881
+ ...(this.input.liveSliceSize !== undefined ? { liveSliceSize: this.input.liveSliceSize } : {}),
882
+ ...(this.input.backfillSliceSize !== undefined ? { backfillSliceSize: this.input.backfillSliceSize } : {}),
883
+ ...(this.input.cadence !== undefined ? { cadence: this.input.cadence } : {}),
884
+ ...(learnedRoutingState.pgVersion !== undefined ? { pgVersion: learnedRoutingState.pgVersion } : {}),
885
+ ...(learnedRoutingState.serveTimeDecisions !== undefined ? { serveTimeDecisions: learnedRoutingState.serveTimeDecisions } : {}),
886
+ ...(learnedRoutingState.baselineState !== undefined ? { baselineState: learnedRoutingState.baselineState } : {})
887
+ });
888
+ this.learnerState = structuredClone(learnerResult.state);
889
+ this.lastMaterialization = cloneAlwaysOnLearningMaterializationJobOrNull(learnerResult.materialization);
890
+ const updatedBaseline = learnerResult.materialization?.candidate.routingBuild.updatedBaseline ?? null;
891
+ if (updatedBaseline !== null) {
892
+ this.input.persistUpdatedBaseline?.(structuredClone(updatedBaseline));
893
+ }
894
+ this.diagnostics.processedExportCount += 1;
895
+ this.diagnostics.emittedArtifactCount += emittedArtifactCount;
896
+ this.diagnostics.dedupedArtifactCount += dedupedArtifactCount;
897
+ this.diagnostics.lastProcessedAt = job.observedAt;
898
+ this.diagnostics.latestFreshness = latestTeacherFreshness(this.teacherArtifacts);
899
+ this.diagnostics.lastNoOpReason = emittedArtifactCount === 0 ? "no_teacher_artifacts" : "none";
900
+ this.refreshNotes();
901
+ }
902
+ catch (error) {
903
+ this.interactionEvents = previousInteractionEvents;
904
+ this.feedbackEvents = previousFeedbackEvents;
905
+ this.teacherArtifacts = previousTeacherArtifacts;
906
+ this.learnerState = previousLearnerState;
907
+ this.lastMaterialization = previousLastMaterialization;
908
+ this.diagnostics = previousDiagnostics;
909
+ this.seenExportDigests.clear();
910
+ for (const exportDigest of previousSeenExportDigests) {
911
+ this.seenExportDigests.add(exportDigest);
912
+ }
913
+ this.refreshNotes();
914
+ throw error;
846
915
  }
847
- this.diagnostics.processedExportCount += 1;
848
- this.diagnostics.emittedArtifactCount += emittedArtifactCount;
849
- this.diagnostics.dedupedArtifactCount += dedupedArtifactCount;
850
- this.diagnostics.lastProcessedAt = job.observedAt;
851
- this.diagnostics.latestFreshness = latestTeacherFreshness(this.teacherArtifacts);
852
- this.diagnostics.lastNoOpReason = emittedArtifactCount === 0 ? "no_teacher_artifacts" : "none";
853
- this.refreshNotes();
854
916
  }
855
917
  }
856
918
  refreshNotes() {
@@ -862,7 +924,8 @@ export class AsyncTeacherLiveLoop {
862
924
  dedupedArtifactCount: this.diagnostics.dedupedArtifactCount,
863
925
  sparseFeedback: this.learnerState.sparseFeedback,
864
926
  noOpReason: this.diagnostics.lastNoOpReason,
865
- materialization: this.lastMaterialization
927
+ materialization: this.lastMaterialization,
928
+ teacherLabeler: this.lastTeacherLabelerResult
866
929
  });
867
930
  }
868
931
  }
@@ -968,7 +1031,8 @@ export function scanLiveEventExport(input) {
968
1031
  dedupedArtifactCount: 0,
969
1032
  sparseFeedback: learnerResult.state.sparseFeedback,
970
1033
  noOpReason: lastNoOpReason,
971
- materialization: learnerResult.materialization
1034
+ materialization: learnerResult.materialization,
1035
+ teacherLabeler: null
972
1036
  })
973
1037
  }
974
1038
  };
@@ -991,8 +1055,270 @@ function readJsonFile(filePath) {
991
1055
  export function resolveAsyncTeacherLiveLoopSnapshotPath(activationRoot) {
992
1056
  return path.join(path.resolve(normalizeNonEmptyString(activationRoot, "activationRoot")), "async-teacher-live-loop.snapshot.json");
993
1057
  }
994
- export function loadAsyncTeacherLiveLoopSnapshot(snapshotPath) {
995
- const snapshot = readJsonFile(path.resolve(snapshotPath));
1058
+ export const WATCH_STATE_DIRNAME = "watch";
1059
+ export const WATCH_SESSION_TAIL_CURSOR_BASENAME = "session-tail-cursor.json";
1060
+ export const WATCH_TEACHER_SNAPSHOT_BASENAME = "teacher-snapshot.json";
1061
+ function isAsyncTeacherLiveLoopSnapshot(value) {
1062
+ if (value === null || typeof value !== "object") {
1063
+ return false;
1064
+ }
1065
+ const candidate = value;
1066
+ return (candidate.runtimeOwner === "openclaw" &&
1067
+ candidate.queue !== undefined &&
1068
+ candidate.teacher !== undefined &&
1069
+ candidate.learner !== undefined &&
1070
+ candidate.diagnostics !== undefined);
1071
+ }
1072
+ function isWatchTeacherSnapshot(value) {
1073
+ if (value === null || typeof value !== "object") {
1074
+ return false;
1075
+ }
1076
+ const candidate = value;
1077
+ return (candidate.contract === "openclaw_watch_teacher_snapshot.v1" &&
1078
+ candidate.runtimeOwner === "openclaw" &&
1079
+ isAsyncTeacherLiveLoopSnapshot(candidate.snapshot));
1080
+ }
1081
+ function cloneWatchTeacherSnapshotFailure(value) {
1082
+ if (value === null ||
1083
+ value === undefined ||
1084
+ !["materialization_failed", "teacher_fail_open"].includes(value.mode) ||
1085
+ typeof value.detail !== "string" ||
1086
+ typeof value.at !== "string") {
1087
+ return null;
1088
+ }
1089
+ return {
1090
+ mode: value.mode,
1091
+ detail: value.detail,
1092
+ at: value.at
1093
+ };
1094
+ }
1095
+ function cloneRuntimeEventExportScannerCheckpoint(value) {
1096
+ return structuredClone(value);
1097
+ }
1098
+ function buildWatchTeacherSnapshotTeacherSummary(snapshot) {
1099
+ return {
1100
+ artifactCount: snapshot.teacher.artifactCount,
1101
+ latestFreshness: snapshot.teacher.latestFreshness,
1102
+ acceptedExportCount: snapshot.diagnostics.acceptedExportCount,
1103
+ processedExportCount: snapshot.diagnostics.processedExportCount,
1104
+ duplicateExportCount: snapshot.diagnostics.duplicateExportCount,
1105
+ droppedExportCount: snapshot.diagnostics.droppedExportCount,
1106
+ emittedArtifactCount: snapshot.diagnostics.emittedArtifactCount,
1107
+ dedupedArtifactCount: snapshot.diagnostics.dedupedArtifactCount,
1108
+ lastProcessedAt: snapshot.diagnostics.lastProcessedAt,
1109
+ lastNoOpReason: snapshot.diagnostics.lastNoOpReason,
1110
+ queueDepth: snapshot.queue.depth,
1111
+ queueCapacity: snapshot.queue.capacity,
1112
+ running: snapshot.queue.running,
1113
+ lastAppliedMaterializationJobId: snapshot.runtime?.lastAppliedMaterializationJobId ?? snapshot.learner.lastMaterialization?.jobId ?? null,
1114
+ lastMaterializedPackId: snapshot.learner.lastMaterialization?.candidate.summary.packId ?? null
1115
+ };
1116
+ }
1117
+ function buildWatchTeacherSnapshotLearningSummary(snapshot, lastHandledMaterializationPackId) {
1118
+ const plan = describeAlwaysOnLearningRuntimeState(snapshot.learner.state, snapshot.learner.lastMaterialization);
1119
+ return {
1120
+ bootstrapped: plan.bootstrapped,
1121
+ mode: plan.mode,
1122
+ nextPriorityLane: plan.nextPriorityLane,
1123
+ nextPriorityBucket: plan.nextPriorityBucket,
1124
+ pendingLive: plan.pending.live,
1125
+ pendingBackfill: plan.pending.backfill,
1126
+ pendingTotal: plan.pending.total,
1127
+ pendingByBucket: { ...plan.pending.byBucket },
1128
+ materializationCount: plan.materialization.count,
1129
+ lastMaterializedAt: plan.materialization.lastMaterializedAt,
1130
+ lastMaterializationReason: plan.materialization.lastReason,
1131
+ lastMaterializationLane: plan.materialization.lastLane,
1132
+ lastMaterializedPackId: snapshot.learner.lastMaterialization?.candidate.summary.packId ?? null,
1133
+ lastHandledMaterializationPackId
1134
+ };
1135
+ }
1136
+ function buildWatchTeacherSnapshotLabelingSummary(snapshot) {
1137
+ const learningSurface = snapshot.learner.lastMaterialization?.candidate.summary.learningSurface ??
1138
+ snapshot.learner.state.learnedEventExport?.provenance.learningSurface ??
1139
+ null;
1140
+ return {
1141
+ learningCadence: learningSurface?.learningCadence ?? "passive_background",
1142
+ scanPolicy: learningSurface?.scanPolicy ?? "always_on",
1143
+ liveSlicesPerCycle: 1,
1144
+ backfillSlicesPerCycle: 1,
1145
+ teacherBudget: snapshot.learner.state.sparseFeedback.teacherBudget,
1146
+ teacherDelayMs: snapshot.learner.state.sparseFeedback.teacherDelayMs,
1147
+ backgroundLabelAmplification: snapshot.learner.state.sparseFeedback.backgroundLabelAmplification
1148
+ };
1149
+ }
1150
+ function normalizeWatchTeacherSnapshotFromValue(value, snapshot, sourcePath) {
1151
+ const activationRoot = path.dirname(path.dirname(sourcePath));
1152
+ const defaultScanRoot = path.join(activationRoot, "event-exports");
1153
+ return {
1154
+ contract: "openclaw_watch_teacher_snapshot.v1",
1155
+ runtimeOwner: "openclaw",
1156
+ updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : new Date(0).toISOString(),
1157
+ lastRunAt: typeof value.lastRunAt === "string"
1158
+ ? value.lastRunAt
1159
+ : typeof value.updatedAt === "string"
1160
+ ? value.updatedAt
1161
+ : snapshot.diagnostics.lastProcessedAt ?? new Date(0).toISOString(),
1162
+ scanRoot: typeof value.scanRoot === "string" ? value.scanRoot : defaultScanRoot,
1163
+ sessionTailCursorPath: typeof value.sessionTailCursorPath === "string"
1164
+ ? value.sessionTailCursorPath
1165
+ : resolveWatchSessionTailCursorPath(activationRoot),
1166
+ sessionTailCursorUpdatedAt: typeof value.sessionTailCursorUpdatedAt === "string"
1167
+ ? value.sessionTailCursorUpdatedAt
1168
+ : typeof value.updatedAt === "string"
1169
+ ? value.updatedAt
1170
+ : new Date(0).toISOString(),
1171
+ sessionTailSessionsTracked: typeof value.sessionTailSessionsTracked === "number" ? value.sessionTailSessionsTracked : 0,
1172
+ sessionTailBridgedEventCount: typeof value.sessionTailBridgedEventCount === "number" ? value.sessionTailBridgedEventCount : 0,
1173
+ scannerCheckpointPath: typeof value.scannerCheckpointPath === "string"
1174
+ ? value.scannerCheckpointPath
1175
+ : path.join(typeof value.scanRoot === "string" ? value.scanRoot : defaultScanRoot, ".openclawbrain-scanner-checkpoint.json"),
1176
+ scannerCheckpoint: value.scannerCheckpoint !== undefined
1177
+ ? cloneRuntimeEventExportScannerCheckpoint(value.scannerCheckpoint)
1178
+ : createRuntimeEventExportScannerCheckpoint({
1179
+ scanRoot: typeof value.scanRoot === "string" ? value.scanRoot : defaultScanRoot
1180
+ }),
1181
+ replayedBundleCount: typeof value.replayedBundleCount === "number" ? value.replayedBundleCount : 0,
1182
+ replayedEventCount: typeof value.replayedEventCount === "number" ? value.replayedEventCount : 0,
1183
+ exportedBundleCount: typeof value.exportedBundleCount === "number" ? value.exportedBundleCount : 0,
1184
+ exportedEventCount: typeof value.exportedEventCount === "number" ? value.exportedEventCount : 0,
1185
+ startupWarnings: Array.isArray(value.startupWarnings)
1186
+ ? value.startupWarnings.filter((warning) => typeof warning === "string")
1187
+ : [],
1188
+ lastTeacherError: typeof value.lastTeacherError === "string"
1189
+ ? value.lastTeacherError
1190
+ : null,
1191
+ localSessionTailNoopReason: typeof value.localSessionTailNoopReason === "string" ? value.localSessionTailNoopReason : null,
1192
+ lastHandledMaterializationPackId: typeof value.lastHandledMaterializationPackId === "string" ? value.lastHandledMaterializationPackId : null,
1193
+ teacher: value.teacher ?? buildWatchTeacherSnapshotTeacherSummary(snapshot),
1194
+ learning: value.learning ?? buildWatchTeacherSnapshotLearningSummary(snapshot, typeof value.lastHandledMaterializationPackId === "string" ? value.lastHandledMaterializationPackId : null),
1195
+ labeling: value.labeling ?? buildWatchTeacherSnapshotLabelingSummary(snapshot),
1196
+ failure: cloneWatchTeacherSnapshotFailure(value.failure),
1197
+ snapshot
1198
+ };
1199
+ }
1200
+ export function resolveWatchStateRoot(activationRoot) {
1201
+ return path.resolve(normalizeNonEmptyString(activationRoot, "activationRoot"), WATCH_STATE_DIRNAME);
1202
+ }
1203
+ export function resolveWatchSessionTailCursorPath(activationRoot) {
1204
+ return path.join(resolveWatchStateRoot(activationRoot), WATCH_SESSION_TAIL_CURSOR_BASENAME);
1205
+ }
1206
+ export function resolveWatchTeacherSnapshotPath(activationRoot) {
1207
+ return path.join(resolveWatchStateRoot(activationRoot), WATCH_TEACHER_SNAPSHOT_BASENAME);
1208
+ }
1209
+ export function resolveOperatorTeacherSnapshotPath(activationRoot, explicitPath) {
1210
+ if (explicitPath !== null && explicitPath !== undefined) {
1211
+ return explicitPath;
1212
+ }
1213
+ const canonicalWatchSnapshotPath = resolveWatchTeacherSnapshotPath(activationRoot);
1214
+ if (existsSync(canonicalWatchSnapshotPath)) {
1215
+ return canonicalWatchSnapshotPath;
1216
+ }
1217
+ const asyncSnapshotPath = resolveAsyncTeacherLiveLoopSnapshotPath(activationRoot);
1218
+ return existsSync(asyncSnapshotPath) ? asyncSnapshotPath : null;
1219
+ }
1220
+ export function loadTeacherSurface(snapshotPath) {
1221
+ const resolvedPath = path.resolve(snapshotPath);
1222
+ let parsed;
1223
+ try {
1224
+ parsed = readJsonFile(resolvedPath);
1225
+ }
1226
+ catch {
1227
+ return null;
1228
+ }
1229
+ if (isWatchTeacherSnapshot(parsed)) {
1230
+ const snapshot = loadAsyncTeacherLiveLoopSnapshotFromValue(parsed.snapshot);
1231
+ return {
1232
+ sourcePath: resolvedPath,
1233
+ sourceKind: "watch_snapshot",
1234
+ snapshot,
1235
+ watchSnapshot: normalizeWatchTeacherSnapshotFromValue(parsed, snapshot, resolvedPath)
1236
+ };
1237
+ }
1238
+ if (isAsyncTeacherLiveLoopSnapshot(parsed)) {
1239
+ return {
1240
+ sourcePath: resolvedPath,
1241
+ sourceKind: "async_snapshot",
1242
+ snapshot: loadAsyncTeacherLiveLoopSnapshotFromValue(parsed),
1243
+ watchSnapshot: null
1244
+ };
1245
+ }
1246
+ return null;
1247
+ }
1248
+ export function loadWatchTeacherSnapshotState(snapshotPath) {
1249
+ const resolvedPath = path.resolve(snapshotPath);
1250
+ if (!existsSync(resolvedPath)) {
1251
+ return {
1252
+ lastHandledMaterializationPackId: null,
1253
+ snapshot: null,
1254
+ error: null
1255
+ };
1256
+ }
1257
+ let parsed;
1258
+ try {
1259
+ parsed = readJsonFile(resolvedPath);
1260
+ }
1261
+ catch (error) {
1262
+ return {
1263
+ lastHandledMaterializationPackId: null,
1264
+ snapshot: null,
1265
+ error: error instanceof Error ? error.message : String(error)
1266
+ };
1267
+ }
1268
+ if (isWatchTeacherSnapshot(parsed)) {
1269
+ return {
1270
+ lastHandledMaterializationPackId: parsed.lastHandledMaterializationPackId,
1271
+ snapshot: loadAsyncTeacherLiveLoopSnapshotFromValue(parsed.snapshot),
1272
+ error: null
1273
+ };
1274
+ }
1275
+ if (isAsyncTeacherLiveLoopSnapshot(parsed)) {
1276
+ return {
1277
+ lastHandledMaterializationPackId: null,
1278
+ snapshot: loadAsyncTeacherLiveLoopSnapshotFromValue(parsed),
1279
+ error: null
1280
+ };
1281
+ }
1282
+ return {
1283
+ lastHandledMaterializationPackId: null,
1284
+ snapshot: null,
1285
+ error: `watch teacher snapshot is invalid: ${resolvedPath}`
1286
+ };
1287
+ }
1288
+ export function persistWatchTeacherSnapshot(snapshotPath, input) {
1289
+ const persistedAt = new Date().toISOString();
1290
+ const canonicalSnapshot = loadAsyncTeacherLiveLoopSnapshotFromValue(input.snapshot);
1291
+ const payload = {
1292
+ contract: "openclaw_watch_teacher_snapshot.v1",
1293
+ runtimeOwner: "openclaw",
1294
+ updatedAt: persistedAt,
1295
+ lastRunAt: input.lastRunAt,
1296
+ scanRoot: path.resolve(input.scanRoot),
1297
+ sessionTailCursorPath: path.resolve(input.sessionTailCursorPath),
1298
+ sessionTailCursorUpdatedAt: input.sessionTailCursorUpdatedAt,
1299
+ sessionTailSessionsTracked: input.sessionTailSessionsTracked,
1300
+ sessionTailBridgedEventCount: input.sessionTailBridgedEventCount,
1301
+ scannerCheckpointPath: path.resolve(input.scannerCheckpointPath),
1302
+ scannerCheckpoint: cloneRuntimeEventExportScannerCheckpoint(input.scannerCheckpoint),
1303
+ replayedBundleCount: input.replayedBundleCount,
1304
+ replayedEventCount: input.replayedEventCount,
1305
+ exportedBundleCount: input.exportedBundleCount,
1306
+ exportedEventCount: input.exportedEventCount,
1307
+ startupWarnings: [...input.startupWarnings],
1308
+ lastTeacherError: input.lastTeacherError,
1309
+ localSessionTailNoopReason: input.localSessionTailNoopReason,
1310
+ lastHandledMaterializationPackId: input.lastHandledMaterializationPackId,
1311
+ teacher: buildWatchTeacherSnapshotTeacherSummary(canonicalSnapshot),
1312
+ learning: buildWatchTeacherSnapshotLearningSummary(canonicalSnapshot, input.lastHandledMaterializationPackId),
1313
+ labeling: buildWatchTeacherSnapshotLabelingSummary(canonicalSnapshot),
1314
+ failure: cloneWatchTeacherSnapshotFailure(input.failure),
1315
+ snapshot: canonicalSnapshot
1316
+ };
1317
+ mkdirSync(path.dirname(snapshotPath), { recursive: true });
1318
+ writeFileSync(snapshotPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
1319
+ return payload;
1320
+ }
1321
+ function loadAsyncTeacherLiveLoopSnapshotFromValue(snapshot) {
996
1322
  if (snapshot.runtimeOwner !== "openclaw") {
997
1323
  throw new Error("async teacher snapshot runtimeOwner must be openclaw");
998
1324
  }
@@ -1019,6 +1345,17 @@ export function loadAsyncTeacherLiveLoopSnapshot(snapshotPath) {
1019
1345
  }
1020
1346
  return cloned;
1021
1347
  }
1348
+ export function loadAsyncTeacherLiveLoopSnapshot(snapshotPath) {
1349
+ const resolvedPath = path.resolve(snapshotPath);
1350
+ const parsed = readJsonFile(resolvedPath);
1351
+ if (isWatchTeacherSnapshot(parsed)) {
1352
+ return loadAsyncTeacherLiveLoopSnapshotFromValue(parsed.snapshot);
1353
+ }
1354
+ if (isAsyncTeacherLiveLoopSnapshot(parsed)) {
1355
+ return loadAsyncTeacherLiveLoopSnapshotFromValue(parsed);
1356
+ }
1357
+ throw new Error(`async teacher snapshot is invalid: ${resolvedPath}`);
1358
+ }
1022
1359
  function resolveBundlePayloadPath(rootDir, payloadPath) {
1023
1360
  const resolved = path.resolve(rootDir, payloadPath);
1024
1361
  const relative = path.relative(rootDir, resolved);
@@ -1653,6 +1990,18 @@ export class RuntimeEventExportScanner {
1653
1990
  snapshot() {
1654
1991
  return structuredClone(this.checkpoint);
1655
1992
  }
1993
+ restoreCheckpoint(checkpoint) {
1994
+ const restored = structuredClone(checkpoint);
1995
+ const errors = validateRuntimeEventExportScannerCheckpoint(restored);
1996
+ if (errors.length > 0) {
1997
+ throw new Error(`runtime event export scanner checkpoint is invalid: ${errors.join("; ")}`);
1998
+ }
1999
+ if (restored.scanRoot !== this.scanRoot) {
2000
+ throw new Error(`runtime event export scanner checkpoint scanRoot mismatch: checkpoint=${restored.scanRoot} scanner=${this.scanRoot}`);
2001
+ }
2002
+ this.checkpoint = restored;
2003
+ writeRuntimeEventExportScannerCheckpoint(this.checkpointPath, this.checkpoint);
2004
+ }
1656
2005
  scanOnce(options = {}) {
1657
2006
  const scannedAt = normalizeIsoTimestamp(options.scannedAt, "scannedAt", new Date().toISOString());
1658
2007
  const discovered = discoverRuntimeEventExportBundles(this.scanRoot);
@@ -2102,6 +2451,31 @@ function normalizeServeRouteChannel(value) {
2102
2451
  function normalizeServeRouteMessage(value) {
2103
2452
  return typeof value === "string" ? value.trim() : "";
2104
2453
  }
2454
+ function normalizeServeRouteInstalledEntryPath(value) {
2455
+ if (typeof value !== "string") {
2456
+ return null;
2457
+ }
2458
+ const trimmed = value.trim();
2459
+ return trimmed.length === 0 ? null : path.resolve(trimmed);
2460
+ }
2461
+ function buildCompileServeRouteBreadcrumbs(input) {
2462
+ return {
2463
+ entrypoint: "compileRuntimeContext",
2464
+ invocationSurface: input._serveRouteBreadcrumbs?.invocationSurface ?? "direct_compile_call",
2465
+ hostEvent: input._serveRouteBreadcrumbs?.hostEvent ?? null,
2466
+ installedEntryPath: normalizeServeRouteInstalledEntryPath(input._serveRouteBreadcrumbs?.installedEntryPath),
2467
+ syntheticTurn: true
2468
+ };
2469
+ }
2470
+ function buildRunRuntimeTurnServeRouteBreadcrumbs() {
2471
+ return {
2472
+ entrypoint: "runRuntimeTurn",
2473
+ invocationSurface: "runtime_turn_helper",
2474
+ hostEvent: null,
2475
+ installedEntryPath: null,
2476
+ syntheticTurn: false
2477
+ };
2478
+ }
2105
2479
  function appendCompileServeRouteDecisionLog(input) {
2106
2480
  if (input.compileInput._suppressServeLog) {
2107
2481
  return;
@@ -2132,7 +2506,8 @@ function appendCompileServeRouteDecisionLog(input) {
2132
2506
  activationRoot: input.activationRoot,
2133
2507
  turn: syntheticTurn,
2134
2508
  compileResult: input.compileResult,
2135
- recordedAt
2509
+ recordedAt,
2510
+ breadcrumbs: buildCompileServeRouteBreadcrumbs(input.compileInput)
2136
2511
  });
2137
2512
  }
2138
2513
  catch (error) {
@@ -3274,6 +3649,7 @@ export function writeScannedEventExportBundle(input) {
3274
3649
  }
3275
3650
  export function runRuntimeTurn(turn, options = {}) {
3276
3651
  const warnings = [];
3652
+ const serveRouteBreadcrumbs = buildRunRuntimeTurnServeRouteBreadcrumbs();
3277
3653
  const agentId = normalizeOptionalString(turn.agentId);
3278
3654
  const compileInput = {
3279
3655
  activationRoot: (options.activationRoot ?? turn.activationRoot),
@@ -3294,7 +3670,8 @@ export function runRuntimeTurn(turn, options = {}) {
3294
3670
  activationRoot: compileResult.activationRoot,
3295
3671
  turn,
3296
3672
  compileResult,
3297
- recordedAt: serveLoggedAt
3673
+ recordedAt: serveLoggedAt,
3674
+ breadcrumbs: serveRouteBreadcrumbs
3298
3675
  });
3299
3676
  }
3300
3677
  catch (error) {
@@ -3312,7 +3689,8 @@ export function runRuntimeTurn(turn, options = {}) {
3312
3689
  turn,
3313
3690
  compileResult,
3314
3691
  normalizedEventExport,
3315
- recordedAt: compileEvent?.createdAt ?? serveLoggedAt
3692
+ recordedAt: compileEvent?.createdAt ?? serveLoggedAt,
3693
+ breadcrumbs: serveRouteBreadcrumbs
3316
3694
  });
3317
3695
  }
3318
3696
  catch (error) {
@@ -4536,9 +4914,137 @@ function summarizeManyProfileSupport(policyMode) {
4536
4914
  detail: "The Host has not declared shared-vs-dedicated attachment policy. Keep the operator read current-profile-only, do not infer profile exclusivity from activation state alone, and do not claim same-gateway many-profile proof."
4537
4915
  };
4538
4916
  }
4917
+ function summarizeRetainedActivationSlots(input) {
4918
+ const retained = [];
4919
+ if (input.candidate !== null) {
4920
+ retained.push(`candidate=${input.candidate.packId}`);
4921
+ }
4922
+ if (input.previous !== null) {
4923
+ retained.push(`previous=${input.previous.packId}`);
4924
+ }
4925
+ return retained.length === 0 ? "none" : retained.join(", ");
4926
+ }
4539
4927
  function isAwaitingFirstExportSlot(slot) {
4540
4928
  return slot !== null && slot.eventRange.count === 0;
4541
4929
  }
4930
+ function summarizeOperatorActivationState(input) {
4931
+ if (input.inspectionError !== null) {
4932
+ return {
4933
+ state: "broken_install",
4934
+ detail: `activation root could not be inspected because activation pointers or pinned pack metadata are unreadable: ${input.inspectionError}`,
4935
+ inspectionError: input.inspectionError
4936
+ };
4937
+ }
4938
+ if (input.active !== null && !input.active.activationReady) {
4939
+ return {
4940
+ state: "broken_install",
4941
+ detail: input.active.findings.length > 0
4942
+ ? `active pack ${input.active.packId} is pinned but not activation-ready: ${input.active.findings.join("; ")}`
4943
+ : `active pack ${input.active.packId} is pinned but not activation-ready`,
4944
+ inspectionError: null
4945
+ };
4946
+ }
4947
+ if (input.active === null) {
4948
+ if (input.candidate !== null || input.previous !== null) {
4949
+ return {
4950
+ state: "stale_incomplete",
4951
+ detail: `activation root has retained non-serving state but no active pack (${summarizeRetainedActivationSlots(input)})`,
4952
+ inspectionError: null
4953
+ };
4954
+ }
4955
+ return {
4956
+ state: "detached",
4957
+ detail: "activation root has no active, candidate, or previous pack pinned",
4958
+ inspectionError: null
4959
+ };
4960
+ }
4961
+ if (input.observability === null) {
4962
+ return {
4963
+ state: "broken_install",
4964
+ detail: `active pack ${input.active.packId} is pinned, but activation observability could not be derived`,
4965
+ inspectionError: null
4966
+ };
4967
+ }
4968
+ if (input.observability.initHandoff.handoffState === "pg_promoted_pack_authoritative") {
4969
+ return {
4970
+ state: "active_promoted",
4971
+ detail: `active pack ${input.active.packId} is authoritative through promotion, not the seed handoff`,
4972
+ inspectionError: null
4973
+ };
4974
+ }
4975
+ if (isAwaitingFirstExportSlot(input.active)) {
4976
+ return {
4977
+ state: "awaiting_first_export",
4978
+ detail: `active seed-state pack ${input.active.packId} is healthy but still waiting for the first live export`,
4979
+ inspectionError: null
4980
+ };
4981
+ }
4982
+ if (input.observability.initHandoff.handoffState === "seed_state_authoritative") {
4983
+ return {
4984
+ state: "healthy_seed",
4985
+ detail: `active seed-state pack ${input.active.packId} is serving beyond the first export`,
4986
+ inspectionError: null
4987
+ };
4988
+ }
4989
+ return {
4990
+ state: "stale_incomplete",
4991
+ detail: `active pack ${input.active.packId} is pinned, but init handoff truth is incomplete (${input.observability.initHandoff.handoffState})`,
4992
+ inspectionError: null
4993
+ };
4994
+ }
4995
+ function summarizeBrainStateWithoutObservability(active, activation) {
4996
+ return {
4997
+ state: active === null ? "no_active_pack" : "missing",
4998
+ initMode: null,
4999
+ runtimePlasticitySource: null,
5000
+ seedStateVisible: false,
5001
+ seedBlockCount: 0,
5002
+ activePackId: active?.packId ?? null,
5003
+ activeWorkspaceSnapshot: active?.workspaceSnapshot ?? null,
5004
+ activeEventExportDigest: active?.eventExportDigest ?? null,
5005
+ detail: activation.detail
5006
+ };
5007
+ }
5008
+ function summarizeGraphWithoutObservability(active, activation) {
5009
+ return {
5010
+ available: false,
5011
+ runtimePlasticitySource: null,
5012
+ structuralOps: null,
5013
+ changed: null,
5014
+ operationsApplied: [],
5015
+ liveBlockCount: null,
5016
+ prunedBlockCount: null,
5017
+ prePruneBlockCount: null,
5018
+ strongestBlockId: null,
5019
+ operatorSummary: null,
5020
+ detail: active === null
5021
+ ? activation.detail
5022
+ : `active pack ${active.packId} is pinned, but graph observability is unavailable`
5023
+ };
5024
+ }
5025
+ function summarizeLearnedRoutingWithoutObservability(active) {
5026
+ return {
5027
+ required: active?.routePolicy === "requires_learned_routing",
5028
+ available: false,
5029
+ routerIdentity: active?.routerIdentity ?? null,
5030
+ routeFnVersion: null,
5031
+ trainingMethod: null,
5032
+ routerTrainedAt: null,
5033
+ objective: null,
5034
+ pgProfile: null,
5035
+ routerChecksum: null,
5036
+ objectiveChecksum: null,
5037
+ updateMechanism: null,
5038
+ updateVersion: null,
5039
+ updateCount: null,
5040
+ supervisionCount: null,
5041
+ collectedLabelsTotal: null,
5042
+ freshnessChecksum: null,
5043
+ handoffState: "missing",
5044
+ initMode: null,
5045
+ seedStateVisible: false
5046
+ };
5047
+ }
4542
5048
  function summarizeBrainState(active, observability) {
4543
5049
  if (active === null) {
4544
5050
  return {
@@ -4692,6 +5198,11 @@ function summarizeServePath(compile) {
4692
5198
  error: compile.error
4693
5199
  };
4694
5200
  }
5201
+ function probeOperatorServePath(activationRoot, observability, activePackId) {
5202
+ const compileInput = buildAttachStatusCompileInput(activationRoot, undefined);
5203
+ const compile = compileInput === null ? null : buildAttachCompileStatus(compileRuntimeContext(compileInput), observability, activePackId);
5204
+ return summarizeServePath(compile);
5205
+ }
4695
5206
  function loadOperatorEventExport(input) {
4696
5207
  const eventExportPath = normalizeOptionalString(input.eventExportPath);
4697
5208
  if (eventExportPath === undefined) {
@@ -4880,70 +5391,125 @@ function summarizeSupervision(input) {
4880
5391
  : "the supplied export does not yet show human supervision"
4881
5392
  };
4882
5393
  }
4883
- function loadTeacherSnapshot(input) {
5394
+ function loadTeacherSurfaceFromInput(input) {
4884
5395
  const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4885
5396
  if (teacherSnapshotPath === undefined) {
4886
5397
  return null;
4887
5398
  }
4888
- return loadAsyncTeacherLiveLoopSnapshot(teacherSnapshotPath);
5399
+ return loadTeacherSurface(teacherSnapshotPath);
4889
5400
  }
4890
5401
  function summarizeTeacherLoop(input) {
4891
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4892
- if (teacherSnapshotPath === undefined) {
5402
+ const loaded = loadTeacherSurfaceFromInput(input);
5403
+ if (loaded === null && normalizeOptionalString(input.teacherSnapshotPath) === undefined) {
4893
5404
  return {
4894
5405
  available: false,
4895
5406
  sourcePath: null,
5407
+ sourceKind: "missing",
5408
+ lastRunAt: null,
4896
5409
  lastNoOpReason: "unavailable",
4897
5410
  latestFreshness: "unavailable",
4898
5411
  startedAt: null,
4899
5412
  lastHeartbeatAt: null,
4900
5413
  lastScanAt: null,
4901
5414
  lastProcessedAt: null,
5415
+ artifactCount: null,
4902
5416
  queueDepth: null,
4903
5417
  queueCapacity: null,
4904
5418
  running: null,
5419
+ replayedBundleCount: null,
5420
+ replayedEventCount: null,
5421
+ exportedBundleCount: null,
5422
+ exportedEventCount: null,
5423
+ sessionTailSessionsTracked: null,
5424
+ sessionTailBridgedEventCount: null,
5425
+ localSessionTailNoopReason: null,
5426
+ learningCadence: "unavailable",
5427
+ scanPolicy: "unavailable",
5428
+ liveSlicesPerCycle: null,
5429
+ backfillSlicesPerCycle: null,
5430
+ failureMode: "unavailable",
5431
+ failureDetail: null,
4905
5432
  lastAppliedMaterializationJobId: null,
4906
5433
  lastMaterializedPackId: null,
4907
5434
  notes: [],
4908
5435
  detail: "no teacher snapshot path supplied"
4909
5436
  };
4910
5437
  }
4911
- const snapshot = loadTeacherSnapshot(input);
4912
- if (snapshot === null) {
5438
+ if (loaded === null) {
5439
+ const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4913
5440
  return {
4914
5441
  available: false,
4915
- sourcePath: path.resolve(teacherSnapshotPath),
5442
+ sourcePath: teacherSnapshotPath === undefined ? null : path.resolve(teacherSnapshotPath),
5443
+ sourceKind: "missing",
5444
+ lastRunAt: null,
4916
5445
  lastNoOpReason: "unavailable",
4917
5446
  latestFreshness: "unavailable",
4918
5447
  startedAt: null,
4919
5448
  lastHeartbeatAt: null,
4920
5449
  lastScanAt: null,
4921
5450
  lastProcessedAt: null,
5451
+ artifactCount: null,
4922
5452
  queueDepth: null,
4923
5453
  queueCapacity: null,
4924
5454
  running: null,
5455
+ replayedBundleCount: null,
5456
+ replayedEventCount: null,
5457
+ exportedBundleCount: null,
5458
+ exportedEventCount: null,
5459
+ sessionTailSessionsTracked: null,
5460
+ sessionTailBridgedEventCount: null,
5461
+ localSessionTailNoopReason: null,
5462
+ learningCadence: "unavailable",
5463
+ scanPolicy: "unavailable",
5464
+ liveSlicesPerCycle: null,
5465
+ backfillSlicesPerCycle: null,
5466
+ failureMode: "unavailable",
5467
+ failureDetail: null,
4925
5468
  lastAppliedMaterializationJobId: null,
4926
5469
  lastMaterializedPackId: null,
4927
5470
  notes: [],
4928
5471
  detail: "teacher snapshot could not be loaded"
4929
5472
  };
4930
5473
  }
5474
+ const snapshot = loaded.snapshot;
5475
+ const watchSnapshot = loaded.watchSnapshot;
4931
5476
  return {
4932
5477
  available: true,
4933
- sourcePath: path.resolve(teacherSnapshotPath),
5478
+ sourcePath: loaded.sourcePath,
5479
+ sourceKind: loaded.sourceKind,
5480
+ lastRunAt: watchSnapshot?.lastRunAt ?? snapshot.runtime?.lastHeartbeatAt ?? snapshot.diagnostics.lastProcessedAt ?? null,
4934
5481
  lastNoOpReason: snapshot.diagnostics.lastNoOpReason,
4935
5482
  latestFreshness: snapshot.diagnostics.latestFreshness,
4936
5483
  startedAt: snapshot.runtime?.startedAt ?? null,
4937
5484
  lastHeartbeatAt: snapshot.runtime?.lastHeartbeatAt ?? null,
4938
5485
  lastScanAt: snapshot.runtime?.lastScanAt ?? null,
4939
5486
  lastProcessedAt: snapshot.diagnostics.lastProcessedAt,
5487
+ artifactCount: watchSnapshot?.teacher.artifactCount ?? snapshot.teacher.artifactCount,
4940
5488
  queueDepth: snapshot.queue.depth,
4941
5489
  queueCapacity: snapshot.queue.capacity,
4942
5490
  running: snapshot.queue.running,
4943
- lastAppliedMaterializationJobId: snapshot.runtime?.lastAppliedMaterializationJobId ?? null,
5491
+ replayedBundleCount: watchSnapshot?.replayedBundleCount ?? null,
5492
+ replayedEventCount: watchSnapshot?.replayedEventCount ?? null,
5493
+ exportedBundleCount: watchSnapshot?.exportedBundleCount ?? null,
5494
+ exportedEventCount: watchSnapshot?.exportedEventCount ?? null,
5495
+ sessionTailSessionsTracked: watchSnapshot?.sessionTailSessionsTracked ?? null,
5496
+ sessionTailBridgedEventCount: watchSnapshot?.sessionTailBridgedEventCount ?? null,
5497
+ localSessionTailNoopReason: watchSnapshot?.localSessionTailNoopReason ?? null,
5498
+ learningCadence: watchSnapshot?.labeling.learningCadence ?? "unavailable",
5499
+ scanPolicy: watchSnapshot?.labeling.scanPolicy ?? "unavailable",
5500
+ liveSlicesPerCycle: watchSnapshot?.labeling.liveSlicesPerCycle ?? null,
5501
+ backfillSlicesPerCycle: watchSnapshot?.labeling.backfillSlicesPerCycle ?? null,
5502
+ failureMode: watchSnapshot?.failure?.mode ?? "none",
5503
+ failureDetail: watchSnapshot?.failure?.detail ?? null,
5504
+ lastAppliedMaterializationJobId: watchSnapshot?.teacher.lastAppliedMaterializationJobId ??
5505
+ snapshot.runtime?.lastAppliedMaterializationJobId ??
5506
+ snapshot.learner.lastMaterialization?.jobId ??
5507
+ null,
4944
5508
  lastMaterializedPackId: snapshot.learner.lastMaterialization?.candidate.summary.packId ?? null,
4945
5509
  notes: [...snapshot.diagnostics.notes],
4946
- detail: "async teacher diagnostics loaded"
5510
+ detail: loaded.sourceKind === "watch_snapshot"
5511
+ ? "canonical watch teacher snapshot loaded"
5512
+ : "raw async teacher snapshot loaded"
4947
5513
  };
4948
5514
  }
4949
5515
  function summarizeLearningBacklogState(plan, principalLagStatus) {
@@ -5000,8 +5566,8 @@ function summarizeAlwaysOnLearning(input, active) {
5000
5566
  sequenceLag: null,
5001
5567
  status: "unavailable"
5002
5568
  };
5003
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
5004
- if (teacherSnapshotPath === undefined) {
5569
+ const loadedTeacherSurface = loadTeacherSurfaceFromInput(input);
5570
+ if (loadedTeacherSurface === null && normalizeOptionalString(input.teacherSnapshotPath) === undefined) {
5005
5571
  return {
5006
5572
  available: false,
5007
5573
  sourcePath: null,
@@ -5033,11 +5599,11 @@ function summarizeAlwaysOnLearning(input, active) {
5033
5599
  detail: "no teacher snapshot path supplied"
5034
5600
  };
5035
5601
  }
5036
- const snapshot = loadTeacherSnapshot(input);
5037
- if (snapshot === null) {
5602
+ if (loadedTeacherSurface === null) {
5603
+ const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
5038
5604
  return {
5039
5605
  available: false,
5040
- sourcePath: path.resolve(teacherSnapshotPath),
5606
+ sourcePath: teacherSnapshotPath === undefined ? null : path.resolve(teacherSnapshotPath),
5041
5607
  bootstrapped: null,
5042
5608
  mode: "unavailable",
5043
5609
  nextPriorityLane: "unavailable",
@@ -5066,6 +5632,7 @@ function summarizeAlwaysOnLearning(input, active) {
5066
5632
  detail: "teacher snapshot could not be loaded"
5067
5633
  };
5068
5634
  }
5635
+ const snapshot = loadedTeacherSurface.snapshot;
5069
5636
  const plan = describeAlwaysOnLearningRuntimeState(snapshot.learner.state, snapshot.learner.lastMaterialization);
5070
5637
  const latestPrincipalSequence = plan.principalBacklog.checkpoints.reduce((latest, checkpoint) => {
5071
5638
  const candidate = checkpoint.newestPendingSequence ?? checkpoint.learnedThroughSequence;
@@ -5091,7 +5658,7 @@ function summarizeAlwaysOnLearning(input, active) {
5091
5658
  });
5092
5659
  return {
5093
5660
  available: true,
5094
- sourcePath: path.resolve(teacherSnapshotPath),
5661
+ sourcePath: loadedTeacherSurface.sourcePath,
5095
5662
  bootstrapped: plan.bootstrapped,
5096
5663
  mode: plan.mode,
5097
5664
  nextPriorityLane: plan.nextPriorityLane,
@@ -5140,7 +5707,13 @@ function buildOperatorFindings(report) {
5140
5707
  const push = (severity, code, summary, detail) => {
5141
5708
  findings.push({ severity, code, summary, detail });
5142
5709
  };
5143
- if (report.active === null) {
5710
+ if (report.activation.state === "broken_install") {
5711
+ push("fail", "activation_broken_install", "activation root is broken", report.activation.detail);
5712
+ }
5713
+ else if (report.activation.state === "stale_incomplete") {
5714
+ push("fail", "activation_stale_incomplete", "activation root is stale or incomplete", report.activation.detail);
5715
+ }
5716
+ else if (report.active === null) {
5144
5717
  push("fail", "active_missing", "active slot is empty", "no active pack found; this is the pre-bootstrap state — call `bootstrapRuntimeAttach()` to activate an initial pack before compiling or serving");
5145
5718
  }
5146
5719
  else if (!report.active.activationReady) {
@@ -5281,12 +5854,18 @@ function summarizeCurrentProfileLogRoot(activationRoot) {
5281
5854
  const logRoot = path.join(path.resolve(activationRoot), LEARNING_SPINE_LOG_LAYOUT.dir);
5282
5855
  return existsSync(logRoot) ? logRoot : null;
5283
5856
  }
5284
- function summarizeCurrentProfileLastLearningUpdateAt(activationRoot, learning) {
5857
+ function summarizeCurrentProfileLastLearningUpdateAt(activationRoot, learning, teacherLoop) {
5285
5858
  const updates = readLearningSpineLogEntries(activationRoot, "pgRouteUpdates");
5286
- return updates.at(-1)?.recordedAt ?? learning.lastMaterializedAt ?? null;
5859
+ return updates.at(-1)?.recordedAt ?? teacherLoop.lastRunAt ?? learning.lastMaterializedAt ?? null;
5287
5860
  }
5288
5861
  function summarizeCurrentProfileBrainSummary(input) {
5289
5862
  const packId = input.activePackId ?? "unknown";
5863
+ if (input.activationState === "broken_install") {
5864
+ return "Brain activation is broken and needs repair before serve-path truth can be trusted.";
5865
+ }
5866
+ if (input.activationState === "stale_incomplete") {
5867
+ return "Brain activation has retained stale/incomplete state and no serving active pack.";
5868
+ }
5290
5869
  if (!input.attached) {
5291
5870
  return "Brain is not attached to the current Profile.";
5292
5871
  }
@@ -5322,6 +5901,7 @@ function summarizeCurrentProfileBrainStatusLevel(input) {
5322
5901
  function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId) {
5323
5902
  const attached = report.active !== null;
5324
5903
  const awaitingFirstExport = isAwaitingFirstExportSlot(report.active);
5904
+ const activationState = report.activation.state;
5325
5905
  const routerIdentity = report.servePath.routerIdentity ?? report.learnedRouting.routerIdentity ?? report.active?.routerIdentity ?? null;
5326
5906
  const routeFreshness = report.servePath.refreshStatus === "updated" || report.servePath.refreshStatus === "no_supervision"
5327
5907
  ? report.servePath.refreshStatus
@@ -5360,16 +5940,20 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5360
5940
  routerIdentity,
5361
5941
  routerChecksum: report.learnedRouting.routerChecksum,
5362
5942
  lastExportAt: report.supervision.exportedAt,
5363
- lastLearningUpdateAt: summarizeCurrentProfileLastLearningUpdateAt(report.activationRoot, report.learning),
5943
+ lastLearningUpdateAt: summarizeCurrentProfileLastLearningUpdateAt(report.activationRoot, report.learning, report.teacherLoop),
5364
5944
  lastPromotionAt: report.promotion.lastPromotion.at,
5365
5945
  summary: summarizeCurrentProfileBrainSummary({
5946
+ activationState,
5366
5947
  attached,
5367
5948
  serveState: report.servePath.state,
5368
5949
  brainState: report.brain.state,
5369
5950
  awaitingFirstExport,
5370
- activePackId
5951
+ activePackId,
5952
+ activationDetail: report.activation.detail
5371
5953
  }),
5372
- detail: report.brain.detail
5954
+ detail: activationState === "broken_install" || activationState === "stale_incomplete" || activationState === "detached"
5955
+ ? report.activation.detail
5956
+ : report.brain.detail
5373
5957
  },
5374
5958
  attachment: attached
5375
5959
  ? {
@@ -5398,21 +5982,28 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5398
5982
  status,
5399
5983
  brainState: report.brain.state,
5400
5984
  serveState: report.servePath.state,
5985
+ activationState,
5401
5986
  usedLearnedRouteFn: report.servePath.usedLearnedRouteFn,
5402
5987
  failOpen: report.servePath.fallbackToStaticContext,
5403
5988
  awaitingFirstExport,
5404
5989
  structuralDecision: report.servePath.structuralDecision,
5405
- detail: report.servePath.state === "serving_active_pack"
5406
- ? awaitingFirstExport
5407
- ? `current profile is serving seed-state pack ${activePackId ?? "unknown"} while awaiting the first exported turn`
5408
- : report.brain.state === "pg_promoted_pack_authoritative"
5409
- ? `current profile is serving promoted pack ${activePackId ?? "unknown"}; serve-visible change came from activation promotion, not hot-path mutation`
5410
- : `current profile is serving active pack ${activePackId ?? "unknown"}; learned routing is active, but authority is still seed-state`
5411
- : report.servePath.state === "fail_open_static_context"
5412
- ? "current profile would fail open to static context because no serving pack is available"
5413
- : report.servePath.state === "hard_fail"
5414
- ? "current profile cannot serve because the learned-route or activation requirement hard-failed"
5415
- : "current profile serve state has not been compile-probed yet"
5990
+ detail: activationState === "broken_install"
5991
+ ? `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"
5416
6007
  },
5417
6008
  currentTurnAttribution: null
5418
6009
  };
@@ -5421,62 +6012,100 @@ export function buildOperatorSurfaceReport(input) {
5421
6012
  const activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
5422
6013
  const updatedAt = normalizeIsoTimestamp(input.updatedAt, "updatedAt", new Date().toISOString());
5423
6014
  const brainAttachmentPolicy = normalizeBrainAttachmentPolicy(input.brainAttachmentPolicy);
5424
- const inspection = inspectActivationState(activationRoot, updatedAt);
5425
- const observability = describeActivationObservability(activationRoot, "active", {
5426
- updatedAt
6015
+ let inspection = null;
6016
+ let observability = null;
6017
+ let inspectionError = null;
6018
+ try {
6019
+ inspection = inspectActivationState(activationRoot, updatedAt);
6020
+ }
6021
+ catch (error) {
6022
+ inspectionError = toErrorMessage(error);
6023
+ }
6024
+ const active = inspection === null ? null : summarizeOperatorSlot(inspection.active, inspection.pointers.active?.updatedAt ?? null);
6025
+ const candidate = inspection === null ? null : summarizeOperatorSlot(inspection.candidate, inspection.pointers.candidate?.updatedAt ?? null);
6026
+ const previous = inspection === null ? null : summarizeOperatorSlot(inspection.previous, inspection.pointers.previous?.updatedAt ?? null);
6027
+ if (inspection !== null && inspection.active !== null) {
6028
+ try {
6029
+ observability = describeActivationObservability(activationRoot, "active", {
6030
+ updatedAt
6031
+ });
6032
+ }
6033
+ catch (error) {
6034
+ inspectionError ??= `activation observability failed: ${toErrorMessage(error)}`;
6035
+ }
6036
+ }
6037
+ const activation = summarizeOperatorActivationState({
6038
+ inspection,
6039
+ observability,
6040
+ active,
6041
+ candidate,
6042
+ previous,
6043
+ inspectionError
5427
6044
  });
5428
- const attachStatus = describeAttachStatus({ activationRoot });
5429
- const active = summarizeOperatorSlot(inspection.active, inspection.pointers.active?.updatedAt ?? null);
5430
- const activeObservability = summarizeActivePackObservability(activationRoot, active);
6045
+ const activeObservability = inspectionError === null
6046
+ ? summarizeActivePackObservability(activationRoot, active)
6047
+ : {
6048
+ labelFlow: buildMissingLabelFlowSummary(`activation observability is unavailable: ${inspectionError}`),
6049
+ learningPath: buildMissingLearningPathSummary(`activation observability is unavailable: ${inspectionError}`)
6050
+ };
6051
+ const servePath = probeOperatorServePath(activationRoot, observability, active?.packId ?? null);
5431
6052
  const reportBase = {
5432
6053
  generatedAt: updatedAt,
5433
6054
  activationRoot,
6055
+ activation,
5434
6056
  active,
5435
- candidate: summarizeOperatorSlot(inspection.candidate, inspection.pointers.candidate?.updatedAt ?? null),
5436
- previous: summarizeOperatorSlot(inspection.previous, inspection.pointers.previous?.updatedAt ?? null),
6057
+ candidate,
6058
+ previous,
5437
6059
  freshness: {
5438
- activeBehindPromotionReadyCandidate: observability.promotionFreshness.activeBehindPromotionReadyCandidate,
5439
- candidateAheadBy: summarizeCandidateAheadBy(observability.promotionFreshness.candidateAheadBy)
6060
+ activeBehindPromotionReadyCandidate: observability?.promotionFreshness.activeBehindPromotionReadyCandidate ?? false,
6061
+ candidateAheadBy: summarizeCandidateAheadBy(observability?.promotionFreshness.candidateAheadBy ?? null)
5440
6062
  },
5441
- brain: summarizeBrainState(active, observability),
5442
- graph: summarizeGraphObservability(active, observability),
6063
+ brain: observability === null ? summarizeBrainStateWithoutObservability(active, activation) : summarizeBrainState(active, observability),
6064
+ graph: observability === null ? summarizeGraphWithoutObservability(active, activation) : summarizeGraphObservability(active, observability),
5443
6065
  labelFlow: activeObservability.labelFlow,
5444
6066
  learningPath: activeObservability.learningPath,
5445
- learnedRouting: {
5446
- required: observability.learnedRouteFn.required,
5447
- available: observability.learnedRouteFn.available,
5448
- routerIdentity: observability.learnedRouteFn.routerIdentity,
5449
- routeFnVersion: observability.learnedRouteFn.routeFnVersion,
5450
- trainingMethod: observability.learnedRouteFn.trainingMethod,
5451
- routerTrainedAt: observability.learnedRouteFn.routerTrainedAt,
5452
- objective: observability.learnedRouteFn.objective,
5453
- pgProfile: observability.learnedRouteFn.pgProfile,
5454
- routerChecksum: observability.learnedRouteFn.routerChecksum,
5455
- objectiveChecksum: observability.learnedRouteFn.objectiveChecksum,
5456
- updateMechanism: observability.learnedRouteFn.updateMechanism,
5457
- updateVersion: observability.learnedRouteFn.updateVersion,
5458
- updateCount: observability.learnedRouteFn.updateCount,
5459
- supervisionCount: observability.learnedRouteFn.supervisionCount,
5460
- collectedLabelsTotal: observability.learnedRouteFn.collectedLabels?.total ?? null,
5461
- freshnessChecksum: observability.learnedRouteFn.freshnessChecksum,
5462
- handoffState: observability.initHandoff.handoffState,
5463
- initMode: observability.initHandoff.initMode,
5464
- seedStateVisible: observability.initHandoff.seedStateVisible
5465
- },
5466
- servePath: summarizeServePath(attachStatus.compile),
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
+ },
6090
+ servePath,
5467
6091
  promotion: {
5468
- allowed: inspection.promotion.allowed,
5469
- findings: [...inspection.promotion.findings],
5470
- lastPromotion: summarizeLastPromotion(inspection),
5471
- activeUpdatedAt: inspection.pointers.active?.updatedAt ?? null,
5472
- candidateUpdatedAt: inspection.pointers.candidate?.updatedAt ?? null,
5473
- previousUpdatedAt: inspection.pointers.previous?.updatedAt ?? null
6092
+ allowed: inspection?.promotion.allowed ?? false,
6093
+ findings: [...(inspection?.promotion.findings ?? [])],
6094
+ lastPromotion: inspection === null ? {
6095
+ known: false,
6096
+ at: null,
6097
+ confidence: "unknown_from_local_pointers",
6098
+ note: activation.detail
6099
+ } : summarizeLastPromotion(inspection),
6100
+ activeUpdatedAt: inspection?.pointers.active?.updatedAt ?? null,
6101
+ candidateUpdatedAt: inspection?.pointers.candidate?.updatedAt ?? null,
6102
+ previousUpdatedAt: inspection?.pointers.previous?.updatedAt ?? null
5474
6103
  },
5475
6104
  rollback: {
5476
- allowed: inspection.rollback.allowed,
5477
- findings: [...inspection.rollback.findings],
5478
- previousPackId: inspection.previous?.packId ?? inspection.pointers.previous?.packId ?? null,
5479
- state: inspection.rollback.allowed ? "ready" : inspection.active === null ? "unknown" : "blocked"
6105
+ allowed: inspection?.rollback.allowed ?? false,
6106
+ findings: [...(inspection?.rollback.findings ?? [])],
6107
+ previousPackId: inspection?.previous?.packId ?? inspection?.pointers.previous?.packId ?? null,
6108
+ state: inspection === null ? "unknown" : inspection.rollback.allowed ? "ready" : inspection.active === null ? "unknown" : "blocked"
5480
6109
  },
5481
6110
  supervision: summarizeSupervision(input),
5482
6111
  learning: summarizeAlwaysOnLearning(input, active),
@@ -5564,6 +6193,7 @@ export { describeActivationObservability, inspectActivationState, rollbackActive
5564
6193
  export { createOpenClawLocalSessionTail, OpenClawLocalSessionTail } from "./session-tail.js";
5565
6194
  export { discoverOpenClawMainSessionStores, discoverOpenClawSessionStores, loadOpenClawSessionIndex, readOpenClawAcpStreamFile, readOpenClawSessionFile } from "./session-store.js";
5566
6195
  export { buildPassiveLearningSessionExportFromOpenClawSessionStore, buildPassiveLearningStoreExportFromOpenClawSessionIndex } from "./local-session-passive-learning.js";
6196
+ export { DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_TIMEOUT_MS, OllamaClient, OllamaClientError, createOllamaClient } from "./ollama-client.js";
5567
6197
  export { resolveActivationRoot } from "./resolve-activation-root.js";
5568
6198
  export { runDaemonCommand, parseDaemonArgs } from "./daemon.js";
5569
6199
  //# sourceMappingURL=index.js.map