@openclawbrain/cli 0.4.21 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.js +34 -9
- package/dist/src/index.d.ts +12 -1
- package/dist/src/index.js +78 -13
- package/dist/src/local-learner.js +9 -7
- package/dist/src/session-store.js +33 -2
- package/dist/src/session-tail.js +36 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -33,6 +33,11 @@ const OPENCLAWBRAIN_EMBEDDER_MODEL_ENV = "OPENCLAWBRAIN_EMBEDDER_MODEL";
|
|
|
33
33
|
const OPENCLAWBRAIN_INSTALL_SKIP_EMBEDDER_PROVISION_ENV = "OPENCLAWBRAIN_INSTALL_SKIP_EMBEDDER_PROVISION";
|
|
34
34
|
const LEGACY_COMPAT_PACKAGE_NAME = "@jonathangu/openclawbrain";
|
|
35
35
|
const INSTALL_COMPATIBLE_LOCAL_TEACHER_MODEL_PREFIXES = [
|
|
36
|
+
"unsloth-qwen3.5-27b:q4_k_m",
|
|
37
|
+
"unsloth-qwen3.5-27b",
|
|
38
|
+
"qwen3.5:32b",
|
|
39
|
+
"qwen3.5:27b",
|
|
40
|
+
"qwen3.5:14b",
|
|
36
41
|
"qwen3.5:9b",
|
|
37
42
|
"qwen3.5:8b",
|
|
38
43
|
"qwen3:8b",
|
|
@@ -813,10 +818,14 @@ function summarizeStatusHookFilesState(installHook) {
|
|
|
813
818
|
return "unverified";
|
|
814
819
|
}
|
|
815
820
|
function summarizeStatusAttachmentWatcher(status) {
|
|
816
|
-
|
|
821
|
+
const watchState = status.passiveLearning.watch?.state ?? status.passiveLearning.watchState;
|
|
822
|
+
if (watchState === "watching") {
|
|
817
823
|
return "alive";
|
|
818
824
|
}
|
|
819
|
-
if (
|
|
825
|
+
if (watchState === "lagging") {
|
|
826
|
+
return "lagging";
|
|
827
|
+
}
|
|
828
|
+
if (watchState === "stale_snapshot") {
|
|
820
829
|
return "stale";
|
|
821
830
|
}
|
|
822
831
|
return "not_visible";
|
|
@@ -1145,13 +1154,14 @@ function summarizeStatusTeacher(report, providerConfig, localLlm) {
|
|
|
1145
1154
|
detail: `${providerConfig.teacher.model} is enabled on Ollama, but no watch teacher snapshot is visible yet`
|
|
1146
1155
|
};
|
|
1147
1156
|
}
|
|
1148
|
-
const
|
|
1157
|
+
const watchState = report.teacherLoop.watch?.state ?? report.teacherLoop.watchState ?? "not_visible";
|
|
1158
|
+
const stale = watchState === "stale_snapshot" || (report.teacherLoop.latestFreshness === "stale" && report.teacherLoop.lastNoOpReason !== "no_teacher_artifacts");
|
|
1149
1159
|
const idle = report.teacherLoop.running === false &&
|
|
1150
1160
|
(report.teacherLoop.queueDepth ?? 0) === 0 &&
|
|
1151
1161
|
report.teacherLoop.failureMode === "none";
|
|
1152
1162
|
const healthy = report.teacherLoop.failureMode === "none" &&
|
|
1153
1163
|
stale === false &&
|
|
1154
|
-
|
|
1164
|
+
watchState === "watching";
|
|
1155
1165
|
const cycleDetail = report.teacherLoop.lastNoOpReason === "no_teacher_artifacts"
|
|
1156
1166
|
? summarizeTeacherNoArtifactCycle(report.teacherLoop.notes).detail
|
|
1157
1167
|
: TEACHER_NO_OP_MESSAGES[report.teacherLoop.lastNoOpReason] ?? "the latest teacher cycle detail is unavailable";
|
|
@@ -1175,7 +1185,9 @@ function summarizeStatusTeacher(report, providerConfig, localLlm) {
|
|
|
1175
1185
|
stale,
|
|
1176
1186
|
idle,
|
|
1177
1187
|
latestCycle,
|
|
1178
|
-
detail:
|
|
1188
|
+
detail: watchState === "lagging"
|
|
1189
|
+
? `${providerConfig.teacher.model} is enabled on Ollama, but the watch heartbeat is lagging; ${cycleDetail}`
|
|
1190
|
+
: `${providerConfig.teacher.model} is enabled on Ollama; ${cycleDetail}`
|
|
1179
1191
|
};
|
|
1180
1192
|
}
|
|
1181
1193
|
function summarizeStatusEmbedder(embeddings) {
|
|
@@ -1329,7 +1341,7 @@ function summarizeStatusAlerts(report, providerConfig, embeddings, localLlm) {
|
|
|
1329
1341
|
return buckets;
|
|
1330
1342
|
}
|
|
1331
1343
|
function summarizeStatusWatchState(status) {
|
|
1332
|
-
return status.passiveLearning.watchState;
|
|
1344
|
+
return status.passiveLearning.watch?.state ?? status.passiveLearning.watchState;
|
|
1333
1345
|
}
|
|
1334
1346
|
function summarizeStatusServeReality(status) {
|
|
1335
1347
|
if (status.brainStatus.serveState === "serving_active_pack") {
|
|
@@ -1478,7 +1490,7 @@ function formatCurrentProfileStatusSummary(status, report, targetInspection, opt
|
|
|
1478
1490
|
`budget requested=${report.servePath.requestedBudgetStrategy ?? "none"} resolved=${report.servePath.resolvedBudgetStrategy ?? "none"} maxBlocks=${report.servePath.resolvedMaxContextBlocks ?? "none"} source=${report.servePath.structuralBudgetSource ?? "none"} origin=${status.brainStatus.structuralDecision.origin} basis=${status.brainStatus.structuralDecision.basis}`,
|
|
1479
1491
|
`decision ${status.brainStatus.structuralDecision.detail}`,
|
|
1480
1492
|
`principal latest=${formatPrincipalLatest(report)} pending=${report.principal.pendingCount ?? report.learning.pendingPrincipalCount ?? "none"} checkpoint=${formatPrincipalCheckpointFrontier(report)} downstream=${yesNo(report.principal.servingDownstreamOfLatestCorrection)} lag=${report.learning.principalLagToPromotion.sequenceLag ?? "none"}`,
|
|
1481
|
-
`passive learner=${yesNo(status.passiveLearning.learnerRunning)} firstExport=${yesNo(status.passiveLearning.firstExportOccurred)} watch=${status
|
|
1493
|
+
`passive learner=${yesNo(status.passiveLearning.learnerRunning)} firstExport=${yesNo(status.passiveLearning.firstExportOccurred)} watch=${summarizeStatusWatchState(status)} export=${status.passiveLearning.exportState} backlog=${status.passiveLearning.backlogState} pending=${formatStatusNullableNumber(status.passiveLearning.pendingLive)}/${formatStatusNullableNumber(status.passiveLearning.pendingBackfill)} detail=${status.passiveLearning.detail}`,
|
|
1482
1494
|
`delta observed=${status.passiveLearning.lastObservedDelta.observedAt ?? "none"} exported=${formatStatusNullableYesNo(status.passiveLearning.lastObservedDelta.exported)} labeled=${formatStatusNullableYesNo(status.passiveLearning.lastObservedDelta.labeled)} promoted=${formatStatusNullableYesNo(status.passiveLearning.lastObservedDelta.promoted)} served=${formatStatusNullableYesNo(status.passiveLearning.lastObservedDelta.served)} transition=${formatStatusObservedDeltaTransition(status.passiveLearning.lastObservedDelta)} detail=${status.passiveLearning.lastObservedDelta.explanation}`,
|
|
1483
1495
|
`scanner flowing=${yesNo(report.supervision.flowing)} scan=${report.supervision.scanPolicy ?? "none"} surfaces=${formatScannerSurfaces(report)} labels=${report.supervision.humanLabelCount ?? "none"}/${report.supervision.selfLabelCount ?? "none"} attributable=${report.supervision.attributedEventCount ?? "none"}/${report.supervision.totalEventCount ?? "none"} digests=${report.supervision.selectionDigestCount ?? "none"}`,
|
|
1484
1496
|
`labels ${formatLabelFlowSummary(report.labelFlow)}`,
|
|
@@ -5111,7 +5123,13 @@ function listWatchRuntimeEventExportBundleRoots(scanRoot) {
|
|
|
5111
5123
|
.map((entry) => path.join(scanRoot, entry.name))
|
|
5112
5124
|
.sort((left, right) => left.localeCompare(right));
|
|
5113
5125
|
}
|
|
5114
|
-
async function replayWatchScanRootIntoTeacherLoop(teacherLoop, scanRoot) {
|
|
5126
|
+
async function replayWatchScanRootIntoTeacherLoop(teacherLoop, scanRoot, options = {}) {
|
|
5127
|
+
if (options.skip === true) {
|
|
5128
|
+
return {
|
|
5129
|
+
replayedBundleCount: 0,
|
|
5130
|
+
replayedEventCount: 0
|
|
5131
|
+
};
|
|
5132
|
+
}
|
|
5115
5133
|
const seenExportDigests = new Set();
|
|
5116
5134
|
const bundles = listWatchRuntimeEventExportBundleRoots(scanRoot)
|
|
5117
5135
|
.map((rootDir) => {
|
|
@@ -5733,6 +5751,7 @@ export async function createWatchCommandRuntime(input) {
|
|
|
5733
5751
|
const restoredSeenExportCount = restoredTeacherState.snapshot.state?.seenExportDigests.length ?? 0;
|
|
5734
5752
|
log(`Restored teacher snapshot: seen=${restoredSeenExportCount} artifacts=${restoredTeacherState.snapshot.teacher.artifactCount}`);
|
|
5735
5753
|
}
|
|
5754
|
+
const restoredSeenExportCount = restoredTeacherState.snapshot?.state?.seenExportDigests.length ?? 0;
|
|
5736
5755
|
const resolvedWatchProfileScope = input.profileRoots === undefined
|
|
5737
5756
|
? resolveWatchProfileRootsForActivationRoot(activationRoot)
|
|
5738
5757
|
: {
|
|
@@ -5774,7 +5793,13 @@ export async function createWatchCommandRuntime(input) {
|
|
|
5774
5793
|
replayedEventCount: 0
|
|
5775
5794
|
};
|
|
5776
5795
|
try {
|
|
5777
|
-
|
|
5796
|
+
const skipStoredReplay = restoredSeenExportCount > 0 && startupWarnings.length === 0;
|
|
5797
|
+
if (skipStoredReplay) {
|
|
5798
|
+
log(`Stored replay skipped: restored teacher snapshot already tracks ${restoredSeenExportCount} export digest${restoredSeenExportCount === 1 ? "" : "s"}.`);
|
|
5799
|
+
}
|
|
5800
|
+
replayState = await replayWatchScanRootIntoTeacherLoop(teacherLoop, scanRoot, {
|
|
5801
|
+
skip: skipStoredReplay
|
|
5802
|
+
});
|
|
5778
5803
|
}
|
|
5779
5804
|
catch (error) {
|
|
5780
5805
|
const message = formatWatchError(error);
|
package/dist/src/index.d.ts
CHANGED
|
@@ -6,6 +6,16 @@ import { type ActivationInspection, type ActivationObservabilityReport, type Gra
|
|
|
6
6
|
export { clearOpenClawProfileRuntimeLoadProof, listOpenClawProfileRuntimeLoadProofs, recordOpenClawProfileRuntimeLoadProof, resolveAttachmentRuntimeLoadProofsPath, type OpenClawProfileRuntimeLoadProofRecordV1, type OpenClawProfileRuntimeLoadProofSetV1, type OpenClawProfileRuntimeLoadProofsV1 } from "./attachment-truth.js";
|
|
7
7
|
import { type AsyncTeacherLabelerConfigV1 } from "./teacher-labeler.js";
|
|
8
8
|
export { createHttpOllamaTeacherLabelerClient, createOllamaTeacherLabeler, createTeacherLabeler, summarizeTeacherLabelerOpportunity, type AsyncTeacherLabelerConfigV1, type AsyncTeacherNoopLabelerConfigV1, type AsyncTeacherOllamaLabelerConfigV1, type OllamaTeacherLabelerClient, type TeacherLabeler, type TeacherLabelerOpportunityInputV1, type TeacherLabelerOpportunityV1, type TeacherLabelerResultV1, type TeacherLabelerRunInputV1 } from "./teacher-labeler.js";
|
|
9
|
+
export type OperatorPassiveLearningWatchState = CurrentProfilePassiveLearningWatchStateV1 | "lagging";
|
|
10
|
+
export interface OperatorPassiveLearningWatchSummary {
|
|
11
|
+
state: OperatorPassiveLearningWatchState;
|
|
12
|
+
detail: string;
|
|
13
|
+
lastHeartbeatAt: string | null;
|
|
14
|
+
lagSeconds: number | null;
|
|
15
|
+
intervalSeconds: number | null;
|
|
16
|
+
healthyWithinSeconds: number | null;
|
|
17
|
+
staleAfterSeconds: number | null;
|
|
18
|
+
}
|
|
9
19
|
export declare const DEFAULT_ASYNC_TEACHER_QUEUE_CAPACITY = 8;
|
|
10
20
|
declare const RECORDED_SESSION_TRACE_CONTRACT: "recorded_session_trace.v1";
|
|
11
21
|
declare const RECORDED_SESSION_FIXTURE_CONTRACT: "recorded_session_replay_fixture.v1";
|
|
@@ -1687,7 +1697,8 @@ export interface OperatorTeacherLoopSummary {
|
|
|
1687
1697
|
lastHeartbeatAt: string | null;
|
|
1688
1698
|
lastScanAt: string | null;
|
|
1689
1699
|
pollIntervalSeconds: number | null;
|
|
1690
|
-
watchState:
|
|
1700
|
+
watchState: OperatorPassiveLearningWatchState;
|
|
1701
|
+
watch: OperatorPassiveLearningWatchSummary;
|
|
1691
1702
|
lastProcessedAt: string | null;
|
|
1692
1703
|
artifactCount: number | null;
|
|
1693
1704
|
queueDepth: number | null;
|
package/dist/src/index.js
CHANGED
|
@@ -6646,11 +6646,18 @@ function loadTeacherSurfaceFromInput(input) {
|
|
|
6646
6646
|
}
|
|
6647
6647
|
function summarizeTeacherLoopWatchState(input) {
|
|
6648
6648
|
if (input.sourceKind !== "watch_snapshot" || input.watchSnapshot === null) {
|
|
6649
|
+
const watchState = input.sourceKind === "async_snapshot" ? "snapshot_only" : "not_visible";
|
|
6649
6650
|
return {
|
|
6650
6651
|
snapshotUpdatedAt: null,
|
|
6651
6652
|
lastWatchHeartbeatAt: null,
|
|
6652
6653
|
pollIntervalSeconds: null,
|
|
6653
|
-
watchState
|
|
6654
|
+
watchState,
|
|
6655
|
+
watch: buildTeacherLoopWatchSummary({
|
|
6656
|
+
state: watchState,
|
|
6657
|
+
detail: watchState === "snapshot_only"
|
|
6658
|
+
? "only a saved watcher snapshot is visible; no live heartbeat is available"
|
|
6659
|
+
: "no watcher snapshot is visible from the current activation root"
|
|
6660
|
+
})
|
|
6654
6661
|
};
|
|
6655
6662
|
}
|
|
6656
6663
|
const lastWatchHeartbeatAt = input.watchSnapshot.snapshot.runtime?.lastHeartbeatAt ?? input.watchSnapshot.lastRunAt;
|
|
@@ -6660,16 +6667,55 @@ function summarizeTeacherLoopWatchState(input) {
|
|
|
6660
6667
|
snapshotUpdatedAt: input.watchSnapshot.updatedAt,
|
|
6661
6668
|
lastWatchHeartbeatAt: null,
|
|
6662
6669
|
pollIntervalSeconds,
|
|
6663
|
-
watchState: "snapshot_only"
|
|
6670
|
+
watchState: "snapshot_only",
|
|
6671
|
+
watch: buildTeacherLoopWatchSummary({
|
|
6672
|
+
state: "snapshot_only",
|
|
6673
|
+
detail: "only a saved watcher snapshot is visible; no live heartbeat is available",
|
|
6674
|
+
intervalSeconds: pollIntervalSeconds
|
|
6675
|
+
})
|
|
6664
6676
|
};
|
|
6665
6677
|
}
|
|
6666
|
-
const
|
|
6667
|
-
const
|
|
6678
|
+
const rawLagMs = Date.parse(input.observedAt) - Date.parse(lastWatchHeartbeatAt);
|
|
6679
|
+
const lagMs = Number.isFinite(rawLagMs) ? Math.max(0, rawLagMs) : null;
|
|
6680
|
+
const healthyWithinMs = pollIntervalSeconds * 2000 + 15_000;
|
|
6681
|
+
const staleAfterMs = healthyWithinMs + Math.max(15_000, pollIntervalSeconds * 1000);
|
|
6682
|
+
const watchState = lagMs !== null && lagMs <= healthyWithinMs
|
|
6683
|
+
? "watching"
|
|
6684
|
+
: lagMs !== null && lagMs <= staleAfterMs
|
|
6685
|
+
? "lagging"
|
|
6686
|
+
: "stale_snapshot";
|
|
6668
6687
|
return {
|
|
6669
6688
|
snapshotUpdatedAt: input.watchSnapshot.updatedAt,
|
|
6670
6689
|
lastWatchHeartbeatAt,
|
|
6671
6690
|
pollIntervalSeconds,
|
|
6672
|
-
|
|
6691
|
+
watchState,
|
|
6692
|
+
watch: buildTeacherLoopWatchSummary({
|
|
6693
|
+
state: watchState,
|
|
6694
|
+
detail: watchState === "watching"
|
|
6695
|
+
? "watch heartbeat is inside the healthy window"
|
|
6696
|
+
: watchState === "lagging"
|
|
6697
|
+
? "watch heartbeat missed the healthy window but has not crossed the stale snapshot threshold"
|
|
6698
|
+
: "watch heartbeat is older than the stale snapshot threshold",
|
|
6699
|
+
lastHeartbeatAt: lastWatchHeartbeatAt,
|
|
6700
|
+
lagMs,
|
|
6701
|
+
intervalSeconds: pollIntervalSeconds,
|
|
6702
|
+
healthyWithinMs,
|
|
6703
|
+
staleAfterMs
|
|
6704
|
+
})
|
|
6705
|
+
};
|
|
6706
|
+
}
|
|
6707
|
+
function buildTeacherLoopWatchSummary(input) {
|
|
6708
|
+
const lagSeconds = Number.isFinite(input.lagMs) ? Math.round((input.lagMs / 1000) * 100) / 100 : null;
|
|
6709
|
+
const healthyWithinSeconds = Number.isFinite(input.healthyWithinMs) ? Math.round((input.healthyWithinMs / 1000) * 100) / 100 : null;
|
|
6710
|
+
const staleAfterSeconds = Number.isFinite(input.staleAfterMs) ? Math.round((input.staleAfterMs / 1000) * 100) / 100 : null;
|
|
6711
|
+
return {
|
|
6712
|
+
state: input.state,
|
|
6713
|
+
detail: input.detail,
|
|
6714
|
+
lastHeartbeatAt: input.lastHeartbeatAt ?? null,
|
|
6715
|
+
lagSeconds,
|
|
6716
|
+
intervalSeconds: input.intervalSeconds ?? null,
|
|
6717
|
+
healthyWithinSeconds,
|
|
6718
|
+
staleAfterSeconds
|
|
6673
6719
|
};
|
|
6674
6720
|
}
|
|
6675
6721
|
function emptyOperatorLearningAttribution(source, snapshotKind, detail) {
|
|
@@ -6798,6 +6844,10 @@ function summarizeTeacherLoop(input) {
|
|
|
6798
6844
|
const teacherSnapshotPath = resolveOperatorTeacherSnapshotPath(input.activationRoot, normalizeOptionalString(input.teacherSnapshotPath) ?? null);
|
|
6799
6845
|
const unavailableFromMissing = buildUnavailableLastObservedDelta("no watch teacher snapshot is visible for the latest observed cycle");
|
|
6800
6846
|
const unavailableFromAsync = buildUnavailableLastObservedDelta("raw async teacher snapshots do not record the last observed export/label/promotion delta");
|
|
6847
|
+
const notVisibleWatch = buildTeacherLoopWatchSummary({
|
|
6848
|
+
state: "not_visible",
|
|
6849
|
+
detail: "no watcher snapshot is visible from the current activation root"
|
|
6850
|
+
});
|
|
6801
6851
|
if (loaded === null && teacherSnapshotPath === null) {
|
|
6802
6852
|
return {
|
|
6803
6853
|
available: false,
|
|
@@ -6812,6 +6862,7 @@ function summarizeTeacherLoop(input) {
|
|
|
6812
6862
|
lastScanAt: null,
|
|
6813
6863
|
pollIntervalSeconds: null,
|
|
6814
6864
|
watchState: "not_visible",
|
|
6865
|
+
watch: notVisibleWatch,
|
|
6815
6866
|
lastProcessedAt: null,
|
|
6816
6867
|
artifactCount: null,
|
|
6817
6868
|
queueDepth: null,
|
|
@@ -6852,6 +6903,7 @@ function summarizeTeacherLoop(input) {
|
|
|
6852
6903
|
lastScanAt: null,
|
|
6853
6904
|
pollIntervalSeconds: null,
|
|
6854
6905
|
watchState: "not_visible",
|
|
6906
|
+
watch: notVisibleWatch,
|
|
6855
6907
|
lastProcessedAt: null,
|
|
6856
6908
|
artifactCount: null,
|
|
6857
6909
|
queueDepth: null,
|
|
@@ -6898,6 +6950,7 @@ function summarizeTeacherLoop(input) {
|
|
|
6898
6950
|
lastScanAt: snapshot.runtime?.lastScanAt ?? null,
|
|
6899
6951
|
pollIntervalSeconds: watchState.pollIntervalSeconds,
|
|
6900
6952
|
watchState: watchState.watchState,
|
|
6953
|
+
watch: watchState.watch,
|
|
6901
6954
|
lastProcessedAt: snapshot.diagnostics.lastProcessedAt,
|
|
6902
6955
|
artifactCount: watchSnapshot?.teacher.artifactCount ?? snapshot.teacher.artifactCount,
|
|
6903
6956
|
queueDepth: snapshot.queue.depth,
|
|
@@ -7486,6 +7539,13 @@ function didCurrentProfileFirstExportOccur(report) {
|
|
|
7486
7539
|
}
|
|
7487
7540
|
function summarizeCurrentProfilePassiveLearning(report, activePackId) {
|
|
7488
7541
|
const firstExportOccurred = didCurrentProfileFirstExportOccur(report);
|
|
7542
|
+
const watch = report.teacherLoop.watch ?? buildTeacherLoopWatchSummary({
|
|
7543
|
+
state: report.teacherLoop.watchState,
|
|
7544
|
+
detail: "watch state came from the legacy passive-learning surface",
|
|
7545
|
+
lastHeartbeatAt: report.teacherLoop.lastHeartbeatAt,
|
|
7546
|
+
intervalSeconds: report.teacherLoop.pollIntervalSeconds
|
|
7547
|
+
});
|
|
7548
|
+
const watchState = watch.state ?? report.teacherLoop.watchState;
|
|
7489
7549
|
const exportState = !firstExportOccurred
|
|
7490
7550
|
? "awaiting_first_export"
|
|
7491
7551
|
: report.supervision.exportedAt !== null
|
|
@@ -7495,28 +7555,33 @@ function summarizeCurrentProfilePassiveLearning(report, activePackId) {
|
|
|
7495
7555
|
? report.learning.backlogState
|
|
7496
7556
|
: "unknown";
|
|
7497
7557
|
const detail = !firstExportOccurred
|
|
7498
|
-
?
|
|
7558
|
+
? watchState === "watching"
|
|
7499
7559
|
? "watch heartbeat is fresh, but this activation root has not observed its first export yet"
|
|
7560
|
+
: watchState === "lagging"
|
|
7561
|
+
? "watch heartbeat is lagging, and this activation root is still waiting for the first export before passive learning can advance"
|
|
7500
7562
|
: "this activation root is still waiting for the first export before passive learning can advance"
|
|
7501
7563
|
: backlogState === "unknown"
|
|
7502
7564
|
? "first export is proven, but passive backlog state is not visible from the current local artifacts"
|
|
7503
|
-
:
|
|
7565
|
+
: watchState === "watching"
|
|
7504
7566
|
? `watch heartbeat is fresh; passive backlog is ${backlogState} with live=${report.learning.pendingLive ?? 0} and backfill=${report.learning.pendingBackfill ?? 0}`
|
|
7505
|
-
:
|
|
7567
|
+
: watchState === "lagging"
|
|
7568
|
+
? `watch heartbeat is lagging; latest known passive backlog is ${backlogState} with live=${report.learning.pendingLive ?? 0} and backfill=${report.learning.pendingBackfill ?? 0}`
|
|
7569
|
+
: watchState === "stale_snapshot"
|
|
7506
7570
|
? `last saved watch snapshot is stale; latest known passive backlog is ${backlogState}`
|
|
7507
|
-
:
|
|
7571
|
+
: watchState === "snapshot_only"
|
|
7508
7572
|
? `passive backlog is visible from the last saved snapshot: ${backlogState}`
|
|
7509
7573
|
: `passive backlog is visible from the last known learner state: ${backlogState}`;
|
|
7510
7574
|
return {
|
|
7511
|
-
learnerRunning:
|
|
7575
|
+
learnerRunning: watchState === "watching",
|
|
7512
7576
|
firstExportOccurred,
|
|
7513
|
-
watchState
|
|
7577
|
+
watchState,
|
|
7578
|
+
watch,
|
|
7514
7579
|
exportState,
|
|
7515
7580
|
backlogState,
|
|
7516
7581
|
pendingLive: report.learning.available ? report.learning.pendingLive : null,
|
|
7517
7582
|
pendingBackfill: report.learning.available ? report.learning.pendingBackfill : null,
|
|
7518
|
-
lastWatchHeartbeatAt: report.teacherLoop.lastHeartbeatAt,
|
|
7519
|
-
watchIntervalSeconds: report.teacherLoop.pollIntervalSeconds,
|
|
7583
|
+
lastWatchHeartbeatAt: watch.lastHeartbeatAt ?? report.teacherLoop.lastHeartbeatAt,
|
|
7584
|
+
watchIntervalSeconds: watch.intervalSeconds ?? report.teacherLoop.pollIntervalSeconds,
|
|
7520
7585
|
lastExportAt: report.supervision.exportedAt,
|
|
7521
7586
|
lastPromotionAt: report.promotion.lastPromotion.at,
|
|
7522
7587
|
currentServingPackId: activePackId,
|
|
@@ -928,13 +928,15 @@ export function advanceAlwaysOnLearningRuntime(input) {
|
|
|
928
928
|
const schedule = selectScheduledSlices(pending, current.learnedEventExport, cadence);
|
|
929
929
|
const selectedSlices = schedule.selected;
|
|
930
930
|
const learnedEventExport = mergeNormalizedEventExports(current.learnedEventExport, selectedSlices);
|
|
931
|
-
const runtimeGraphSnapshot =
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
...
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
931
|
+
const runtimeGraphSnapshot = selectedSlices.length === 0
|
|
932
|
+
? null
|
|
933
|
+
: buildRuntimeGraphSnapshot({
|
|
934
|
+
...input,
|
|
935
|
+
state: {
|
|
936
|
+
...current,
|
|
937
|
+
structuralController
|
|
938
|
+
}
|
|
939
|
+
});
|
|
938
940
|
const runtimeGraph = runtimeGraphSnapshot?.graph ?? current.runtimeGraph;
|
|
939
941
|
const runtimePlasticity = runtimeGraphSnapshot?.plasticity ?? current.runtimePlasticity;
|
|
940
942
|
const sparseFeedbackObservedAt = input.builtAt ?? learnedEventExport?.range.lastCreatedAt ?? learnedEventExport?.range.firstCreatedAt ?? current.lastMaterializedAt ?? "1970-01-01T00:00:00.000Z";
|
|
@@ -171,6 +171,35 @@ function parseOpenClawSessionRecord(value, lineNumber) {
|
|
|
171
171
|
timestamp: expectString(record.timestamp, `${lineNumber}.timestamp`)
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
+
case "compaction": {
|
|
175
|
+
const data = {};
|
|
176
|
+
if (record.summary !== undefined) {
|
|
177
|
+
data.summary = expectString(record.summary, `${lineNumber}.summary`);
|
|
178
|
+
}
|
|
179
|
+
if (record.firstKeptEntryId !== undefined) {
|
|
180
|
+
data.firstKeptEntryId = expectString(record.firstKeptEntryId, `${lineNumber}.firstKeptEntryId`);
|
|
181
|
+
}
|
|
182
|
+
if (record.tokensBefore !== undefined) {
|
|
183
|
+
data.tokensBefore = expectNumber(record.tokensBefore, `${lineNumber}.tokensBefore`);
|
|
184
|
+
}
|
|
185
|
+
if (record.details !== undefined) {
|
|
186
|
+
data.details = expectRecord(record.details, `${lineNumber}.details`);
|
|
187
|
+
}
|
|
188
|
+
if (record.fromHook !== undefined) {
|
|
189
|
+
if (typeof record.fromHook !== "boolean") {
|
|
190
|
+
throw new Error(`${lineNumber}.fromHook must be a boolean`);
|
|
191
|
+
}
|
|
192
|
+
data.fromHook = record.fromHook;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
type: "custom",
|
|
196
|
+
customType: "openclaw.compaction",
|
|
197
|
+
data,
|
|
198
|
+
id: expectString(record.id, `${lineNumber}.id`),
|
|
199
|
+
parentId: expectNullableString(record.parentId, `${lineNumber}.parentId`),
|
|
200
|
+
timestamp: expectString(record.timestamp, `${lineNumber}.timestamp`)
|
|
201
|
+
};
|
|
202
|
+
}
|
|
174
203
|
case "message":
|
|
175
204
|
return {
|
|
176
205
|
type,
|
|
@@ -185,7 +214,9 @@ function parseOpenClawSessionRecord(value, lineNumber) {
|
|
|
185
214
|
}
|
|
186
215
|
function parseMessagePayload(value, path) {
|
|
187
216
|
const payload = expectRecord(value, path);
|
|
188
|
-
const content =
|
|
217
|
+
const content = typeof payload.content === "string"
|
|
218
|
+
? [{ type: "text", text: payload.content }]
|
|
219
|
+
: expectArray(payload.content, `${path}.content`).map((entry, index) => parseContentPart(entry, `${path}.content[${index}]`));
|
|
189
220
|
return {
|
|
190
221
|
...payload,
|
|
191
222
|
role: expectString(payload.role, `${path}.role`),
|
|
@@ -267,4 +298,4 @@ function expectNumber(value, path) {
|
|
|
267
298
|
}
|
|
268
299
|
return value;
|
|
269
300
|
}
|
|
270
|
-
//# sourceMappingURL=session-store.js.map
|
|
301
|
+
//# sourceMappingURL=session-store.js.map
|
package/dist/src/session-tail.js
CHANGED
|
@@ -383,6 +383,7 @@ export class OpenClawLocalSessionTail {
|
|
|
383
383
|
const firstPoll = this.initialized === false;
|
|
384
384
|
const warnings = [];
|
|
385
385
|
const changes = [];
|
|
386
|
+
const observedKeys = new Set();
|
|
386
387
|
const sources = discoverOpenClawSessionStores({
|
|
387
388
|
...(this.homeDir === undefined ? {} : { homeDir: this.homeDir }),
|
|
388
389
|
...(this.profileRoots === undefined ? {} : { profileRoots: this.profileRoots })
|
|
@@ -409,9 +410,21 @@ export class OpenClawLocalSessionTail {
|
|
|
409
410
|
continue;
|
|
410
411
|
}
|
|
411
412
|
const key = cursorKey(source.indexPath, sessionKey);
|
|
413
|
+
observedKeys.add(key);
|
|
412
414
|
const existing = this.cursorBySession.get(key) ?? null;
|
|
413
415
|
const sessionFile = typeof entry.sessionFile === "string" && entry.sessionFile.trim().length > 0 ? path.resolve(entry.sessionFile) : null;
|
|
414
416
|
if (sessionFile === null) {
|
|
417
|
+
const nextCursor = createCursor(source.indexPath, sessionKey, entry.sessionId, null, entry.updatedAt, 0, 0);
|
|
418
|
+
this.cursorBySession.set(key, nextCursor);
|
|
419
|
+
const changed = existing === null ||
|
|
420
|
+
existing.sessionId !== entry.sessionId ||
|
|
421
|
+
existing.sessionFile !== null ||
|
|
422
|
+
existing.updatedAt !== entry.updatedAt ||
|
|
423
|
+
existing.rawRecordCount !== 0 ||
|
|
424
|
+
existing.bridgedEventCount !== 0;
|
|
425
|
+
if (!changed) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
415
428
|
changes.push(createChange({
|
|
416
429
|
source,
|
|
417
430
|
sessionKey,
|
|
@@ -425,6 +438,17 @@ export class OpenClawLocalSessionTail {
|
|
|
425
438
|
continue;
|
|
426
439
|
}
|
|
427
440
|
if (!existsSync(sessionFile)) {
|
|
441
|
+
const nextCursor = createCursor(source.indexPath, sessionKey, entry.sessionId, sessionFile, entry.updatedAt, 0, 0);
|
|
442
|
+
this.cursorBySession.set(key, nextCursor);
|
|
443
|
+
const changed = existing === null ||
|
|
444
|
+
existing.sessionId !== entry.sessionId ||
|
|
445
|
+
existing.sessionFile !== sessionFile ||
|
|
446
|
+
existing.updatedAt !== entry.updatedAt ||
|
|
447
|
+
existing.rawRecordCount !== 0 ||
|
|
448
|
+
existing.bridgedEventCount !== 0;
|
|
449
|
+
if (!changed) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
428
452
|
changes.push(createChange({
|
|
429
453
|
source,
|
|
430
454
|
sessionKey,
|
|
@@ -535,6 +559,17 @@ export class OpenClawLocalSessionTail {
|
|
|
535
559
|
}));
|
|
536
560
|
}
|
|
537
561
|
}
|
|
562
|
+
let prunedCursorCount = 0;
|
|
563
|
+
for (const key of [...this.cursorBySession.keys()]) {
|
|
564
|
+
if (observedKeys.has(key)) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
this.cursorBySession.delete(key);
|
|
568
|
+
prunedCursorCount += 1;
|
|
569
|
+
}
|
|
570
|
+
if (prunedCursorCount > 0) {
|
|
571
|
+
warnings.push(`session tail pruned ${prunedCursorCount} stale cursor entr${prunedCursorCount === 1 ? "y" : "ies"}`);
|
|
572
|
+
}
|
|
538
573
|
this.initialized = true;
|
|
539
574
|
const noopReason = firstPoll && !changes.some((change) => change.scannedEventExport !== null)
|
|
540
575
|
? "seeded_existing_sessions"
|
|
@@ -599,4 +634,4 @@ export class OpenClawLocalSessionTail {
|
|
|
599
634
|
export function createOpenClawLocalSessionTail(input = {}) {
|
|
600
635
|
return new OpenClawLocalSessionTail(input);
|
|
601
636
|
}
|
|
602
|
-
//# sourceMappingURL=session-tail.js.map
|
|
637
|
+
//# sourceMappingURL=session-tail.js.map
|
package/package.json
CHANGED