@openclawbrain/openclaw 0.2.2 → 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 (47) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +10 -0
  3. package/dist/extension/index.d.ts +1 -0
  4. package/dist/extension/index.js +73 -0
  5. package/dist/extension/index.js.map +1 -0
  6. package/dist/extension/runtime-guard.d.ts +61 -0
  7. package/dist/extension/runtime-guard.js +230 -0
  8. package/dist/extension/runtime-guard.js.map +1 -0
  9. package/dist/src/cli.d.ts +66 -4
  10. package/dist/src/cli.js +1845 -241
  11. package/dist/src/cli.js.map +1 -1
  12. package/dist/src/daemon.d.ts +7 -4
  13. package/dist/src/daemon.js +311 -28
  14. package/dist/src/daemon.js.map +1 -1
  15. package/dist/src/index.d.ts +213 -4
  16. package/dist/src/index.js +1151 -157
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/learning-spine.d.ts +2 -1
  19. package/dist/src/learning-spine.js +8 -0
  20. package/dist/src/learning-spine.js.map +1 -1
  21. package/dist/src/local-session-passive-learning.d.ts +1 -0
  22. package/dist/src/local-session-passive-learning.js +97 -7
  23. package/dist/src/local-session-passive-learning.js.map +1 -1
  24. package/dist/src/ollama-client.d.ts +46 -0
  25. package/dist/src/ollama-client.js +231 -0
  26. package/dist/src/ollama-client.js.map +1 -0
  27. package/dist/src/provider-config.d.ts +28 -0
  28. package/dist/src/provider-config.js +150 -0
  29. package/dist/src/provider-config.js.map +1 -0
  30. package/dist/src/resolve-activation-root.d.ts +3 -3
  31. package/dist/src/resolve-activation-root.js +105 -35
  32. package/dist/src/resolve-activation-root.js.map +1 -1
  33. package/dist/src/session-store.d.ts +18 -0
  34. package/dist/src/session-store.js +40 -0
  35. package/dist/src/session-store.js.map +1 -1
  36. package/dist/src/session-tail.d.ts +6 -3
  37. package/dist/src/session-tail.js +35 -4
  38. package/dist/src/session-tail.js.map +1 -1
  39. package/dist/src/shadow-extension-proof.d.ts +40 -0
  40. package/dist/src/shadow-extension-proof.js +214 -0
  41. package/dist/src/shadow-extension-proof.js.map +1 -0
  42. package/dist/src/teacher-labeler.d.ts +50 -0
  43. package/dist/src/teacher-labeler.js +424 -0
  44. package/dist/src/teacher-labeler.js.map +1 -0
  45. package/extension/index.ts +74 -35
  46. package/extension/runtime-guard.ts +353 -0
  47. package/package.json +13 -13
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
  }
@@ -229,6 +233,22 @@ function cloneAlwaysOnLearningMaterializationJobOrNull(value) {
229
233
  function cloneTeacherSupervisionArtifacts(value) {
230
234
  return [...structuredClone(value)];
231
235
  }
236
+ function cloneAsyncTeacherSnapshotState(value) {
237
+ if (value === undefined) {
238
+ return undefined;
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 : [];
243
+ return {
244
+ interactionEvents: [...structuredClone(interactionEvents)],
245
+ feedbackEvents: [...structuredClone(feedbackEvents)],
246
+ seenExportDigests: [...seenExportDigests]
247
+ };
248
+ }
249
+ function cloneAsyncTeacherSnapshotRuntime(value) {
250
+ return value === undefined ? undefined : { ...value };
251
+ }
232
252
  function cloneCanonicalSupervision(value) {
233
253
  return structuredClone(value);
234
254
  }
@@ -508,6 +528,7 @@ export class AsyncTeacherLiveLoop {
508
528
  input;
509
529
  queueCapacity;
510
530
  staleAfterMs;
531
+ teacherLabeler;
511
532
  queuedExportDigests = new Set();
512
533
  seenExportDigests = new Set();
513
534
  queue = [];
@@ -517,6 +538,7 @@ export class AsyncTeacherLiveLoop {
517
538
  teacherArtifacts = [];
518
539
  learnerState = createAlwaysOnLearningRuntimeState();
519
540
  lastMaterialization = null;
541
+ lastTeacherLabelerResult = null;
520
542
  diagnostics = {
521
543
  acceptedExportCount: 0,
522
544
  processedExportCount: 0,
@@ -535,19 +557,40 @@ export class AsyncTeacherLiveLoop {
535
557
  dedupedArtifactCount: 0,
536
558
  sparseFeedback: this.learnerState.sparseFeedback,
537
559
  noOpReason: "none",
538
- materialization: null
560
+ materialization: null,
561
+ teacherLabeler: null
539
562
  })
540
563
  };
541
564
  constructor(input) {
542
565
  this.input = input;
543
566
  this.queueCapacity = input.maxQueuedExports ?? DEFAULT_ASYNC_TEACHER_QUEUE_CAPACITY;
544
567
  this.staleAfterMs = input.staleAfterMs ?? DEFAULT_TEACHER_SUPERVISION_STALE_AFTER_MS;
568
+ this.teacherLabeler = createTeacherLabeler(input.teacherLabeler);
545
569
  if (!Number.isInteger(this.queueCapacity) || this.queueCapacity <= 0) {
546
570
  throw new Error("maxQueuedExports must be a positive integer");
547
571
  }
548
572
  if (!Number.isInteger(this.staleAfterMs) || this.staleAfterMs <= 0) {
549
573
  throw new Error("staleAfterMs must be a positive integer");
550
574
  }
575
+ const resumedSnapshot = input.resumeFromSnapshot;
576
+ if (resumedSnapshot !== undefined && resumedSnapshot !== null) {
577
+ if (resumedSnapshot.runtimeOwner !== "openclaw") {
578
+ throw new Error("async teacher resume snapshot runtimeOwner must be openclaw");
579
+ }
580
+ this.interactionEvents = [...structuredClone(resumedSnapshot.state?.interactionEvents ?? [])];
581
+ this.feedbackEvents = [...structuredClone(resumedSnapshot.state?.feedbackEvents ?? [])];
582
+ this.teacherArtifacts = cloneTeacherSupervisionArtifacts(resumedSnapshot.teacher.artifacts);
583
+ this.learnerState = structuredClone(resumedSnapshot.learner.state);
584
+ this.lastMaterialization = cloneAlwaysOnLearningMaterializationJobOrNull(resumedSnapshot.learner.lastMaterialization);
585
+ this.diagnostics = {
586
+ ...structuredClone(resumedSnapshot.diagnostics),
587
+ notes: [...resumedSnapshot.diagnostics.notes]
588
+ };
589
+ for (const exportDigest of resumedSnapshot.state?.seenExportDigests ?? []) {
590
+ this.seenExportDigests.add(exportDigest);
591
+ }
592
+ this.refreshNotes();
593
+ }
551
594
  }
552
595
  enqueueNormalizedEventExport(normalizedEventExport, options = {}) {
553
596
  const validationErrors = validateNormalizedEventExport(normalizedEventExport);
@@ -589,7 +632,9 @@ export class AsyncTeacherLiveLoop {
589
632
  this.queuedExportDigests.add(exportDigest);
590
633
  this.diagnostics.acceptedExportCount += 1;
591
634
  this.refreshNotes();
592
- 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
+ });
593
638
  return {
594
639
  accepted: true,
595
640
  exportDigest,
@@ -745,6 +790,11 @@ export class AsyncTeacherLiveLoop {
745
790
  diagnostics: {
746
791
  ...this.diagnostics,
747
792
  notes: [...this.diagnostics.notes]
793
+ },
794
+ state: {
795
+ interactionEvents: [...structuredClone(this.interactionEvents)],
796
+ feedbackEvents: [...structuredClone(this.feedbackEvents)],
797
+ seenExportDigests: [...this.seenExportDigests].sort()
748
798
  }
749
799
  };
750
800
  }
@@ -763,49 +813,106 @@ export class AsyncTeacherLiveLoop {
763
813
  while (this.queue.length > 0) {
764
814
  const job = this.queue.shift();
765
815
  this.queuedExportDigests.delete(job.exportDigest);
766
- this.seenExportDigests.add(job.exportDigest);
767
- this.interactionEvents = mergeUniqueEvents(this.interactionEvents, job.normalizedEventExport.interactionEvents);
768
- this.feedbackEvents = mergeUniqueEvents(this.feedbackEvents, job.normalizedEventExport.feedbackEvents);
769
- const mergedNormalizedEventExport = buildNormalizedEventExport({
770
- interactionEvents: this.interactionEvents,
771
- feedbackEvents: this.feedbackEvents
772
- });
773
- const builtArtifacts = buildTeacherSupervisionArtifactsFromNormalizedEventExport({
774
- normalizedEventExport: mergedNormalizedEventExport,
775
- observedAt: job.observedAt,
776
- staleAfterMs: this.staleAfterMs,
777
- ...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {})
778
- });
779
- const currentDedupIds = new Set(this.teacherArtifacts.map((artifact) => artifact.dedupId));
780
- const nextTeacherArtifacts = mergeTeacherArtifacts(this.teacherArtifacts, builtArtifacts);
781
- const emittedArtifactCount = builtArtifacts.filter((artifact) => !currentDedupIds.has(artifact.dedupId)).length;
782
- const dedupedArtifactCount = builtArtifacts.length - emittedArtifactCount;
783
- this.teacherArtifacts = nextTeacherArtifacts;
784
- const learnerResult = advanceAlwaysOnLearningRuntime({
785
- packLabel: this.input.packLabel,
786
- workspace: this.input.workspace,
787
- interactionEvents: this.interactionEvents,
788
- feedbackEvents: this.feedbackEvents,
789
- teacherSupervisionArtifacts: this.teacherArtifacts,
790
- learnedRouting: this.input.learnedRouting,
791
- state: this.learnerState,
792
- builtAt: this.input.builtAt ?? job.observedAt,
793
- ...(this.input.offlineArtifacts !== undefined ? { offlineArtifacts: this.input.offlineArtifacts } : {}),
794
- ...(this.input.structuralOps !== undefined ? { structuralOps: this.input.structuralOps } : {}),
795
- ...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {}),
796
- ...(this.input.liveSliceSize !== undefined ? { liveSliceSize: this.input.liveSliceSize } : {}),
797
- ...(this.input.backfillSliceSize !== undefined ? { backfillSliceSize: this.input.backfillSliceSize } : {}),
798
- ...(this.input.cadence !== undefined ? { cadence: this.input.cadence } : {})
799
- });
800
- this.learnerState = structuredClone(learnerResult.state);
801
- this.lastMaterialization = cloneAlwaysOnLearningMaterializationJobOrNull(learnerResult.materialization);
802
- this.diagnostics.processedExportCount += 1;
803
- this.diagnostics.emittedArtifactCount += emittedArtifactCount;
804
- this.diagnostics.dedupedArtifactCount += dedupedArtifactCount;
805
- this.diagnostics.lastProcessedAt = job.observedAt;
806
- this.diagnostics.latestFreshness = latestTeacherFreshness(this.teacherArtifacts);
807
- this.diagnostics.lastNoOpReason = emittedArtifactCount === 0 ? "no_teacher_artifacts" : "none";
808
- this.refreshNotes();
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;
915
+ }
809
916
  }
810
917
  }
811
918
  refreshNotes() {
@@ -817,7 +924,8 @@ export class AsyncTeacherLiveLoop {
817
924
  dedupedArtifactCount: this.diagnostics.dedupedArtifactCount,
818
925
  sparseFeedback: this.learnerState.sparseFeedback,
819
926
  noOpReason: this.diagnostics.lastNoOpReason,
820
- materialization: this.lastMaterialization
927
+ materialization: this.lastMaterialization,
928
+ teacherLabeler: this.lastTeacherLabelerResult
821
929
  });
822
930
  }
823
931
  }
@@ -923,22 +1031,331 @@ export function scanLiveEventExport(input) {
923
1031
  dedupedArtifactCount: 0,
924
1032
  sparseFeedback: learnerResult.state.sparseFeedback,
925
1033
  noOpReason: lastNoOpReason,
926
- materialization: learnerResult.materialization
1034
+ materialization: learnerResult.materialization,
1035
+ teacherLabeler: null
927
1036
  })
928
1037
  }
929
1038
  };
1039
+ const labelFlow = summarizeNormalizedEventExportLabelFlow(normalizedEventExport, teacherArtifacts.length);
1040
+ const learningPath = summarizeLearningPathFromMaterialization(learnerResult.materialization);
930
1041
  return {
931
1042
  runtimeOwner: "openclaw",
932
1043
  scanMode: "live",
933
1044
  observedAt,
934
1045
  packLabel,
935
1046
  supervision: buildCanonicalSupervision(normalizedEventExport),
936
- snapshot
1047
+ snapshot,
1048
+ labelFlow,
1049
+ learningPath
937
1050
  };
938
1051
  }
939
1052
  function readJsonFile(filePath) {
940
1053
  return JSON.parse(readFileSync(filePath, "utf8"));
941
1054
  }
1055
+ export function resolveAsyncTeacherLiveLoopSnapshotPath(activationRoot) {
1056
+ return path.join(path.resolve(normalizeNonEmptyString(activationRoot, "activationRoot")), "async-teacher-live-loop.snapshot.json");
1057
+ }
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) {
1322
+ if (snapshot.runtimeOwner !== "openclaw") {
1323
+ throw new Error("async teacher snapshot runtimeOwner must be openclaw");
1324
+ }
1325
+ const cloned = {
1326
+ ...snapshot,
1327
+ diagnostics: {
1328
+ ...snapshot.diagnostics,
1329
+ notes: [...snapshot.diagnostics.notes]
1330
+ },
1331
+ teacher: {
1332
+ ...snapshot.teacher,
1333
+ artifacts: cloneTeacherSupervisionArtifacts(snapshot.teacher.artifacts)
1334
+ },
1335
+ learner: {
1336
+ state: structuredClone(snapshot.learner.state),
1337
+ lastMaterialization: cloneAlwaysOnLearningMaterializationJobOrNull(snapshot.learner.lastMaterialization)
1338
+ }
1339
+ };
1340
+ if (snapshot.state !== undefined) {
1341
+ cloned.state = cloneAsyncTeacherSnapshotState(snapshot.state);
1342
+ }
1343
+ if (snapshot.runtime !== undefined) {
1344
+ cloned.runtime = cloneAsyncTeacherSnapshotRuntime(snapshot.runtime);
1345
+ }
1346
+ return cloned;
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
+ }
942
1359
  function resolveBundlePayloadPath(rootDir, payloadPath) {
943
1360
  const resolved = path.resolve(rootDir, payloadPath);
944
1361
  const relative = path.relative(rootDir, resolved);
@@ -1573,6 +1990,18 @@ export class RuntimeEventExportScanner {
1573
1990
  snapshot() {
1574
1991
  return structuredClone(this.checkpoint);
1575
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
+ }
1576
2005
  scanOnce(options = {}) {
1577
2006
  const scannedAt = normalizeIsoTimestamp(options.scannedAt, "scannedAt", new Date().toISOString());
1578
2007
  const discovered = discoverRuntimeEventExportBundles(this.scanRoot);
@@ -1770,6 +2199,15 @@ function normalizeIsoTimestamp(value, fieldName, fallbackValue) {
1770
2199
  function normalizeMode(value) {
1771
2200
  return value ?? "heuristic";
1772
2201
  }
2202
+ function normalizeCompileSelectionMode(value) {
2203
+ if (value === undefined) {
2204
+ return undefined;
2205
+ }
2206
+ if (value === "flat_rank_v1" || value === "graph_walk_v1") {
2207
+ return value;
2208
+ }
2209
+ throw new Error(`selectionMode must be flat_rank_v1 or graph_walk_v1, received ${String(value)}`);
2210
+ }
1773
2211
  function normalizeRuntimeHints(value) {
1774
2212
  if (value === undefined) {
1775
2213
  return [];
@@ -1955,6 +2393,9 @@ export function formatPromptContext(compileResponse) {
1955
2393
  lines.push("[/BRAIN_CONTEXT]");
1956
2394
  return `${lines.join("\n")}\n`;
1957
2395
  }
2396
+ function resolveActivationRootForFailure(value) {
2397
+ return path.resolve(normalizeOptionalString(value) ?? ".");
2398
+ }
1958
2399
  function failOpenCompileResult(error, activationRoot) {
1959
2400
  return {
1960
2401
  ok: false,
@@ -1990,6 +2431,96 @@ function classifyCompileFailure(error, activationRoot) {
1990
2431
  function uniqueNotes(notes) {
1991
2432
  return [...new Set(notes.filter((note) => note.length > 0))];
1992
2433
  }
2434
+ function buildServeRouteLogFailOpenWarning(scope, error) {
2435
+ return `learning spine serve route log failed open (${scope}): ${toErrorMessage(error)}`;
2436
+ }
2437
+ function buildServeRouteLogFailOpenNotes(scope, error) {
2438
+ return [
2439
+ "serve_route_log_status=fail_open",
2440
+ `serve_route_log_scope=${scope}`,
2441
+ `serve_route_log_error=${toErrorMessage(error)}`
2442
+ ];
2443
+ }
2444
+ function normalizeServeRouteChannel(value) {
2445
+ if (typeof value !== "string") {
2446
+ return undefined;
2447
+ }
2448
+ const trimmed = value.trim();
2449
+ return trimmed.length > 0 ? trimmed : undefined;
2450
+ }
2451
+ function normalizeServeRouteMessage(value) {
2452
+ return typeof value === "string" ? value.trim() : "";
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
+ }
2479
+ function appendCompileServeRouteDecisionLog(input) {
2480
+ if (input.compileInput._suppressServeLog) {
2481
+ return;
2482
+ }
2483
+ const recordedAt = new Date().toISOString();
2484
+ const sessionId = normalizeServeRouteChannel(input.compileInput.sessionId) ?? `ext-compile-${Date.now()}`;
2485
+ const channel = normalizeServeRouteChannel(input.compileInput.channel) ?? "extension";
2486
+ const syntheticTurn = {
2487
+ sessionId,
2488
+ channel,
2489
+ userMessage: input.userMessage,
2490
+ createdAt: recordedAt
2491
+ };
2492
+ if (input.compileInput.maxContextBlocks !== undefined) {
2493
+ syntheticTurn.maxContextBlocks = input.compileInput.maxContextBlocks;
2494
+ }
2495
+ if (input.compileInput.budgetStrategy === "fixed_v1" || input.compileInput.budgetStrategy === "empirical_v1") {
2496
+ syntheticTurn.budgetStrategy = input.compileInput.budgetStrategy;
2497
+ }
2498
+ if (input.compileInput.mode === "heuristic" || input.compileInput.mode === "learned") {
2499
+ syntheticTurn.mode = input.compileInput.mode;
2500
+ }
2501
+ if (input.compileInput.runtimeHints !== undefined) {
2502
+ syntheticTurn.runtimeHints = input.compileInput.runtimeHints;
2503
+ }
2504
+ try {
2505
+ appendServeTimeRouteDecisionLog({
2506
+ activationRoot: input.activationRoot,
2507
+ turn: syntheticTurn,
2508
+ compileResult: input.compileResult,
2509
+ recordedAt,
2510
+ breadcrumbs: buildCompileServeRouteBreadcrumbs(input.compileInput)
2511
+ });
2512
+ }
2513
+ catch (error) {
2514
+ if (input.compileResult.ok) {
2515
+ input.compileResult.compileResponse.diagnostics.notes = uniqueNotes([
2516
+ ...input.compileResult.compileResponse.diagnostics.notes,
2517
+ ...buildServeRouteLogFailOpenNotes("compileRuntimeContext", error)
2518
+ ]);
2519
+ }
2520
+ console.warn(`[openclawbrain] ${buildServeRouteLogFailOpenWarning("compileRuntimeContext", error)} ` +
2521
+ `(activationRoot=${input.activationRoot}, sessionId=${sessionId}, channel=${channel})`);
2522
+ }
2523
+ }
1993
2524
  function roundMetric(value) {
1994
2525
  return Math.round(value * 100) / 100;
1995
2526
  }
@@ -2220,24 +2751,52 @@ export function resolveActivePackForCompile(activationRoot) {
2220
2751
  };
2221
2752
  }
2222
2753
  export function compileRuntimeContext(input) {
2223
- const activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
2224
- const agentId = normalizeOptionalString(input.agentId) ?? process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
2225
- const runtimeHints = normalizeRuntimeHints(input.runtimeHints);
2754
+ const fallbackActivationRoot = resolveActivationRootForFailure(input.activationRoot);
2755
+ let activationRoot = fallbackActivationRoot;
2756
+ let agentId = process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
2757
+ let runtimeHints = [];
2758
+ let selectionMode;
2759
+ let userMessage = "";
2760
+ let maxContextChars;
2761
+ let mode = "heuristic";
2762
+ let result;
2763
+ try {
2764
+ activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
2765
+ agentId = normalizeOptionalString(input.agentId) ?? process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
2766
+ runtimeHints = normalizeRuntimeHints(input.runtimeHints);
2767
+ selectionMode = normalizeCompileSelectionMode(input.selectionMode);
2768
+ userMessage = normalizeNonEmptyString(input.message, "message");
2769
+ maxContextChars =
2770
+ input.maxContextChars !== undefined
2771
+ ? normalizeNonNegativeInteger(input.maxContextChars, "maxContextChars", input.maxContextChars)
2772
+ : undefined;
2773
+ mode = normalizeMode(input.mode);
2774
+ }
2775
+ catch (error) {
2776
+ result = failOpenCompileResult(error, fallbackActivationRoot);
2777
+ appendCompileServeRouteDecisionLog({
2778
+ compileInput: input,
2779
+ activationRoot: result.activationRoot,
2780
+ compileResult: result,
2781
+ userMessage: normalizeServeRouteMessage(input.message)
2782
+ });
2783
+ return result;
2784
+ }
2226
2785
  try {
2227
2786
  const target = resolveActivePackForCompile(activationRoot);
2228
2787
  const resolvedBudget = resolveCompileBudget(target, input);
2229
2788
  const compile = compileRuntimeFromActivation(activationRoot, {
2230
2789
  contract: CONTRACT_IDS.runtimeCompile,
2231
2790
  agentId,
2232
- userMessage: normalizeNonEmptyString(input.message, "message"),
2791
+ userMessage,
2233
2792
  maxContextBlocks: resolvedBudget.maxContextBlocks,
2234
- ...(input.maxContextChars !== undefined
2235
- ? { maxContextChars: normalizeNonNegativeInteger(input.maxContextChars, "maxContextChars", input.maxContextChars) }
2236
- : {}),
2237
- modeRequested: normalizeMode(input.mode),
2793
+ ...(maxContextChars !== undefined ? { maxContextChars } : {}),
2794
+ modeRequested: mode,
2238
2795
  activePackId: target.activePointer.packId,
2239
2796
  ...(input.compactionMode !== undefined ? { compactionMode: input.compactionMode } : {}),
2240
2797
  ...(runtimeHints.length > 0 ? { runtimeHints } : {})
2798
+ }, {
2799
+ ...(selectionMode !== undefined ? { selectionMode } : {})
2241
2800
  });
2242
2801
  const compileResponse = {
2243
2802
  ...compile.response,
@@ -2246,7 +2805,7 @@ export function compileRuntimeContext(input) {
2246
2805
  notes: uniqueNotes([...compile.response.diagnostics.notes, ...resolvedBudget.notes, "OpenClaw remains the runtime owner"])
2247
2806
  }
2248
2807
  };
2249
- return {
2808
+ result = {
2250
2809
  ok: true,
2251
2810
  fallbackToStaticContext: false,
2252
2811
  hardRequirementViolated: false,
@@ -2258,8 +2817,15 @@ export function compileRuntimeContext(input) {
2258
2817
  };
2259
2818
  }
2260
2819
  catch (error) {
2261
- return classifyCompileFailure(error, activationRoot);
2820
+ result = classifyCompileFailure(error, activationRoot);
2262
2821
  }
2822
+ appendCompileServeRouteDecisionLog({
2823
+ compileInput: input,
2824
+ activationRoot: result.activationRoot,
2825
+ compileResult: result,
2826
+ userMessage
2827
+ });
2828
+ return result;
2263
2829
  }
2264
2830
  function readDiagnosticNoteValue(notes, prefix) {
2265
2831
  const note = notes.find((entry) => entry.startsWith(prefix));
@@ -3082,29 +3648,35 @@ export function writeScannedEventExportBundle(input) {
3082
3648
  });
3083
3649
  }
3084
3650
  export function runRuntimeTurn(turn, options = {}) {
3651
+ const warnings = [];
3652
+ const serveRouteBreadcrumbs = buildRunRuntimeTurnServeRouteBreadcrumbs();
3085
3653
  const agentId = normalizeOptionalString(turn.agentId);
3086
3654
  const compileInput = {
3087
- activationRoot: options.activationRoot ?? normalizeNonEmptyString(turn.activationRoot ?? undefined, "activationRoot"),
3088
- message: normalizeNonEmptyString(turn.userMessage, "userMessage"),
3655
+ activationRoot: (options.activationRoot ?? turn.activationRoot),
3656
+ message: turn.userMessage,
3089
3657
  ...(agentId !== undefined ? { agentId } : {}),
3090
3658
  ...(turn.maxContextBlocks !== undefined ? { maxContextBlocks: turn.maxContextBlocks } : {}),
3091
3659
  ...(turn.budgetStrategy !== undefined ? { budgetStrategy: turn.budgetStrategy } : {}),
3092
3660
  ...(turn.mode !== undefined ? { mode: turn.mode } : {}),
3093
- ...(turn.runtimeHints !== undefined ? { runtimeHints: turn.runtimeHints } : {})
3661
+ ...(turn.selectionMode !== undefined ? { selectionMode: turn.selectionMode } : {}),
3662
+ ...(turn.runtimeHints !== undefined ? { runtimeHints: turn.runtimeHints } : {}),
3663
+ _suppressServeLog: true
3094
3664
  };
3095
3665
  const compileResult = compileRuntimeContext(compileInput);
3096
- const warnings = [];
3097
3666
  const serveLoggedAt = turn.compile?.createdAt ?? turn.createdAt ?? new Date().toISOString();
3098
3667
  if (!compileResult.ok && compileResult.hardRequirementViolated) {
3099
3668
  try {
3100
3669
  appendServeTimeRouteDecisionLog({
3101
- activationRoot: compileInput.activationRoot,
3670
+ activationRoot: compileResult.activationRoot,
3102
3671
  turn,
3103
3672
  compileResult,
3104
- recordedAt: serveLoggedAt
3673
+ recordedAt: serveLoggedAt,
3674
+ breadcrumbs: serveRouteBreadcrumbs
3105
3675
  });
3106
3676
  }
3107
- catch {
3677
+ catch (error) {
3678
+ console.warn(`[openclawbrain] serve-time route decision log failed before hard-fail throw: ${toErrorMessage(error)} ` +
3679
+ `(activationRoot=${compileResult.activationRoot}, sessionId=${turn.sessionId}, channel=${turn.channel})`);
3108
3680
  }
3109
3681
  throw new Error(compileResult.error);
3110
3682
  }
@@ -3113,27 +3685,48 @@ export function runRuntimeTurn(turn, options = {}) {
3113
3685
  try {
3114
3686
  const compileEvent = normalizedEventExport.interactionEvents.find((event) => event.kind === "memory_compiled");
3115
3687
  appendServeTimeRouteDecisionLog({
3116
- activationRoot: compileInput.activationRoot,
3688
+ activationRoot: compileResult.activationRoot,
3117
3689
  turn,
3118
3690
  compileResult,
3119
3691
  normalizedEventExport,
3120
- recordedAt: compileEvent?.createdAt ?? serveLoggedAt
3692
+ recordedAt: compileEvent?.createdAt ?? serveLoggedAt,
3693
+ breadcrumbs: serveRouteBreadcrumbs
3121
3694
  });
3122
3695
  }
3123
3696
  catch (error) {
3124
- warnings.push(`learning spine serve log failed: ${toErrorMessage(error)}`);
3697
+ warnings.push(buildServeRouteLogFailOpenWarning("runRuntimeTurn", error));
3698
+ if (compileResult.ok) {
3699
+ compileResult.compileResponse.diagnostics.notes = uniqueNotes([
3700
+ ...compileResult.compileResponse.diagnostics.notes,
3701
+ ...buildServeRouteLogFailOpenNotes("runRuntimeTurn", error)
3702
+ ]);
3703
+ }
3704
+ }
3705
+ try {
3706
+ const eventExport = writeRuntimeEventExportBundle(turn, normalizedEventExport);
3707
+ return {
3708
+ ...compileResult,
3709
+ eventExport,
3710
+ warnings
3711
+ };
3712
+ }
3713
+ catch (error) {
3714
+ if (options.failOpen === false) {
3715
+ throw error;
3716
+ }
3717
+ warnings.push(toErrorMessage(error));
3718
+ return {
3719
+ ...compileResult,
3720
+ eventExport: {
3721
+ ok: false,
3722
+ wroteBundle: false,
3723
+ error: toErrorMessage(error)
3724
+ },
3725
+ warnings
3726
+ };
3125
3727
  }
3126
- const eventExport = writeRuntimeEventExportBundle(turn, normalizedEventExport);
3127
- return {
3128
- ...compileResult,
3129
- eventExport,
3130
- warnings
3131
- };
3132
3728
  }
3133
3729
  catch (error) {
3134
- if (options.failOpen === false) {
3135
- throw error;
3136
- }
3137
3730
  warnings.push(toErrorMessage(error));
3138
3731
  return {
3139
3732
  ...compileResult,
@@ -4117,6 +4710,144 @@ function summarizeOperatorSlot(slot, updatedAt) {
4117
4710
  findings: [...slot.findings]
4118
4711
  };
4119
4712
  }
4713
+ function buildMissingLabelFlowSummary(detail) {
4714
+ return {
4715
+ source: "missing",
4716
+ humanLabelCount: null,
4717
+ selfLabelCount: null,
4718
+ asyncTeacherArtifactCount: null,
4719
+ implicitPositiveCount: null,
4720
+ detail
4721
+ };
4722
+ }
4723
+ function buildMissingLearningPathSummary(detail) {
4724
+ return {
4725
+ available: false,
4726
+ source: "missing",
4727
+ policyGradientVersion: "unavailable",
4728
+ policyGradientMethod: null,
4729
+ objective: null,
4730
+ targetConstruction: null,
4731
+ connectOpsFired: null,
4732
+ reconstructedTrajectoryCount: null,
4733
+ detail
4734
+ };
4735
+ }
4736
+ function countTeacherArtifactBlocks(blocks) {
4737
+ return blocks.filter((block) => block.learning.role === "teacher_supervision" && !block.id.endsWith(":teacher-supervision-summary")).length;
4738
+ }
4739
+ function summarizePolicyGradientVersion(targetConstruction) {
4740
+ if (targetConstruction === "trajectory_reconstruction") {
4741
+ return "v2";
4742
+ }
4743
+ if (targetConstruction === "event_block_plus_related_interaction") {
4744
+ return "v1";
4745
+ }
4746
+ return "unavailable";
4747
+ }
4748
+ function summarizePackLabelFlow(source, pack) {
4749
+ const labelHarvest = pack.manifest.provenance.learningSurface.labelHarvest;
4750
+ return {
4751
+ source,
4752
+ humanLabelCount: labelHarvest.humanLabels,
4753
+ selfLabelCount: labelHarvest.selfLabels,
4754
+ asyncTeacherArtifactCount: countTeacherArtifactBlocks(pack.graph.blocks),
4755
+ implicitPositiveCount: labelHarvest.approvals,
4756
+ detail: source === "active_pack"
4757
+ ? "active pack label harvest and teacher artifacts are visible"
4758
+ : source === "materialized_candidate"
4759
+ ? "materialized candidate label harvest and teacher artifacts are visible"
4760
+ : "event export label harvest is visible"
4761
+ };
4762
+ }
4763
+ function summarizePackLearningPath(source, pack) {
4764
+ const targetConstruction = pack.router?.training.objective.profile.targetConstruction ?? null;
4765
+ const policyGradientVersion = summarizePolicyGradientVersion(targetConstruction);
4766
+ const reconstructedTrajectoryCount = policyGradientVersion === "v2"
4767
+ ? pack.router?.training.routeTraceCount ?? 0
4768
+ : pack.router === null
4769
+ ? null
4770
+ : 0;
4771
+ return {
4772
+ available: pack.router !== null,
4773
+ source,
4774
+ policyGradientVersion,
4775
+ policyGradientMethod: pack.router?.training.method ?? null,
4776
+ objective: pack.router?.training.objective.objective ?? null,
4777
+ targetConstruction,
4778
+ connectOpsFired: pack.manifest.graphDynamics.structuralOps.connect,
4779
+ reconstructedTrajectoryCount,
4780
+ detail: pack.router === null
4781
+ ? "pack has no learned router artifact"
4782
+ : policyGradientVersion === "v2"
4783
+ ? "learned routing uses trajectory-reconstruction PG"
4784
+ : policyGradientVersion === "v1"
4785
+ ? "learned routing uses event-reconstruction PG"
4786
+ : "learned routing is present but the PG profile is not recognizable"
4787
+ };
4788
+ }
4789
+ function summarizePackObservability(source, pack) {
4790
+ return {
4791
+ labelFlow: summarizePackLabelFlow(source, pack),
4792
+ learningPath: summarizePackLearningPath(source, pack)
4793
+ };
4794
+ }
4795
+ export function summarizeNormalizedEventExportLabelFlow(normalizedEventExport, asyncTeacherArtifactCount = 0) {
4796
+ const labelHarvest = normalizedEventExport.provenance.learningSurface.labelHarvest;
4797
+ return {
4798
+ source: "event_export",
4799
+ humanLabelCount: labelHarvest.humanLabels,
4800
+ selfLabelCount: labelHarvest.selfLabels,
4801
+ asyncTeacherArtifactCount,
4802
+ implicitPositiveCount: labelHarvest.approvals,
4803
+ detail: "event export label harvest is visible"
4804
+ };
4805
+ }
4806
+ export function summarizeLearningPathFromMaterialization(materialization) {
4807
+ if (materialization === null) {
4808
+ return buildMissingLearningPathSummary("no candidate pack materialized during this learning pass");
4809
+ }
4810
+ return summarizePackLearningPath("materialized_candidate", {
4811
+ manifest: materialization.candidate.manifest,
4812
+ graph: materialization.candidate.payloads.graph,
4813
+ router: materialization.candidate.payloads.router
4814
+ });
4815
+ }
4816
+ function summarizeActivePackObservability(activationRoot, active) {
4817
+ if (active === null) {
4818
+ return {
4819
+ labelFlow: buildMissingLabelFlowSummary("no active pack is attached"),
4820
+ learningPath: buildMissingLearningPathSummary("no active pack is attached")
4821
+ };
4822
+ }
4823
+ if (!active.activationReady) {
4824
+ return {
4825
+ labelFlow: buildMissingLabelFlowSummary(`active pack ${active.packId} is not activation-ready`),
4826
+ learningPath: buildMissingLearningPathSummary(`active pack ${active.packId} is not activation-ready`)
4827
+ };
4828
+ }
4829
+ try {
4830
+ const pack = loadPackFromActivation(activationRoot, "active", { requireActivationReady: true });
4831
+ if (pack === null) {
4832
+ return {
4833
+ labelFlow: buildMissingLabelFlowSummary("active pack payloads are unavailable"),
4834
+ learningPath: buildMissingLearningPathSummary("active pack payloads are unavailable")
4835
+ };
4836
+ }
4837
+ return summarizePackObservability("active_pack", {
4838
+ manifest: pack.manifest,
4839
+ graph: pack.graph,
4840
+ router: pack.router
4841
+ });
4842
+ }
4843
+ catch (error) {
4844
+ const detail = `active pack observability could not be loaded: ${toErrorMessage(error)}`;
4845
+ return {
4846
+ labelFlow: buildMissingLabelFlowSummary(detail),
4847
+ learningPath: buildMissingLearningPathSummary(detail)
4848
+ };
4849
+ }
4850
+ }
4120
4851
  function summarizeLastPromotion(inspection) {
4121
4852
  if (inspection.active === null) {
4122
4853
  return {
@@ -4183,9 +4914,137 @@ function summarizeManyProfileSupport(policyMode) {
4183
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."
4184
4915
  };
4185
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
+ }
4186
4927
  function isAwaitingFirstExportSlot(slot) {
4187
4928
  return slot !== null && slot.eventRange.count === 0;
4188
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
+ }
4189
5048
  function summarizeBrainState(active, observability) {
4190
5049
  if (active === null) {
4191
5050
  return {
@@ -4339,6 +5198,11 @@ function summarizeServePath(compile) {
4339
5198
  error: compile.error
4340
5199
  };
4341
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
+ }
4342
5206
  function loadOperatorEventExport(input) {
4343
5207
  const eventExportPath = normalizeOptionalString(input.eventExportPath);
4344
5208
  if (eventExportPath === undefined) {
@@ -4527,62 +5391,125 @@ function summarizeSupervision(input) {
4527
5391
  : "the supplied export does not yet show human supervision"
4528
5392
  };
4529
5393
  }
4530
- function loadTeacherSnapshot(input) {
5394
+ function loadTeacherSurfaceFromInput(input) {
4531
5395
  const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4532
5396
  if (teacherSnapshotPath === undefined) {
4533
5397
  return null;
4534
5398
  }
4535
- const snapshot = readJsonFile(path.resolve(teacherSnapshotPath));
4536
- if (snapshot.runtimeOwner !== "openclaw") {
4537
- throw new Error("teacher snapshot runtimeOwner must be openclaw");
4538
- }
4539
- return snapshot;
5399
+ return loadTeacherSurface(teacherSnapshotPath);
4540
5400
  }
4541
5401
  function summarizeTeacherLoop(input) {
4542
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4543
- if (teacherSnapshotPath === undefined) {
5402
+ const loaded = loadTeacherSurfaceFromInput(input);
5403
+ if (loaded === null && normalizeOptionalString(input.teacherSnapshotPath) === undefined) {
4544
5404
  return {
4545
5405
  available: false,
4546
5406
  sourcePath: null,
5407
+ sourceKind: "missing",
5408
+ lastRunAt: null,
4547
5409
  lastNoOpReason: "unavailable",
4548
5410
  latestFreshness: "unavailable",
5411
+ startedAt: null,
5412
+ lastHeartbeatAt: null,
5413
+ lastScanAt: null,
4549
5414
  lastProcessedAt: null,
5415
+ artifactCount: null,
4550
5416
  queueDepth: null,
4551
5417
  queueCapacity: null,
4552
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,
5432
+ lastAppliedMaterializationJobId: null,
4553
5433
  lastMaterializedPackId: null,
4554
5434
  notes: [],
4555
5435
  detail: "no teacher snapshot path supplied"
4556
5436
  };
4557
5437
  }
4558
- const snapshot = loadTeacherSnapshot(input);
4559
- if (snapshot === null) {
5438
+ if (loaded === null) {
5439
+ const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4560
5440
  return {
4561
5441
  available: false,
4562
- sourcePath: path.resolve(teacherSnapshotPath),
5442
+ sourcePath: teacherSnapshotPath === undefined ? null : path.resolve(teacherSnapshotPath),
5443
+ sourceKind: "missing",
5444
+ lastRunAt: null,
4563
5445
  lastNoOpReason: "unavailable",
4564
5446
  latestFreshness: "unavailable",
5447
+ startedAt: null,
5448
+ lastHeartbeatAt: null,
5449
+ lastScanAt: null,
4565
5450
  lastProcessedAt: null,
5451
+ artifactCount: null,
4566
5452
  queueDepth: null,
4567
5453
  queueCapacity: null,
4568
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,
5468
+ lastAppliedMaterializationJobId: null,
4569
5469
  lastMaterializedPackId: null,
4570
5470
  notes: [],
4571
5471
  detail: "teacher snapshot could not be loaded"
4572
5472
  };
4573
5473
  }
5474
+ const snapshot = loaded.snapshot;
5475
+ const watchSnapshot = loaded.watchSnapshot;
4574
5476
  return {
4575
5477
  available: true,
4576
- sourcePath: path.resolve(teacherSnapshotPath),
5478
+ sourcePath: loaded.sourcePath,
5479
+ sourceKind: loaded.sourceKind,
5480
+ lastRunAt: watchSnapshot?.lastRunAt ?? snapshot.runtime?.lastHeartbeatAt ?? snapshot.diagnostics.lastProcessedAt ?? null,
4577
5481
  lastNoOpReason: snapshot.diagnostics.lastNoOpReason,
4578
5482
  latestFreshness: snapshot.diagnostics.latestFreshness,
5483
+ startedAt: snapshot.runtime?.startedAt ?? null,
5484
+ lastHeartbeatAt: snapshot.runtime?.lastHeartbeatAt ?? null,
5485
+ lastScanAt: snapshot.runtime?.lastScanAt ?? null,
4579
5486
  lastProcessedAt: snapshot.diagnostics.lastProcessedAt,
5487
+ artifactCount: watchSnapshot?.teacher.artifactCount ?? snapshot.teacher.artifactCount,
4580
5488
  queueDepth: snapshot.queue.depth,
4581
5489
  queueCapacity: snapshot.queue.capacity,
4582
5490
  running: snapshot.queue.running,
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,
4583
5508
  lastMaterializedPackId: snapshot.learner.lastMaterialization?.candidate.summary.packId ?? null,
4584
5509
  notes: [...snapshot.diagnostics.notes],
4585
- detail: "async teacher diagnostics loaded"
5510
+ detail: loaded.sourceKind === "watch_snapshot"
5511
+ ? "canonical watch teacher snapshot loaded"
5512
+ : "raw async teacher snapshot loaded"
4586
5513
  };
4587
5514
  }
4588
5515
  function summarizeLearningBacklogState(plan, principalLagStatus) {
@@ -4639,8 +5566,8 @@ function summarizeAlwaysOnLearning(input, active) {
4639
5566
  sequenceLag: null,
4640
5567
  status: "unavailable"
4641
5568
  };
4642
- const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4643
- if (teacherSnapshotPath === undefined) {
5569
+ const loadedTeacherSurface = loadTeacherSurfaceFromInput(input);
5570
+ if (loadedTeacherSurface === null && normalizeOptionalString(input.teacherSnapshotPath) === undefined) {
4644
5571
  return {
4645
5572
  available: false,
4646
5573
  sourcePath: null,
@@ -4672,11 +5599,11 @@ function summarizeAlwaysOnLearning(input, active) {
4672
5599
  detail: "no teacher snapshot path supplied"
4673
5600
  };
4674
5601
  }
4675
- const snapshot = loadTeacherSnapshot(input);
4676
- if (snapshot === null) {
5602
+ if (loadedTeacherSurface === null) {
5603
+ const teacherSnapshotPath = normalizeOptionalString(input.teacherSnapshotPath);
4677
5604
  return {
4678
5605
  available: false,
4679
- sourcePath: path.resolve(teacherSnapshotPath),
5606
+ sourcePath: teacherSnapshotPath === undefined ? null : path.resolve(teacherSnapshotPath),
4680
5607
  bootstrapped: null,
4681
5608
  mode: "unavailable",
4682
5609
  nextPriorityLane: "unavailable",
@@ -4705,6 +5632,7 @@ function summarizeAlwaysOnLearning(input, active) {
4705
5632
  detail: "teacher snapshot could not be loaded"
4706
5633
  };
4707
5634
  }
5635
+ const snapshot = loadedTeacherSurface.snapshot;
4708
5636
  const plan = describeAlwaysOnLearningRuntimeState(snapshot.learner.state, snapshot.learner.lastMaterialization);
4709
5637
  const latestPrincipalSequence = plan.principalBacklog.checkpoints.reduce((latest, checkpoint) => {
4710
5638
  const candidate = checkpoint.newestPendingSequence ?? checkpoint.learnedThroughSequence;
@@ -4730,7 +5658,7 @@ function summarizeAlwaysOnLearning(input, active) {
4730
5658
  });
4731
5659
  return {
4732
5660
  available: true,
4733
- sourcePath: path.resolve(teacherSnapshotPath),
5661
+ sourcePath: loadedTeacherSurface.sourcePath,
4734
5662
  bootstrapped: plan.bootstrapped,
4735
5663
  mode: plan.mode,
4736
5664
  nextPriorityLane: plan.nextPriorityLane,
@@ -4779,7 +5707,13 @@ function buildOperatorFindings(report) {
4779
5707
  const push = (severity, code, summary, detail) => {
4780
5708
  findings.push({ severity, code, summary, detail });
4781
5709
  };
4782
- 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) {
4783
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");
4784
5718
  }
4785
5719
  else if (!report.active.activationReady) {
@@ -4920,12 +5854,18 @@ function summarizeCurrentProfileLogRoot(activationRoot) {
4920
5854
  const logRoot = path.join(path.resolve(activationRoot), LEARNING_SPINE_LOG_LAYOUT.dir);
4921
5855
  return existsSync(logRoot) ? logRoot : null;
4922
5856
  }
4923
- function summarizeCurrentProfileLastLearningUpdateAt(activationRoot, learning) {
5857
+ function summarizeCurrentProfileLastLearningUpdateAt(activationRoot, learning, teacherLoop) {
4924
5858
  const updates = readLearningSpineLogEntries(activationRoot, "pgRouteUpdates");
4925
- return updates.at(-1)?.recordedAt ?? learning.lastMaterializedAt ?? null;
5859
+ return updates.at(-1)?.recordedAt ?? teacherLoop.lastRunAt ?? learning.lastMaterializedAt ?? null;
4926
5860
  }
4927
5861
  function summarizeCurrentProfileBrainSummary(input) {
4928
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
+ }
4929
5869
  if (!input.attached) {
4930
5870
  return "Brain is not attached to the current Profile.";
4931
5871
  }
@@ -4961,6 +5901,7 @@ function summarizeCurrentProfileBrainStatusLevel(input) {
4961
5901
  function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId) {
4962
5902
  const attached = report.active !== null;
4963
5903
  const awaitingFirstExport = isAwaitingFirstExportSlot(report.active);
5904
+ const activationState = report.activation.state;
4964
5905
  const routerIdentity = report.servePath.routerIdentity ?? report.learnedRouting.routerIdentity ?? report.active?.routerIdentity ?? null;
4965
5906
  const routeFreshness = report.servePath.refreshStatus === "updated" || report.servePath.refreshStatus === "no_supervision"
4966
5907
  ? report.servePath.refreshStatus
@@ -4999,16 +5940,20 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
4999
5940
  routerIdentity,
5000
5941
  routerChecksum: report.learnedRouting.routerChecksum,
5001
5942
  lastExportAt: report.supervision.exportedAt,
5002
- lastLearningUpdateAt: summarizeCurrentProfileLastLearningUpdateAt(report.activationRoot, report.learning),
5943
+ lastLearningUpdateAt: summarizeCurrentProfileLastLearningUpdateAt(report.activationRoot, report.learning, report.teacherLoop),
5003
5944
  lastPromotionAt: report.promotion.lastPromotion.at,
5004
5945
  summary: summarizeCurrentProfileBrainSummary({
5946
+ activationState,
5005
5947
  attached,
5006
5948
  serveState: report.servePath.state,
5007
5949
  brainState: report.brain.state,
5008
5950
  awaitingFirstExport,
5009
- activePackId
5951
+ activePackId,
5952
+ activationDetail: report.activation.detail
5010
5953
  }),
5011
- detail: report.brain.detail
5954
+ detail: activationState === "broken_install" || activationState === "stale_incomplete" || activationState === "detached"
5955
+ ? report.activation.detail
5956
+ : report.brain.detail
5012
5957
  },
5013
5958
  attachment: attached
5014
5959
  ? {
@@ -5037,21 +5982,28 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
5037
5982
  status,
5038
5983
  brainState: report.brain.state,
5039
5984
  serveState: report.servePath.state,
5985
+ activationState,
5040
5986
  usedLearnedRouteFn: report.servePath.usedLearnedRouteFn,
5041
5987
  failOpen: report.servePath.fallbackToStaticContext,
5042
5988
  awaitingFirstExport,
5043
5989
  structuralDecision: report.servePath.structuralDecision,
5044
- detail: report.servePath.state === "serving_active_pack"
5045
- ? awaitingFirstExport
5046
- ? `current profile is serving seed-state pack ${activePackId ?? "unknown"} while awaiting the first exported turn`
5047
- : report.brain.state === "pg_promoted_pack_authoritative"
5048
- ? `current profile is serving promoted pack ${activePackId ?? "unknown"}; serve-visible change came from activation promotion, not hot-path mutation`
5049
- : `current profile is serving active pack ${activePackId ?? "unknown"}; learned routing is active, but authority is still seed-state`
5050
- : report.servePath.state === "fail_open_static_context"
5051
- ? "current profile would fail open to static context because no serving pack is available"
5052
- : report.servePath.state === "hard_fail"
5053
- ? "current profile cannot serve because the learned-route or activation requirement hard-failed"
5054
- : "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"
5055
6007
  },
5056
6008
  currentTurnAttribution: null
5057
6009
  };
@@ -5060,59 +6012,100 @@ export function buildOperatorSurfaceReport(input) {
5060
6012
  const activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
5061
6013
  const updatedAt = normalizeIsoTimestamp(input.updatedAt, "updatedAt", new Date().toISOString());
5062
6014
  const brainAttachmentPolicy = normalizeBrainAttachmentPolicy(input.brainAttachmentPolicy);
5063
- const inspection = inspectActivationState(activationRoot, updatedAt);
5064
- const observability = describeActivationObservability(activationRoot, "active", {
5065
- 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
5066
6044
  });
5067
- const attachStatus = describeAttachStatus({ activationRoot });
5068
- const active = summarizeOperatorSlot(inspection.active, inspection.pointers.active?.updatedAt ?? null);
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);
5069
6052
  const reportBase = {
5070
6053
  generatedAt: updatedAt,
5071
6054
  activationRoot,
6055
+ activation,
5072
6056
  active,
5073
- candidate: summarizeOperatorSlot(inspection.candidate, inspection.pointers.candidate?.updatedAt ?? null),
5074
- previous: summarizeOperatorSlot(inspection.previous, inspection.pointers.previous?.updatedAt ?? null),
6057
+ candidate,
6058
+ previous,
5075
6059
  freshness: {
5076
- activeBehindPromotionReadyCandidate: observability.promotionFreshness.activeBehindPromotionReadyCandidate,
5077
- candidateAheadBy: summarizeCandidateAheadBy(observability.promotionFreshness.candidateAheadBy)
6060
+ activeBehindPromotionReadyCandidate: observability?.promotionFreshness.activeBehindPromotionReadyCandidate ?? false,
6061
+ candidateAheadBy: summarizeCandidateAheadBy(observability?.promotionFreshness.candidateAheadBy ?? null)
5078
6062
  },
5079
- brain: summarizeBrainState(active, observability),
5080
- graph: summarizeGraphObservability(active, observability),
5081
- learnedRouting: {
5082
- required: observability.learnedRouteFn.required,
5083
- available: observability.learnedRouteFn.available,
5084
- routerIdentity: observability.learnedRouteFn.routerIdentity,
5085
- routeFnVersion: observability.learnedRouteFn.routeFnVersion,
5086
- trainingMethod: observability.learnedRouteFn.trainingMethod,
5087
- routerTrainedAt: observability.learnedRouteFn.routerTrainedAt,
5088
- objective: observability.learnedRouteFn.objective,
5089
- pgProfile: observability.learnedRouteFn.pgProfile,
5090
- routerChecksum: observability.learnedRouteFn.routerChecksum,
5091
- objectiveChecksum: observability.learnedRouteFn.objectiveChecksum,
5092
- updateMechanism: observability.learnedRouteFn.updateMechanism,
5093
- updateVersion: observability.learnedRouteFn.updateVersion,
5094
- updateCount: observability.learnedRouteFn.updateCount,
5095
- supervisionCount: observability.learnedRouteFn.supervisionCount,
5096
- collectedLabelsTotal: observability.learnedRouteFn.collectedLabels?.total ?? null,
5097
- freshnessChecksum: observability.learnedRouteFn.freshnessChecksum,
5098
- handoffState: observability.initHandoff.handoffState,
5099
- initMode: observability.initHandoff.initMode,
5100
- seedStateVisible: observability.initHandoff.seedStateVisible
5101
- },
5102
- servePath: summarizeServePath(attachStatus.compile),
6063
+ brain: observability === null ? summarizeBrainStateWithoutObservability(active, activation) : summarizeBrainState(active, observability),
6064
+ graph: observability === null ? summarizeGraphWithoutObservability(active, activation) : summarizeGraphObservability(active, observability),
6065
+ labelFlow: activeObservability.labelFlow,
6066
+ 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
+ },
6090
+ servePath,
5103
6091
  promotion: {
5104
- allowed: inspection.promotion.allowed,
5105
- findings: [...inspection.promotion.findings],
5106
- lastPromotion: summarizeLastPromotion(inspection),
5107
- activeUpdatedAt: inspection.pointers.active?.updatedAt ?? null,
5108
- candidateUpdatedAt: inspection.pointers.candidate?.updatedAt ?? null,
5109
- 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
5110
6103
  },
5111
6104
  rollback: {
5112
- allowed: inspection.rollback.allowed,
5113
- findings: [...inspection.rollback.findings],
5114
- previousPackId: inspection.previous?.packId ?? inspection.pointers.previous?.packId ?? null,
5115
- 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"
5116
6109
  },
5117
6110
  supervision: summarizeSupervision(input),
5118
6111
  learning: summarizeAlwaysOnLearning(input, active),
@@ -5198,8 +6191,9 @@ export { describeNormalizedEventExportObservability } from "@openclawbrain/event
5198
6191
  export { describeCompileFallbackUsage } from "@openclawbrain/compiler";
5199
6192
  export { describeActivationObservability, inspectActivationState, rollbackActivePack } from "@openclawbrain/pack-format";
5200
6193
  export { createOpenClawLocalSessionTail, OpenClawLocalSessionTail } from "./session-tail.js";
5201
- export { discoverOpenClawMainSessionStores, loadOpenClawSessionIndex, readOpenClawAcpStreamFile, readOpenClawSessionFile } from "./session-store.js";
6194
+ export { discoverOpenClawMainSessionStores, discoverOpenClawSessionStores, loadOpenClawSessionIndex, readOpenClawAcpStreamFile, readOpenClawSessionFile } from "./session-store.js";
5202
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";
5203
6197
  export { resolveActivationRoot } from "./resolve-activation-root.js";
5204
6198
  export { runDaemonCommand, parseDaemonArgs } from "./daemon.js";
5205
6199
  //# sourceMappingURL=index.js.map