@openclawbrain/cli 0.4.14 → 0.4.16
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/extension/index.js +30 -4
- package/dist/extension/runtime-guard.d.ts +6 -0
- package/dist/extension/runtime-guard.js +71 -15
- package/dist/src/cli.js +163 -23
- package/dist/src/index.d.ts +248 -4
- package/dist/src/index.js +1199 -44
- package/dist/src/install-converge.js +4 -3
- package/dist/src/learning-spine.js +43 -1
- package/dist/src/local-learner.d.ts +6 -0
- package/dist/src/local-learner.js +86 -152
- package/dist/src/local-session-passive-learning.js +28 -2
- package/dist/src/materialization-embedder.js +11 -0
- package/dist/src/openclaw-hook-truth.d.ts +6 -0
- package/dist/src/openclaw-hook-truth.js +27 -0
- package/dist/src/proof-command.js +251 -4
- package/dist/src/runtime-core.js +87 -7
- package/dist/src/status-learning-path.js +32 -2
- package/dist/src/teacher-decision-match.js +47 -10
- package/dist/src/teacher-labeler.d.ts +12 -0
- package/dist/src/teacher-labeler.js +42 -0
- package/dist/src/traced-learning-bridge.js +17 -1
- package/extension/index.ts +35 -4
- package/extension/runtime-guard.ts +92 -14
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import { compileRuntimeFromActivation } from "@openclawbrain/compiler";
|
|
@@ -11,8 +12,8 @@ import { inspectOpenClawBrainHookStatus, summarizeOpenClawBrainHookLoad } from "
|
|
|
11
12
|
import { appendLearningUpdateLogs, appendServeTimeRouteDecisionLog } from "./learning-spine.js";
|
|
12
13
|
import { buildFeedbackSemanticMetadata, buildInteractionSemanticMetadata } from "./semantic-metadata.js";
|
|
13
14
|
export { clearOpenClawProfileRuntimeLoadProof, listOpenClawProfileRuntimeLoadProofs, recordOpenClawProfileRuntimeLoadProof, resolveAttachmentRuntimeLoadProofsPath } from "./attachment-truth.js";
|
|
14
|
-
import { createTeacherLabeler } from "./teacher-labeler.js";
|
|
15
|
-
export { createHttpOllamaTeacherLabelerClient, createOllamaTeacherLabeler, createTeacherLabeler } from "./teacher-labeler.js";
|
|
15
|
+
import { createTeacherLabeler, summarizeTeacherLabelerOpportunity } from "./teacher-labeler.js";
|
|
16
|
+
export { createHttpOllamaTeacherLabelerClient, createOllamaTeacherLabeler, createTeacherLabeler, summarizeTeacherLabelerOpportunity } from "./teacher-labeler.js";
|
|
16
17
|
const DEFAULT_AGENT_ID = "openclaw-runtime";
|
|
17
18
|
const FEEDBACK_KINDS = new Set(["correction", "teaching", "approval", "suppression"]);
|
|
18
19
|
export const DEFAULT_ASYNC_TEACHER_QUEUE_CAPACITY = 8;
|
|
@@ -27,6 +28,27 @@ export const RUNTIME_EVENT_EXPORT_BUNDLE_LAYOUT = {
|
|
|
27
28
|
manifest: "manifest.json",
|
|
28
29
|
payload: "normalized-event-export.json"
|
|
29
30
|
};
|
|
31
|
+
const RECORDED_SESSION_REPLAY_PROOF_MANIFEST_CONTRACT = "recorded_session_replay_proof_manifest.v1";
|
|
32
|
+
const RECORDED_SESSION_REPLAY_PROOF_ENVIRONMENT_CONTRACT = "recorded_session_replay_environment.v1";
|
|
33
|
+
const RECORDED_SESSION_REPLAY_PROOF_SUMMARY_TABLES_CONTRACT = "recorded_session_replay_summary_tables.v1";
|
|
34
|
+
const RECORDED_SESSION_REPLAY_PROOF_COVERAGE_SNAPSHOT_CONTRACT = "recorded_session_replay_coverage_snapshot.v1";
|
|
35
|
+
const RECORDED_SESSION_REPLAY_PROOF_HARDENING_SNAPSHOT_CONTRACT = "recorded_session_replay_hardening_snapshot.v1";
|
|
36
|
+
const RECORDED_SESSION_REPLAY_PROOF_HASHES_CONTRACT = "recorded_session_replay_hashes.v1";
|
|
37
|
+
const RECORDED_SESSION_REPLAY_PROOF_VALIDATION_CONTRACT = "recorded_session_replay_proof_validation.v1";
|
|
38
|
+
const RECORDED_SESSION_REPLAY_MODE_ORDER = ["no_brain", "vector_only", "graph_prior_only", "learned_route"];
|
|
39
|
+
export const RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT = {
|
|
40
|
+
manifest: "manifest.json",
|
|
41
|
+
trace: "trace.json",
|
|
42
|
+
fixture: "fixture.json",
|
|
43
|
+
bundle: "bundle.json",
|
|
44
|
+
environment: "environment.json",
|
|
45
|
+
summary: "summary.md",
|
|
46
|
+
summaryTables: "summary-tables.json",
|
|
47
|
+
coverageSnapshot: "coverage-snapshot.json",
|
|
48
|
+
hardeningSnapshot: "hardening-snapshot.json",
|
|
49
|
+
hashes: "hashes.json",
|
|
50
|
+
modeDir: "modes"
|
|
51
|
+
};
|
|
30
52
|
function normalizeRuntimeProfileSelector(value, fieldName, fallback = "current_profile") {
|
|
31
53
|
if (value === undefined || value === null) {
|
|
32
54
|
return fallback;
|
|
@@ -228,9 +250,45 @@ function buildAsyncTeacherLoopNotes(input) {
|
|
|
228
250
|
`teacher_noop=${input.noOpReason}`,
|
|
229
251
|
`teacher_labeler=${input.teacherLabeler?.status ?? "disabled"}`,
|
|
230
252
|
`teacher_labeler_detail=${input.teacherLabeler?.detail ?? "disabled"}`,
|
|
253
|
+
`teacher_last_cycle_deterministic_artifacts=${input.lastCycle?.deterministicArtifactCount ?? "unknown"}`,
|
|
254
|
+
`teacher_last_cycle_new_deterministic_artifacts=${input.lastCycle?.newDeterministicArtifactCount ?? "unknown"}`,
|
|
255
|
+
`teacher_last_cycle_labeler_candidates=${input.lastCycle?.labelerCandidateCount ?? "unknown"}`,
|
|
256
|
+
`teacher_last_cycle_labeler_budgeted_candidates=${input.lastCycle?.labelerBudgetedCandidateCount ?? "unknown"}`,
|
|
257
|
+
`teacher_last_cycle_labeler_status=${input.lastCycle?.labelerStatus ?? "unknown"}`,
|
|
258
|
+
`teacher_last_cycle_labeler_detail=${input.lastCycle?.labelerDetail ?? "unknown"}`,
|
|
231
259
|
input.materialization === null ? "teacher_materialization=noop" : `teacher_materialized_pack=${input.materialization.candidate.summary.packId}`
|
|
232
260
|
];
|
|
233
261
|
}
|
|
262
|
+
function parseAsyncTeacherLastCycleNotes(notes) {
|
|
263
|
+
const values = new Map();
|
|
264
|
+
for (const note of notes) {
|
|
265
|
+
const separator = note.indexOf("=");
|
|
266
|
+
if (separator <= 0) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
values.set(note.slice(0, separator), note.slice(separator + 1));
|
|
270
|
+
}
|
|
271
|
+
const readNullableNumber = (key) => {
|
|
272
|
+
const raw = values.get(key);
|
|
273
|
+
if (raw === undefined || raw === "unknown") {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
const parsed = Number.parseInt(raw, 10);
|
|
277
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
278
|
+
};
|
|
279
|
+
const readNullableString = (key) => {
|
|
280
|
+
const raw = values.get(key);
|
|
281
|
+
return raw === undefined || raw === "unknown" ? null : raw;
|
|
282
|
+
};
|
|
283
|
+
return {
|
|
284
|
+
deterministicArtifactCount: readNullableNumber("teacher_last_cycle_deterministic_artifacts"),
|
|
285
|
+
newDeterministicArtifactCount: readNullableNumber("teacher_last_cycle_new_deterministic_artifacts"),
|
|
286
|
+
labelerCandidateCount: readNullableNumber("teacher_last_cycle_labeler_candidates"),
|
|
287
|
+
labelerBudgetedCandidateCount: readNullableNumber("teacher_last_cycle_labeler_budgeted_candidates"),
|
|
288
|
+
labelerStatus: readNullableString("teacher_last_cycle_labeler_status"),
|
|
289
|
+
labelerDetail: readNullableString("teacher_last_cycle_labeler_detail")
|
|
290
|
+
};
|
|
291
|
+
}
|
|
234
292
|
function cloneAlwaysOnLearningMaterializationJobOrNull(value) {
|
|
235
293
|
return value === null ? null : structuredClone(value);
|
|
236
294
|
}
|
|
@@ -543,6 +601,14 @@ export class AsyncTeacherLiveLoop {
|
|
|
543
601
|
learnerState = createAlwaysOnLearningRuntimeState();
|
|
544
602
|
lastMaterialization = null;
|
|
545
603
|
lastTeacherLabelerResult = null;
|
|
604
|
+
lastCycle = {
|
|
605
|
+
deterministicArtifactCount: null,
|
|
606
|
+
newDeterministicArtifactCount: null,
|
|
607
|
+
labelerCandidateCount: null,
|
|
608
|
+
labelerBudgetedCandidateCount: null,
|
|
609
|
+
labelerStatus: null,
|
|
610
|
+
labelerDetail: null
|
|
611
|
+
};
|
|
546
612
|
diagnostics = {
|
|
547
613
|
acceptedExportCount: 0,
|
|
548
614
|
processedExportCount: 0,
|
|
@@ -562,7 +628,8 @@ export class AsyncTeacherLiveLoop {
|
|
|
562
628
|
sparseFeedback: this.learnerState.sparseFeedback,
|
|
563
629
|
noOpReason: "none",
|
|
564
630
|
materialization: null,
|
|
565
|
-
teacherLabeler: null
|
|
631
|
+
teacherLabeler: null,
|
|
632
|
+
lastCycle: this.lastCycle
|
|
566
633
|
})
|
|
567
634
|
};
|
|
568
635
|
constructor(input) {
|
|
@@ -590,6 +657,7 @@ export class AsyncTeacherLiveLoop {
|
|
|
590
657
|
...structuredClone(resumedSnapshot.diagnostics),
|
|
591
658
|
notes: [...resumedSnapshot.diagnostics.notes]
|
|
592
659
|
};
|
|
660
|
+
this.lastCycle = parseAsyncTeacherLastCycleNotes(this.diagnostics.notes);
|
|
593
661
|
for (const exportDigest of resumedSnapshot.state?.seenExportDigests ?? []) {
|
|
594
662
|
this.seenExportDigests.add(exportDigest);
|
|
595
663
|
}
|
|
@@ -836,6 +904,19 @@ export class AsyncTeacherLiveLoop {
|
|
|
836
904
|
feedbackEvents: this.feedbackEvents
|
|
837
905
|
});
|
|
838
906
|
const learnedRoutingState = this.input.resolveLearnedRoutingState?.() ?? {};
|
|
907
|
+
const currentDedupIds = new Set(this.teacherArtifacts.map((artifact) => artifact.dedupId));
|
|
908
|
+
const currentCycleBuiltArtifacts = buildTeacherSupervisionArtifactsFromNormalizedEventExport({
|
|
909
|
+
normalizedEventExport: job.normalizedEventExport,
|
|
910
|
+
observedAt: job.observedAt,
|
|
911
|
+
staleAfterMs: this.staleAfterMs,
|
|
912
|
+
...(this.input.sparseFeedback !== undefined ? { sparseFeedback: this.input.sparseFeedback } : {})
|
|
913
|
+
});
|
|
914
|
+
const currentCycleOpportunity = summarizeTeacherLabelerOpportunity({
|
|
915
|
+
normalizedEventExport: job.normalizedEventExport,
|
|
916
|
+
...(learnedRoutingState.serveTimeDecisions !== undefined
|
|
917
|
+
? { serveTimeDecisions: learnedRoutingState.serveTimeDecisions }
|
|
918
|
+
: {})
|
|
919
|
+
}, this.input.teacherLabeler ?? null);
|
|
839
920
|
const builtArtifacts = buildTeacherSupervisionArtifactsFromNormalizedEventExport({
|
|
840
921
|
normalizedEventExport: mergedNormalizedEventExport,
|
|
841
922
|
observedAt: job.observedAt,
|
|
@@ -865,10 +946,21 @@ export class AsyncTeacherLiveLoop {
|
|
|
865
946
|
}
|
|
866
947
|
}
|
|
867
948
|
const nextBuiltArtifacts = mergeTeacherArtifacts([], [...builtArtifacts, ...generatedTeacherArtifacts]);
|
|
868
|
-
const currentDedupIds = new Set(this.teacherArtifacts.map((artifact) => artifact.dedupId));
|
|
869
949
|
const nextTeacherArtifacts = mergeTeacherArtifacts(this.teacherArtifacts, nextBuiltArtifacts);
|
|
870
950
|
const emittedArtifactCount = nextBuiltArtifacts.filter((artifact) => !currentDedupIds.has(artifact.dedupId)).length;
|
|
871
951
|
const dedupedArtifactCount = nextBuiltArtifacts.length - emittedArtifactCount;
|
|
952
|
+
this.lastCycle = {
|
|
953
|
+
deterministicArtifactCount: currentCycleBuiltArtifacts.length,
|
|
954
|
+
newDeterministicArtifactCount: currentCycleBuiltArtifacts.filter((artifact) => !currentDedupIds.has(artifact.dedupId)).length,
|
|
955
|
+
labelerCandidateCount: currentCycleOpportunity.candidateCount,
|
|
956
|
+
labelerBudgetedCandidateCount: currentCycleOpportunity.budgetedCandidateCount,
|
|
957
|
+
labelerStatus: currentCycleOpportunity.candidateCount === 0
|
|
958
|
+
? currentCycleOpportunity.status
|
|
959
|
+
: this.lastTeacherLabelerResult?.status ?? (currentCycleOpportunity.enabled ? "unknown" : "disabled"),
|
|
960
|
+
labelerDetail: currentCycleOpportunity.candidateCount === 0
|
|
961
|
+
? currentCycleOpportunity.detail
|
|
962
|
+
: this.lastTeacherLabelerResult?.detail ?? currentCycleOpportunity.detail
|
|
963
|
+
};
|
|
872
964
|
this.teacherArtifacts = nextTeacherArtifacts;
|
|
873
965
|
const learnerResult = advanceAlwaysOnLearningRuntime({
|
|
874
966
|
packLabel: this.input.packLabel,
|
|
@@ -930,7 +1022,8 @@ export class AsyncTeacherLiveLoop {
|
|
|
930
1022
|
sparseFeedback: this.learnerState.sparseFeedback,
|
|
931
1023
|
noOpReason: this.diagnostics.lastNoOpReason,
|
|
932
1024
|
materialization: this.lastMaterialization,
|
|
933
|
-
teacherLabeler: this.lastTeacherLabelerResult
|
|
1025
|
+
teacherLabeler: this.lastTeacherLabelerResult,
|
|
1026
|
+
lastCycle: this.lastCycle
|
|
934
1027
|
});
|
|
935
1028
|
}
|
|
936
1029
|
}
|
|
@@ -1058,6 +1151,24 @@ export function scanLiveEventExport(input) {
|
|
|
1058
1151
|
function readJsonFile(filePath) {
|
|
1059
1152
|
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
1060
1153
|
}
|
|
1154
|
+
function checksumTextPayload(value) {
|
|
1155
|
+
return `sha256-${createHash("sha256").update(value).digest("hex")}`;
|
|
1156
|
+
}
|
|
1157
|
+
function orderedRecordedSessionReplayModes(modes) {
|
|
1158
|
+
const byMode = new Map(modes.map((mode) => [mode.mode, mode]));
|
|
1159
|
+
return RECORDED_SESSION_REPLAY_MODE_ORDER
|
|
1160
|
+
.map((mode) => byMode.get(mode))
|
|
1161
|
+
.filter(isPresent);
|
|
1162
|
+
}
|
|
1163
|
+
function recordedSessionReplayModeOutputPath(mode) {
|
|
1164
|
+
return path.posix.join(RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.modeDir, `${mode}.json`);
|
|
1165
|
+
}
|
|
1166
|
+
function resolveRecordedSessionReplayProofPath(rootDir, relativePath, fieldName) {
|
|
1167
|
+
return resolveBundlePayloadPath(rootDir, normalizeNonEmptyString(relativePath, fieldName));
|
|
1168
|
+
}
|
|
1169
|
+
function readRecordedSessionReplayProofText(rootDir, relativePath, fieldName) {
|
|
1170
|
+
return readFileSync(resolveRecordedSessionReplayProofPath(rootDir, relativePath, fieldName), "utf8");
|
|
1171
|
+
}
|
|
1061
1172
|
export function resolveAsyncTeacherLiveLoopSnapshotPath(activationRoot) {
|
|
1062
1173
|
return path.join(path.resolve(normalizeNonEmptyString(activationRoot, "activationRoot")), "async-teacher-live-loop.snapshot.json");
|
|
1063
1174
|
}
|
|
@@ -2340,8 +2451,61 @@ function normalizeIsoTimestamp(value, fieldName, fallbackValue) {
|
|
|
2340
2451
|
}
|
|
2341
2452
|
return new Date(candidate).toISOString();
|
|
2342
2453
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2454
|
+
const RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG = {
|
|
2455
|
+
vector_only: {
|
|
2456
|
+
routeMode: "heuristic",
|
|
2457
|
+
selectionMode: "flat_rank_v1"
|
|
2458
|
+
},
|
|
2459
|
+
graph_prior_only: {
|
|
2460
|
+
routeMode: "heuristic",
|
|
2461
|
+
selectionMode: "graph_walk_v1"
|
|
2462
|
+
},
|
|
2463
|
+
learned_route: {
|
|
2464
|
+
routeMode: "learned",
|
|
2465
|
+
selectionMode: "graph_walk_v1"
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
function resolveRuntimeComparativeReplayMode(value) {
|
|
2469
|
+
return Object.prototype.hasOwnProperty.call(RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG, value)
|
|
2470
|
+
? value
|
|
2471
|
+
: null;
|
|
2472
|
+
}
|
|
2473
|
+
function resolveCompileModePlan(modeValue, selectionModeValue) {
|
|
2474
|
+
const requestedSelectionMode = normalizeCompileSelectionMode(selectionModeValue);
|
|
2475
|
+
const comparativeMode = resolveRuntimeComparativeReplayMode(modeValue);
|
|
2476
|
+
if (comparativeMode === null) {
|
|
2477
|
+
if (modeValue === undefined) {
|
|
2478
|
+
return {
|
|
2479
|
+
comparativeMode: null,
|
|
2480
|
+
routeMode: "heuristic",
|
|
2481
|
+
selectionMode: requestedSelectionMode
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
if (modeValue === "heuristic" || modeValue === "learned") {
|
|
2485
|
+
return {
|
|
2486
|
+
comparativeMode: null,
|
|
2487
|
+
routeMode: modeValue,
|
|
2488
|
+
selectionMode: requestedSelectionMode
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
throw new Error("mode must be heuristic, learned, vector_only, graph_prior_only, or learned_route");
|
|
2492
|
+
}
|
|
2493
|
+
const plan = RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG[comparativeMode];
|
|
2494
|
+
if (requestedSelectionMode !== undefined && requestedSelectionMode !== plan.selectionMode) {
|
|
2495
|
+
throw new Error(`selectionMode ${requestedSelectionMode} conflicts with comparative mode ${comparativeMode}, expected ${plan.selectionMode}`);
|
|
2496
|
+
}
|
|
2497
|
+
return {
|
|
2498
|
+
comparativeMode,
|
|
2499
|
+
routeMode: plan.routeMode,
|
|
2500
|
+
selectionMode: plan.selectionMode
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
function resolveSyntheticTurnMode(value) {
|
|
2504
|
+
const comparativeMode = resolveRuntimeComparativeReplayMode(value);
|
|
2505
|
+
if (comparativeMode !== null) {
|
|
2506
|
+
return RUNTIME_COMPARATIVE_REPLAY_MODE_CONFIG[comparativeMode].routeMode;
|
|
2507
|
+
}
|
|
2508
|
+
return value === "heuristic" || value === "learned" ? value : undefined;
|
|
2345
2509
|
}
|
|
2346
2510
|
function normalizeCompileSelectionMode(value) {
|
|
2347
2511
|
if (value === undefined) {
|
|
@@ -2681,8 +2845,9 @@ function appendCompileServeRouteDecisionLog(input) {
|
|
|
2681
2845
|
if (input.compileInput.budgetStrategy === "fixed_v1" || input.compileInput.budgetStrategy === "empirical_v1") {
|
|
2682
2846
|
syntheticTurn.budgetStrategy = input.compileInput.budgetStrategy;
|
|
2683
2847
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2848
|
+
const syntheticTurnMode = resolveSyntheticTurnMode(input.compileInput.mode);
|
|
2849
|
+
if (syntheticTurnMode !== undefined) {
|
|
2850
|
+
syntheticTurn.mode = syntheticTurnMode;
|
|
2686
2851
|
}
|
|
2687
2852
|
if (input.compileInput.runtimeHints !== undefined) {
|
|
2688
2853
|
syntheticTurn.runtimeHints = input.compileInput.runtimeHints;
|
|
@@ -2936,16 +3101,42 @@ export function resolveActivePackForCompile(activationRoot) {
|
|
|
2936
3101
|
inspection: inspection.active
|
|
2937
3102
|
};
|
|
2938
3103
|
}
|
|
3104
|
+
function normalizeFrozenReplayEvalIdentity(value) {
|
|
3105
|
+
if (value === undefined || value === null) {
|
|
3106
|
+
return null;
|
|
3107
|
+
}
|
|
3108
|
+
if (typeof value !== "object") {
|
|
3109
|
+
throw new Error("_frozenReplayEvalIdentity must be an object");
|
|
3110
|
+
}
|
|
3111
|
+
const frozenIdentity = value;
|
|
3112
|
+
return {
|
|
3113
|
+
packId: normalizeNonEmptyString(frozenIdentity.packId, "_frozenReplayEvalIdentity.packId"),
|
|
3114
|
+
routerIdentity: normalizeOptionalString(frozenIdentity.routerIdentity) ?? null
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
function validateFrozenReplayEvalIdentity(target, frozenReplayEvalIdentity) {
|
|
3118
|
+
if (frozenReplayEvalIdentity === null) {
|
|
3119
|
+
return;
|
|
3120
|
+
}
|
|
3121
|
+
const actualRouterIdentity = target.inspection.routerIdentity ?? null;
|
|
3122
|
+
if (target.activePointer.packId === frozenReplayEvalIdentity.packId &&
|
|
3123
|
+
actualRouterIdentity === frozenReplayEvalIdentity.routerIdentity) {
|
|
3124
|
+
return;
|
|
3125
|
+
}
|
|
3126
|
+
throw new Error(`Frozen replay eval identity mismatch: expected pack ${frozenReplayEvalIdentity.packId} (routerIdentity=${frozenReplayEvalIdentity.routerIdentity ?? "null"}), received pack ${target.activePointer.packId} (routerIdentity=${actualRouterIdentity ?? "null"})`);
|
|
3127
|
+
}
|
|
2939
3128
|
export function compileRuntimeContext(input) {
|
|
2940
3129
|
const totalStartedAtNs = monotonicClockNs();
|
|
2941
3130
|
const fallbackActivationRoot = resolveActivationRootForFailure(input.activationRoot);
|
|
2942
3131
|
let activationRoot = fallbackActivationRoot;
|
|
2943
3132
|
let agentId = process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
|
|
2944
3133
|
let runtimeHints = [];
|
|
3134
|
+
let comparativeMode = null;
|
|
2945
3135
|
let selectionMode;
|
|
2946
3136
|
let userMessage = "";
|
|
2947
3137
|
let maxContextChars;
|
|
2948
3138
|
let mode = "heuristic";
|
|
3139
|
+
let frozenReplayEvalIdentity = null;
|
|
2949
3140
|
let routeSelectionStartedAtNs = null;
|
|
2950
3141
|
let routeSelectionMs = null;
|
|
2951
3142
|
let promptAssemblyStartedAtNs = null;
|
|
@@ -2955,13 +3146,16 @@ export function compileRuntimeContext(input) {
|
|
|
2955
3146
|
activationRoot = path.resolve(normalizeNonEmptyString(input.activationRoot, "activationRoot"));
|
|
2956
3147
|
agentId = normalizeOptionalString(input.agentId) ?? process.env.OPENCLAWBRAIN_AGENT_ID ?? DEFAULT_AGENT_ID;
|
|
2957
3148
|
runtimeHints = normalizeRuntimeHints(input.runtimeHints);
|
|
2958
|
-
selectionMode = normalizeCompileSelectionMode(input.selectionMode);
|
|
2959
3149
|
userMessage = normalizeNonEmptyString(input.message, "message");
|
|
2960
3150
|
maxContextChars =
|
|
2961
3151
|
input.maxContextChars !== undefined
|
|
2962
3152
|
? normalizeNonNegativeInteger(input.maxContextChars, "maxContextChars", input.maxContextChars)
|
|
2963
3153
|
: undefined;
|
|
2964
|
-
|
|
3154
|
+
const compileModePlan = resolveCompileModePlan(input.mode, input.selectionMode);
|
|
3155
|
+
comparativeMode = compileModePlan.comparativeMode;
|
|
3156
|
+
mode = compileModePlan.routeMode;
|
|
3157
|
+
selectionMode = compileModePlan.selectionMode;
|
|
3158
|
+
frozenReplayEvalIdentity = normalizeFrozenReplayEvalIdentity(input._frozenReplayEvalIdentity);
|
|
2965
3159
|
}
|
|
2966
3160
|
catch (error) {
|
|
2967
3161
|
result = failOpenCompileResult(error, fallbackActivationRoot, buildBrainServeHotPathTiming({
|
|
@@ -2979,6 +3173,7 @@ export function compileRuntimeContext(input) {
|
|
|
2979
3173
|
}
|
|
2980
3174
|
try {
|
|
2981
3175
|
const target = resolveActivePackForCompile(activationRoot);
|
|
3176
|
+
validateFrozenReplayEvalIdentity(target, frozenReplayEvalIdentity);
|
|
2982
3177
|
const resolvedBudget = resolveCompileBudget(target, input);
|
|
2983
3178
|
routeSelectionStartedAtNs = monotonicClockNs();
|
|
2984
3179
|
const compile = compileRuntimeFromActivation(activationRoot, {
|
|
@@ -2995,11 +3190,29 @@ export function compileRuntimeContext(input) {
|
|
|
2995
3190
|
...(selectionMode !== undefined ? { selectionMode } : {})
|
|
2996
3191
|
});
|
|
2997
3192
|
routeSelectionMs = elapsedMsFrom(routeSelectionStartedAtNs);
|
|
3193
|
+
const selectionEngine = selectionMode ?? "flat_rank_v1";
|
|
2998
3194
|
const compileResponse = {
|
|
2999
3195
|
...compile.response,
|
|
3000
3196
|
diagnostics: {
|
|
3001
3197
|
...compile.response.diagnostics,
|
|
3002
|
-
notes: uniqueNotes([
|
|
3198
|
+
notes: uniqueNotes([
|
|
3199
|
+
...compile.response.diagnostics.notes,
|
|
3200
|
+
...resolvedBudget.notes,
|
|
3201
|
+
`selection_engine=${selectionEngine}`,
|
|
3202
|
+
...(comparativeMode === null
|
|
3203
|
+
? []
|
|
3204
|
+
: [
|
|
3205
|
+
`comparative_mode=${comparativeMode}`,
|
|
3206
|
+
`comparative_mode_plan=${mode}+${selectionEngine}`
|
|
3207
|
+
]),
|
|
3208
|
+
...(frozenReplayEvalIdentity === null
|
|
3209
|
+
? []
|
|
3210
|
+
: [
|
|
3211
|
+
`replay_eval_pack_id=${frozenReplayEvalIdentity.packId}`,
|
|
3212
|
+
`replay_eval_router_identity=${frozenReplayEvalIdentity.routerIdentity ?? "null"}`
|
|
3213
|
+
]),
|
|
3214
|
+
"OpenClaw remains the runtime owner"
|
|
3215
|
+
])
|
|
3003
3216
|
}
|
|
3004
3217
|
};
|
|
3005
3218
|
promptAssemblyStartedAtNs = monotonicClockNs();
|
|
@@ -3882,6 +4095,7 @@ export function runRuntimeTurn(turn, options = {}) {
|
|
|
3882
4095
|
...(turn.mode !== undefined ? { mode: turn.mode } : {}),
|
|
3883
4096
|
...(turn.selectionMode !== undefined ? { selectionMode: turn.selectionMode } : {}),
|
|
3884
4097
|
...(turn.runtimeHints !== undefined ? { runtimeHints: turn.runtimeHints } : {}),
|
|
4098
|
+
...(options._frozenReplayEvalIdentity !== undefined ? { _frozenReplayEvalIdentity: options._frozenReplayEvalIdentity } : {}),
|
|
3885
4099
|
_suppressServeLog: true
|
|
3886
4100
|
};
|
|
3887
4101
|
const compileResult = compileRuntimeContext(compileInput);
|
|
@@ -4130,6 +4344,7 @@ function ensureRecordedSessionTrace(trace) {
|
|
|
4130
4344
|
if (trace.turns.length === 0) {
|
|
4131
4345
|
throw new Error("recorded session trace requires at least one turn");
|
|
4132
4346
|
}
|
|
4347
|
+
resolveRecordedSessionReplayEvalTurnCount(trace.turns.length, trace.evalTurnCount, "evalTurnCount");
|
|
4133
4348
|
for (const [index, cue] of trace.seedCues.entries()) {
|
|
4134
4349
|
normalizeNonEmptyString(cue.cueId, `seedCues[${index}].cueId`);
|
|
4135
4350
|
normalizeIsoTimestamp(cue.createdAt, `seedCues[${index}].createdAt`);
|
|
@@ -4181,6 +4396,52 @@ function uniqueStringsInOrder(values) {
|
|
|
4181
4396
|
}
|
|
4182
4397
|
return unique;
|
|
4183
4398
|
}
|
|
4399
|
+
function resolveRecordedSessionReplayEvalTurnCount(turnCount, value, fieldName) {
|
|
4400
|
+
if (turnCount === 0) {
|
|
4401
|
+
return 0;
|
|
4402
|
+
}
|
|
4403
|
+
if (value === undefined) {
|
|
4404
|
+
return 1;
|
|
4405
|
+
}
|
|
4406
|
+
const normalized = normalizeNonNegativeInteger(value, fieldName, value);
|
|
4407
|
+
if (normalized < 1) {
|
|
4408
|
+
throw new Error(`${fieldName} must be at least 1 when turns are present`);
|
|
4409
|
+
}
|
|
4410
|
+
if (normalized > turnCount) {
|
|
4411
|
+
throw new Error(`${fieldName} cannot exceed turns.length (${turnCount})`);
|
|
4412
|
+
}
|
|
4413
|
+
return normalized;
|
|
4414
|
+
}
|
|
4415
|
+
function buildRecordedSessionReplayTurnPlan(fixture) {
|
|
4416
|
+
const evalTurnCount = resolveRecordedSessionReplayEvalTurnCount(fixture.turns.length, fixture.evalTurnCount, "evalTurnCount");
|
|
4417
|
+
const trainTurnCount = Math.max(0, fixture.turns.length - evalTurnCount);
|
|
4418
|
+
return {
|
|
4419
|
+
trainTurns: fixture.turns.slice(0, trainTurnCount),
|
|
4420
|
+
evalTurns: fixture.turns.slice(trainTurnCount),
|
|
4421
|
+
trainTurnCount,
|
|
4422
|
+
evalTurnCount
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
function cloneRecordedSessionReplayFrozenEvalIdentity(value) {
|
|
4426
|
+
if (value === null) {
|
|
4427
|
+
return null;
|
|
4428
|
+
}
|
|
4429
|
+
return {
|
|
4430
|
+
packId: value.packId,
|
|
4431
|
+
routerIdentity: value.routerIdentity
|
|
4432
|
+
};
|
|
4433
|
+
}
|
|
4434
|
+
function readRecordedSessionReplayFrozenEvalIdentity(activationRoot) {
|
|
4435
|
+
const inspection = inspectActivationState(activationRoot);
|
|
4436
|
+
const activePointer = inspection.pointers.active;
|
|
4437
|
+
if (inspection.active === null || activePointer === null) {
|
|
4438
|
+
return null;
|
|
4439
|
+
}
|
|
4440
|
+
return {
|
|
4441
|
+
packId: activePointer.packId,
|
|
4442
|
+
routerIdentity: inspection.active.routerIdentity ?? null
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4184
4445
|
function buildRecordedSessionSeedExport(trace) {
|
|
4185
4446
|
const agentId = normalizeOptionalString(trace.agentId) ?? DEFAULT_AGENT_ID;
|
|
4186
4447
|
const seedSessionId = `${trace.sessionId}-seed`;
|
|
@@ -4292,6 +4553,9 @@ function recordedSessionFixtureBase(trace) {
|
|
|
4292
4553
|
revision: trace.workspace.revision,
|
|
4293
4554
|
...(trace.workspace.labels !== undefined ? { labels: [...trace.workspace.labels] } : {})
|
|
4294
4555
|
},
|
|
4556
|
+
...(trace.evalTurnCount !== undefined
|
|
4557
|
+
? { evalTurnCount: resolveRecordedSessionReplayEvalTurnCount(trace.turns.length, trace.evalTurnCount, "evalTurnCount") }
|
|
4558
|
+
: {}),
|
|
4295
4559
|
seedBuiltAt: trace.seedBuiltAt,
|
|
4296
4560
|
seedActivatedAt: trace.seedActivatedAt,
|
|
4297
4561
|
seedExport: buildRecordedSessionSeedExport(trace),
|
|
@@ -4327,6 +4591,9 @@ function recordedSessionReplayFixtureBase(fixture) {
|
|
|
4327
4591
|
revision: fixture.workspace.revision,
|
|
4328
4592
|
...(fixture.workspace.labels !== undefined ? { labels: [...fixture.workspace.labels] } : {})
|
|
4329
4593
|
},
|
|
4594
|
+
...(fixture.evalTurnCount !== undefined
|
|
4595
|
+
? { evalTurnCount: resolveRecordedSessionReplayEvalTurnCount(fixture.turns.length, fixture.evalTurnCount, "evalTurnCount") }
|
|
4596
|
+
: {}),
|
|
4330
4597
|
seedBuiltAt: fixture.seedBuiltAt,
|
|
4331
4598
|
seedActivatedAt: fixture.seedActivatedAt,
|
|
4332
4599
|
seedExport: fixture.seedExport,
|
|
@@ -4377,7 +4644,49 @@ function buildRecordedSessionTurnObservability(result) {
|
|
|
4377
4644
|
freshestCreatedAt: observability.teacherFreshness.freshestCreatedAt ?? freshestSource?.freshestCreatedAt ?? null
|
|
4378
4645
|
};
|
|
4379
4646
|
}
|
|
4380
|
-
|
|
4647
|
+
const RECORDED_SESSION_REPLAY_MODE_PLAN = {
|
|
4648
|
+
no_brain: {
|
|
4649
|
+
activationStrategy: "no_brain",
|
|
4650
|
+
runtimeMode: null,
|
|
4651
|
+
routeModeRequested: null,
|
|
4652
|
+
selectionEngine: null,
|
|
4653
|
+
learnedRouting: false
|
|
4654
|
+
},
|
|
4655
|
+
vector_only: {
|
|
4656
|
+
activationStrategy: "seed_pack",
|
|
4657
|
+
runtimeMode: "vector_only",
|
|
4658
|
+
routeModeRequested: "heuristic",
|
|
4659
|
+
selectionEngine: "flat_rank_v1",
|
|
4660
|
+
learnedRouting: false
|
|
4661
|
+
},
|
|
4662
|
+
graph_prior_only: {
|
|
4663
|
+
activationStrategy: "seed_pack",
|
|
4664
|
+
runtimeMode: "graph_prior_only",
|
|
4665
|
+
routeModeRequested: "heuristic",
|
|
4666
|
+
selectionEngine: "graph_walk_v1",
|
|
4667
|
+
learnedRouting: false
|
|
4668
|
+
},
|
|
4669
|
+
learned_route: {
|
|
4670
|
+
activationStrategy: "continuous_learned_loop",
|
|
4671
|
+
runtimeMode: "learned_route",
|
|
4672
|
+
routeModeRequested: "learned",
|
|
4673
|
+
selectionEngine: "graph_walk_v1",
|
|
4674
|
+
learnedRouting: true
|
|
4675
|
+
}
|
|
4676
|
+
};
|
|
4677
|
+
function recordedSessionReplayModePlan(mode) {
|
|
4678
|
+
return RECORDED_SESSION_REPLAY_MODE_PLAN[mode];
|
|
4679
|
+
}
|
|
4680
|
+
function buildRecordedSessionReplayTurnInput(turnFixture, modeRoot, replayMode) {
|
|
4681
|
+
const plan = recordedSessionReplayModePlan(replayMode);
|
|
4682
|
+
return {
|
|
4683
|
+
...turnFixture.turn,
|
|
4684
|
+
...(plan.runtimeMode === null ? {} : { mode: plan.runtimeMode }),
|
|
4685
|
+
export: buildRecordedSessionTurnExportRoot(modeRoot, turnFixture.turnId)
|
|
4686
|
+
};
|
|
4687
|
+
}
|
|
4688
|
+
function buildRecordedSessionTurnReport(replayMode, turnFixture, result, options) {
|
|
4689
|
+
const plan = recordedSessionReplayModePlan(replayMode);
|
|
4381
4690
|
const compileOk = result.ok;
|
|
4382
4691
|
const selectedContextTexts = compileOk ? result.compileResponse.selectedContext.map((block) => block.text) : [];
|
|
4383
4692
|
const selectedContextIds = compileOk ? result.compileResponse.selectedContext.map((block) => block.id) : [];
|
|
@@ -4390,10 +4699,15 @@ function buildRecordedSessionTurnReport(turnFixture, result, options) {
|
|
|
4390
4699
|
const eventExportDigest = result.eventExport.ok === true ? result.eventExport.normalizedEventExport.provenance.exportDigest : null;
|
|
4391
4700
|
return {
|
|
4392
4701
|
turnId: turnFixture.turnId,
|
|
4702
|
+
replayMode,
|
|
4703
|
+
phase: options.phase,
|
|
4393
4704
|
compileOk,
|
|
4394
4705
|
fallbackToStaticContext: result.fallbackToStaticContext,
|
|
4395
4706
|
hardRequirementViolated: result.hardRequirementViolated,
|
|
4396
4707
|
activePackId: result.ok ? result.activePackId : null,
|
|
4708
|
+
modeRequested: plan.routeModeRequested,
|
|
4709
|
+
modeEffective: result.ok ? result.compileResponse.diagnostics.modeEffective : null,
|
|
4710
|
+
selectionEngine: plan.selectionEngine,
|
|
4397
4711
|
usedLearnedRouteFn: result.ok ? result.compileResponse.diagnostics.usedLearnedRouteFn : false,
|
|
4398
4712
|
routerIdentity: result.ok ? result.compileResponse.diagnostics.routerIdentity : null,
|
|
4399
4713
|
selectionDigest: result.ok ? result.compileResponse.diagnostics.selectionDigest : null,
|
|
@@ -4447,7 +4761,7 @@ function buildRecordedSessionReplayScannerWarnings(mode, turns, activePackChange
|
|
|
4447
4761
|
if (turns.some((turn) => turn.compileOk) && selectionDigestTurnCount === 0) {
|
|
4448
4762
|
warnings.push("selection_digest_missing");
|
|
4449
4763
|
}
|
|
4450
|
-
if (mode === "
|
|
4764
|
+
if (mode === "learned_route" && activePackChangeCount === 0) {
|
|
4451
4765
|
warnings.push("active_pack_never_moved");
|
|
4452
4766
|
}
|
|
4453
4767
|
return warnings;
|
|
@@ -4479,17 +4793,33 @@ function buildRecordedSessionReplayScannerEvidence(mode, turns) {
|
|
|
4479
4793
|
warnings: buildRecordedSessionReplayScannerWarnings(mode, turns, activePackChangeCount)
|
|
4480
4794
|
};
|
|
4481
4795
|
}
|
|
4482
|
-
function
|
|
4796
|
+
function buildRecordedSessionReplayModeQualityScore(turns, compileOkCount, phraseHitCount, phraseCount) {
|
|
4797
|
+
if (turns.length === 0) {
|
|
4798
|
+
return 0;
|
|
4799
|
+
}
|
|
4800
|
+
// Score mode quality from aggregate replay coverage so multi-phrase turns
|
|
4801
|
+
// keep their full weight instead of being flattened into a per-turn average.
|
|
4802
|
+
const compileScore = (compileOkCount / turns.length) * 40;
|
|
4803
|
+
const phraseScore = phraseCount === 0 ? 60 : (phraseHitCount / phraseCount) * 60;
|
|
4804
|
+
return Math.min(100, Math.round(compileScore + phraseScore));
|
|
4805
|
+
}
|
|
4806
|
+
function buildRecordedSessionReplayModeSummary(mode, turns, options = {}) {
|
|
4807
|
+
const plan = recordedSessionReplayModePlan(mode);
|
|
4483
4808
|
const compileOkCount = turns.filter((turn) => turn.compileOk).length;
|
|
4484
4809
|
const phraseHitCount = turns.reduce((sum, turn) => sum + turn.phraseHits.length, 0);
|
|
4485
4810
|
const phraseCount = turns.reduce((sum, turn) => sum + turn.expectedContextPhrases.length, 0);
|
|
4486
4811
|
const usedLearnedRouteTurnCount = turns.filter((turn) => turn.usedLearnedRouteFn).length;
|
|
4487
4812
|
const promotionCount = turns.filter((turn) => turn.promoted).length;
|
|
4488
|
-
const qualityScore =
|
|
4813
|
+
const qualityScore = buildRecordedSessionReplayModeQualityScore(turns, compileOkCount, phraseHitCount, phraseCount);
|
|
4489
4814
|
const packIds = uniqueStringsInOrder(turns.map((turn) => turn.activePackId).filter(isPresent));
|
|
4815
|
+
const trainTurnCount = turns.filter((turn) => turn.phase === "train").length;
|
|
4816
|
+
const evalTurnCount = turns.filter((turn) => turn.phase === "eval").length;
|
|
4490
4817
|
const scannerEvidence = buildRecordedSessionReplayScannerEvidence(mode, turns);
|
|
4491
4818
|
const base = {
|
|
4492
4819
|
mode,
|
|
4820
|
+
activationStrategy: plan.activationStrategy,
|
|
4821
|
+
modeRequested: plan.routeModeRequested,
|
|
4822
|
+
selectionEngine: plan.selectionEngine,
|
|
4493
4823
|
qualityScore,
|
|
4494
4824
|
compileOkCount,
|
|
4495
4825
|
phraseHitCount,
|
|
@@ -4497,6 +4827,10 @@ function buildRecordedSessionReplayModeSummary(mode, turns) {
|
|
|
4497
4827
|
usedLearnedRouteTurnCount,
|
|
4498
4828
|
promotionCount,
|
|
4499
4829
|
packIds,
|
|
4830
|
+
trainTurnCount,
|
|
4831
|
+
evalTurnCount,
|
|
4832
|
+
frozenEvalPackId: options.frozenEvalIdentity?.packId ?? null,
|
|
4833
|
+
frozenEvalRouterIdentity: options.frozenEvalIdentity?.routerIdentity ?? null,
|
|
4500
4834
|
scannerEvidence
|
|
4501
4835
|
};
|
|
4502
4836
|
return {
|
|
@@ -4505,10 +4839,15 @@ function buildRecordedSessionReplayModeSummary(mode, turns) {
|
|
|
4505
4839
|
summary: base,
|
|
4506
4840
|
turns: turns.map((turn) => ({
|
|
4507
4841
|
turnId: turn.turnId,
|
|
4842
|
+
phase: turn.phase,
|
|
4508
4843
|
qualityScore: turn.qualityScore,
|
|
4509
4844
|
phraseHits: turn.phraseHits,
|
|
4510
4845
|
missedPhrases: turn.missedPhrases,
|
|
4511
4846
|
compileOk: turn.compileOk,
|
|
4847
|
+
replayMode: turn.replayMode,
|
|
4848
|
+
modeRequested: turn.modeRequested,
|
|
4849
|
+
modeEffective: turn.modeEffective,
|
|
4850
|
+
selectionEngine: turn.selectionEngine,
|
|
4512
4851
|
usedLearnedRouteFn: turn.usedLearnedRouteFn,
|
|
4513
4852
|
activePackId: turn.activePackId,
|
|
4514
4853
|
selectionDigest: turn.selectionDigest,
|
|
@@ -4518,16 +4857,19 @@ function buildRecordedSessionReplayModeSummary(mode, turns) {
|
|
|
4518
4857
|
})
|
|
4519
4858
|
};
|
|
4520
4859
|
}
|
|
4521
|
-
function buildRecordedSessionReplayModeReport(mode, turns) {
|
|
4860
|
+
function buildRecordedSessionReplayModeReport(mode, turns, options = {}) {
|
|
4522
4861
|
return {
|
|
4523
4862
|
mode,
|
|
4524
|
-
summary: buildRecordedSessionReplayModeSummary(mode, turns),
|
|
4863
|
+
summary: buildRecordedSessionReplayModeSummary(mode, turns, options),
|
|
4525
4864
|
turns: [...turns]
|
|
4526
4865
|
};
|
|
4527
4866
|
}
|
|
4528
4867
|
function buildRecordedSessionReplayScoreHash(modes) {
|
|
4529
4868
|
return checksumJsonPayload(modes.map((mode) => ({
|
|
4530
4869
|
mode: mode.mode,
|
|
4870
|
+
activationStrategy: mode.summary.activationStrategy,
|
|
4871
|
+
modeRequested: mode.summary.modeRequested,
|
|
4872
|
+
selectionEngine: mode.summary.selectionEngine,
|
|
4531
4873
|
qualityScore: mode.summary.qualityScore,
|
|
4532
4874
|
compileOkCount: mode.summary.compileOkCount,
|
|
4533
4875
|
phraseHitCount: mode.summary.phraseHitCount,
|
|
@@ -4535,6 +4877,10 @@ function buildRecordedSessionReplayScoreHash(modes) {
|
|
|
4535
4877
|
usedLearnedRouteTurnCount: mode.summary.usedLearnedRouteTurnCount,
|
|
4536
4878
|
promotionCount: mode.summary.promotionCount,
|
|
4537
4879
|
packIds: mode.summary.packIds,
|
|
4880
|
+
trainTurnCount: mode.summary.trainTurnCount,
|
|
4881
|
+
evalTurnCount: mode.summary.evalTurnCount,
|
|
4882
|
+
frozenEvalPackId: mode.summary.frozenEvalPackId,
|
|
4883
|
+
frozenEvalRouterIdentity: mode.summary.frozenEvalRouterIdentity,
|
|
4538
4884
|
scannerEvidence: mode.summary.scannerEvidence,
|
|
4539
4885
|
scoreHash: mode.summary.scoreHash
|
|
4540
4886
|
})));
|
|
@@ -4625,45 +4971,42 @@ function runRecordedSessionNoBrainMode(rootDir, fixture) {
|
|
|
4625
4971
|
const modeRoot = prepareReplayModeRoot(rootDir, "no_brain");
|
|
4626
4972
|
const activationRoot = path.join(modeRoot, "activation");
|
|
4627
4973
|
const turns = fixture.turns.map((turnFixture) => {
|
|
4628
|
-
const result = runRuntimeTurn({
|
|
4629
|
-
...turnFixture.turn,
|
|
4630
|
-
export: buildRecordedSessionTurnExportRoot(modeRoot, turnFixture.turnId)
|
|
4631
|
-
}, {
|
|
4974
|
+
const result = runRuntimeTurn(buildRecordedSessionReplayTurnInput(turnFixture, modeRoot, "no_brain"), {
|
|
4632
4975
|
activationRoot,
|
|
4633
4976
|
failOpen: true
|
|
4634
4977
|
});
|
|
4635
|
-
return buildRecordedSessionTurnReport(turnFixture, result, {
|
|
4978
|
+
return buildRecordedSessionTurnReport("no_brain", turnFixture, result, {
|
|
4979
|
+
phase: "eval",
|
|
4636
4980
|
compileActiveVersion: null,
|
|
4637
4981
|
promoted: false
|
|
4638
4982
|
});
|
|
4639
4983
|
});
|
|
4640
4984
|
return buildRecordedSessionReplayModeReport("no_brain", turns);
|
|
4641
4985
|
}
|
|
4642
|
-
function
|
|
4643
|
-
const modeRoot = prepareReplayModeRoot(rootDir,
|
|
4986
|
+
function runRecordedSessionSeededComparativeMode(rootDir, fixture, replayMode) {
|
|
4987
|
+
const modeRoot = prepareReplayModeRoot(rootDir, replayMode);
|
|
4644
4988
|
const { activationRoot } = prepareSeedActivation(modeRoot, fixture);
|
|
4645
4989
|
const turns = fixture.turns.map((turnFixture) => {
|
|
4646
|
-
const result = runRuntimeTurn({
|
|
4647
|
-
...turnFixture.turn,
|
|
4648
|
-
export: buildRecordedSessionTurnExportRoot(modeRoot, turnFixture.turnId)
|
|
4649
|
-
}, {
|
|
4990
|
+
const result = runRuntimeTurn(buildRecordedSessionReplayTurnInput(turnFixture, modeRoot, replayMode), {
|
|
4650
4991
|
activationRoot,
|
|
4651
4992
|
failOpen: false
|
|
4652
4993
|
});
|
|
4653
|
-
return buildRecordedSessionTurnReport(turnFixture, result, {
|
|
4994
|
+
return buildRecordedSessionTurnReport(replayMode, turnFixture, result, {
|
|
4995
|
+
phase: "eval",
|
|
4654
4996
|
compileActiveVersion: 1,
|
|
4655
4997
|
promoted: false
|
|
4656
4998
|
});
|
|
4657
4999
|
});
|
|
4658
|
-
return buildRecordedSessionReplayModeReport(
|
|
5000
|
+
return buildRecordedSessionReplayModeReport(replayMode, turns);
|
|
4659
5001
|
}
|
|
4660
|
-
function
|
|
4661
|
-
const modeRoot = prepareReplayModeRoot(rootDir, "
|
|
5002
|
+
function runRecordedSessionLearnedRouteMode(rootDir, fixture) {
|
|
5003
|
+
const modeRoot = prepareReplayModeRoot(rootDir, "learned_route");
|
|
4662
5004
|
const { activationRoot } = prepareSeedActivation(modeRoot, fixture);
|
|
4663
5005
|
const loopRoot = path.join(modeRoot, "loop");
|
|
5006
|
+
const turnPlan = buildRecordedSessionReplayTurnPlan(fixture);
|
|
4664
5007
|
let state;
|
|
4665
5008
|
const turns = [];
|
|
4666
|
-
for (const turnFixture of
|
|
5009
|
+
for (const turnFixture of turnPlan.trainTurns) {
|
|
4667
5010
|
const compileCreatedAt = normalizeIsoTimestamp(turnFixture.turn.compile?.createdAt, "turn.compile.createdAt", turnFixture.turn.createdAt);
|
|
4668
5011
|
const result = runContinuousProductLoopTurn({
|
|
4669
5012
|
activationRoot,
|
|
@@ -4682,12 +5025,40 @@ function runRecordedSessionLearnedReplayMode(rootDir, fixture) {
|
|
|
4682
5025
|
promoteUpdatedAt: addMinutes(compileCreatedAt, 4)
|
|
4683
5026
|
});
|
|
4684
5027
|
state = result.state;
|
|
4685
|
-
turns.push(buildRecordedSessionTurnReport(turnFixture, result.turn, {
|
|
5028
|
+
turns.push(buildRecordedSessionTurnReport("learned_route", turnFixture, result.turn, {
|
|
5029
|
+
phase: "train",
|
|
4686
5030
|
compileActiveVersion: result.compileActiveVersion,
|
|
4687
5031
|
promoted: result.learning.promoted
|
|
4688
5032
|
}));
|
|
4689
5033
|
}
|
|
4690
|
-
|
|
5034
|
+
const frozenEvalIdentity = readRecordedSessionReplayFrozenEvalIdentity(activationRoot);
|
|
5035
|
+
for (const turnFixture of turnPlan.evalTurns) {
|
|
5036
|
+
const currentState = cloneContinuousProductLoopState(state ??
|
|
5037
|
+
createContinuousProductLoopState({
|
|
5038
|
+
activationRoot,
|
|
5039
|
+
loopRoot
|
|
5040
|
+
}));
|
|
5041
|
+
currentState.activationRoot = activationRoot;
|
|
5042
|
+
currentState.loopRoot = loopRoot;
|
|
5043
|
+
const activeBeforeTurn = syncContinuousActivePack(currentState);
|
|
5044
|
+
state = cloneContinuousProductLoopState(currentState);
|
|
5045
|
+
const result = runRuntimeTurn({
|
|
5046
|
+
...turnFixture.turn,
|
|
5047
|
+
export: buildRecordedSessionTurnExportRoot(modeRoot, turnFixture.turnId)
|
|
5048
|
+
}, {
|
|
5049
|
+
activationRoot,
|
|
5050
|
+
failOpen: false,
|
|
5051
|
+
...(frozenEvalIdentity === null ? {} : { _frozenReplayEvalIdentity: frozenEvalIdentity })
|
|
5052
|
+
});
|
|
5053
|
+
turns.push(buildRecordedSessionTurnReport("learned_route", turnFixture, result, {
|
|
5054
|
+
phase: "eval",
|
|
5055
|
+
compileActiveVersion: activeBeforeTurn?.version ?? 0,
|
|
5056
|
+
promoted: false
|
|
5057
|
+
}));
|
|
5058
|
+
}
|
|
5059
|
+
return buildRecordedSessionReplayModeReport("learned_route", turns, {
|
|
5060
|
+
frozenEvalIdentity: cloneRecordedSessionReplayFrozenEvalIdentity(frozenEvalIdentity)
|
|
5061
|
+
});
|
|
4691
5062
|
}
|
|
4692
5063
|
export function runRecordedSessionReplay(rootDir, fixture) {
|
|
4693
5064
|
const resolvedRoot = path.resolve(normalizeNonEmptyString(rootDir, "rootDir"));
|
|
@@ -4701,8 +5072,9 @@ export function runRecordedSessionReplay(rootDir, fixture) {
|
|
|
4701
5072
|
}
|
|
4702
5073
|
const modes = [
|
|
4703
5074
|
runRecordedSessionNoBrainMode(resolvedRoot, fixture),
|
|
4704
|
-
|
|
4705
|
-
|
|
5075
|
+
runRecordedSessionSeededComparativeMode(resolvedRoot, fixture, "vector_only"),
|
|
5076
|
+
runRecordedSessionSeededComparativeMode(resolvedRoot, fixture, "graph_prior_only"),
|
|
5077
|
+
runRecordedSessionLearnedRouteMode(resolvedRoot, fixture)
|
|
4706
5078
|
];
|
|
4707
5079
|
const ranking = modes
|
|
4708
5080
|
.map((mode) => ({
|
|
@@ -4754,7 +5126,14 @@ function rescoreRecordedSessionReplayTurn(turn) {
|
|
|
4754
5126
|
}
|
|
4755
5127
|
function rescoreRecordedSessionReplayMode(mode) {
|
|
4756
5128
|
const turns = mode.turns.map((turn) => rescoreRecordedSessionReplayTurn(turn));
|
|
4757
|
-
return buildRecordedSessionReplayModeReport(mode.mode, turns
|
|
5129
|
+
return buildRecordedSessionReplayModeReport(mode.mode, turns, {
|
|
5130
|
+
frozenEvalIdentity: mode.summary.frozenEvalPackId === null
|
|
5131
|
+
? null
|
|
5132
|
+
: {
|
|
5133
|
+
packId: mode.summary.frozenEvalPackId,
|
|
5134
|
+
routerIdentity: mode.summary.frozenEvalRouterIdentity ?? null
|
|
5135
|
+
}
|
|
5136
|
+
});
|
|
4758
5137
|
}
|
|
4759
5138
|
export function rescoreRecordedSessionReplayBundle(bundle) {
|
|
4760
5139
|
const modes = bundle.modes.map((mode) => rescoreRecordedSessionReplayMode(mode));
|
|
@@ -4775,6 +5154,558 @@ export function verifyRecordedSessionReplayBundleHashes(bundle) {
|
|
|
4775
5154
|
scoreHashMatches: rescored.scoreHash === bundle.scoreHash
|
|
4776
5155
|
};
|
|
4777
5156
|
}
|
|
5157
|
+
function buildRecordedSessionReplayProofEnvironment() {
|
|
5158
|
+
return {
|
|
5159
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_ENVIRONMENT_CONTRACT,
|
|
5160
|
+
runtimeOwner: "openclaw",
|
|
5161
|
+
generator: {
|
|
5162
|
+
packageName: "@openclawbrain/cli",
|
|
5163
|
+
entrypoint: "writeRecordedSessionReplayProofBundle",
|
|
5164
|
+
nodeVersion: process.version,
|
|
5165
|
+
platform: process.platform,
|
|
5166
|
+
arch: process.arch
|
|
5167
|
+
},
|
|
5168
|
+
determinism: {
|
|
5169
|
+
hashAlgorithm: "sha256",
|
|
5170
|
+
canonicalJson: true,
|
|
5171
|
+
modeOrder: [...RECORDED_SESSION_REPLAY_MODE_ORDER],
|
|
5172
|
+
scratchReplayRoot: "temporary_directory"
|
|
5173
|
+
}
|
|
5174
|
+
};
|
|
5175
|
+
}
|
|
5176
|
+
function toRecordedSessionReplayProofRate(numerator, denominator) {
|
|
5177
|
+
return denominator > 0 ? Number((numerator / denominator).toFixed(6)) : null;
|
|
5178
|
+
}
|
|
5179
|
+
function buildRecordedSessionReplayProofSummaryTables(bundle) {
|
|
5180
|
+
const orderedModes = orderedRecordedSessionReplayModes(bundle.modes);
|
|
5181
|
+
return {
|
|
5182
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_SUMMARY_TABLES_CONTRACT,
|
|
5183
|
+
traceId: bundle.traceId,
|
|
5184
|
+
winnerMode: bundle.summary.winnerMode,
|
|
5185
|
+
ranking: bundle.summary.ranking.map((entry) => ({ ...entry })),
|
|
5186
|
+
modes: orderedModes.map((mode) => ({
|
|
5187
|
+
mode: mode.mode,
|
|
5188
|
+
turnCount: mode.turns.length,
|
|
5189
|
+
qualityScore: mode.summary.qualityScore,
|
|
5190
|
+
compileOkCount: mode.summary.compileOkCount,
|
|
5191
|
+
phraseHitCount: mode.summary.phraseHitCount,
|
|
5192
|
+
phraseCount: mode.summary.phraseCount,
|
|
5193
|
+
usedLearnedRouteTurnCount: mode.summary.usedLearnedRouteTurnCount,
|
|
5194
|
+
promotionCount: mode.summary.promotionCount,
|
|
5195
|
+
exportTurnCount: mode.summary.scannerEvidence.exportTurnCount,
|
|
5196
|
+
humanLabelCount: mode.summary.scannerEvidence.humanLabelCount,
|
|
5197
|
+
attributedTurnCount: mode.summary.scannerEvidence.attributedTurnCount,
|
|
5198
|
+
activePackChangeCount: mode.summary.scannerEvidence.activePackChangeCount,
|
|
5199
|
+
warningCount: mode.summary.scannerEvidence.warnings.length,
|
|
5200
|
+
scoreHash: mode.summary.scoreHash
|
|
5201
|
+
})),
|
|
5202
|
+
turns: orderedModes.flatMap((mode) => mode.turns.map((turn) => ({
|
|
5203
|
+
mode: mode.mode,
|
|
5204
|
+
turnId: turn.turnId,
|
|
5205
|
+
qualityScore: turn.qualityScore,
|
|
5206
|
+
compileOk: turn.compileOk,
|
|
5207
|
+
phraseHitCount: turn.phraseHits.length,
|
|
5208
|
+
phraseCount: turn.expectedContextPhrases.length,
|
|
5209
|
+
usedLearnedRouteFn: turn.usedLearnedRouteFn,
|
|
5210
|
+
promoted: turn.promoted,
|
|
5211
|
+
activePackId: turn.activePackId,
|
|
5212
|
+
selectionDigest: turn.selectionDigest,
|
|
5213
|
+
eventExportDigest: turn.eventExportDigest,
|
|
5214
|
+
warningCount: turn.warnings.length
|
|
5215
|
+
})))
|
|
5216
|
+
};
|
|
5217
|
+
}
|
|
5218
|
+
function buildRecordedSessionReplayProofCoverageSnapshot(bundle) {
|
|
5219
|
+
const orderedModes = orderedRecordedSessionReplayModes(bundle.modes);
|
|
5220
|
+
const totalTurns = orderedModes.reduce((sum, mode) => sum + mode.turns.length, 0);
|
|
5221
|
+
const compileOkTurnCount = orderedModes.reduce((sum, mode) => sum + mode.summary.compileOkCount, 0);
|
|
5222
|
+
const phraseHitCount = orderedModes.reduce((sum, mode) => sum + mode.summary.phraseHitCount, 0);
|
|
5223
|
+
const phraseCount = orderedModes.reduce((sum, mode) => sum + mode.summary.phraseCount, 0);
|
|
5224
|
+
return {
|
|
5225
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_COVERAGE_SNAPSHOT_CONTRACT,
|
|
5226
|
+
traceId: bundle.traceId,
|
|
5227
|
+
winnerMode: bundle.summary.winnerMode,
|
|
5228
|
+
totalTurns,
|
|
5229
|
+
compileOkTurnCount,
|
|
5230
|
+
compileOkRate: toRecordedSessionReplayProofRate(compileOkTurnCount, totalTurns),
|
|
5231
|
+
phraseHitCount,
|
|
5232
|
+
phraseCount,
|
|
5233
|
+
phraseHitRate: toRecordedSessionReplayProofRate(phraseHitCount, phraseCount),
|
|
5234
|
+
modes: orderedModes.map((mode) => ({
|
|
5235
|
+
mode: mode.mode,
|
|
5236
|
+
turnCount: mode.turns.length,
|
|
5237
|
+
compileOkRate: toRecordedSessionReplayProofRate(mode.summary.compileOkCount, mode.turns.length),
|
|
5238
|
+
phraseHitRate: toRecordedSessionReplayProofRate(mode.summary.phraseHitCount, mode.summary.phraseCount),
|
|
5239
|
+
learnedRouteTurnRate: toRecordedSessionReplayProofRate(mode.summary.usedLearnedRouteTurnCount, mode.turns.length),
|
|
5240
|
+
attributedTurnRate: toRecordedSessionReplayProofRate(mode.summary.scannerEvidence.attributedTurnCount, mode.turns.length)
|
|
5241
|
+
}))
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
function buildRecordedSessionReplayProofHardeningSnapshot(bundle) {
|
|
5245
|
+
const orderedModes = orderedRecordedSessionReplayModes(bundle.modes);
|
|
5246
|
+
const totalTurns = orderedModes.reduce((sum, mode) => sum + mode.turns.length, 0);
|
|
5247
|
+
const compileFailureCount = orderedModes.reduce((sum, mode) => sum + (mode.turns.length - mode.summary.compileOkCount), 0);
|
|
5248
|
+
const warningCount = orderedModes.reduce((sum, mode) => sum + mode.summary.scannerEvidence.warnings.length, 0);
|
|
5249
|
+
const promotionCount = orderedModes.reduce((sum, mode) => sum + mode.summary.promotionCount, 0);
|
|
5250
|
+
const exportTurnCount = orderedModes.reduce((sum, mode) => sum + mode.summary.scannerEvidence.exportTurnCount, 0);
|
|
5251
|
+
const attributedTurnCount = orderedModes.reduce((sum, mode) => sum + mode.summary.scannerEvidence.attributedTurnCount, 0);
|
|
5252
|
+
return {
|
|
5253
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_HARDENING_SNAPSHOT_CONTRACT,
|
|
5254
|
+
traceId: bundle.traceId,
|
|
5255
|
+
totalTurns,
|
|
5256
|
+
compileFailureCount,
|
|
5257
|
+
compileFailureRate: toRecordedSessionReplayProofRate(compileFailureCount, totalTurns),
|
|
5258
|
+
warningCount,
|
|
5259
|
+
promotionCount,
|
|
5260
|
+
exportTurnCount,
|
|
5261
|
+
attributedTurnCount,
|
|
5262
|
+
modes: orderedModes.map((mode) => ({
|
|
5263
|
+
mode: mode.mode,
|
|
5264
|
+
warningCount: mode.summary.scannerEvidence.warnings.length,
|
|
5265
|
+
compileFailureCount: mode.turns.length - mode.summary.compileOkCount,
|
|
5266
|
+
promotionCount: mode.summary.promotionCount,
|
|
5267
|
+
exportTurnCount: mode.summary.scannerEvidence.exportTurnCount,
|
|
5268
|
+
attributedTurnCount: mode.summary.scannerEvidence.attributedTurnCount
|
|
5269
|
+
}))
|
|
5270
|
+
};
|
|
5271
|
+
}
|
|
5272
|
+
function buildRecordedSessionReplayProofSummary(input) {
|
|
5273
|
+
const rankingRows = input.summaryTables.ranking.map((entry, index) => `| ${index + 1} | ${entry.mode} | ${entry.qualityScore} |`);
|
|
5274
|
+
const modeRows = input.summaryTables.modes.map((mode) => `| ${mode.mode} | ${mode.turnCount} | ${mode.compileOkCount} | ${mode.phraseHitCount}/${mode.phraseCount} | ${mode.usedLearnedRouteTurnCount} | ${mode.promotionCount} | ${mode.exportTurnCount} | ${mode.humanLabelCount} | ${mode.warningCount} | ${mode.scoreHash} |`);
|
|
5275
|
+
const turnRows = input.summaryTables.turns.map((turn) => `| ${turn.mode} | ${turn.turnId} | ${turn.qualityScore} | ${turn.compileOk ? "yes" : "no"} | ${turn.phraseHitCount}/${turn.phraseCount} | ${turn.usedLearnedRouteFn ? "yes" : "no"} | ${turn.promoted ? "yes" : "no"} | ${turn.activePackId ?? "none"} | ${turn.selectionDigest ?? "none"} |`);
|
|
5276
|
+
const coverageRows = input.coverageSnapshot.modes.map((mode) => `| ${mode.mode} | ${mode.turnCount} | ${mode.compileOkRate ?? "none"} | ${mode.phraseHitRate ?? "none"} | ${mode.learnedRouteTurnRate ?? "none"} | ${mode.attributedTurnRate ?? "none"} |`);
|
|
5277
|
+
const hardeningRows = input.hardeningSnapshot.modes.map((mode) => `| ${mode.mode} | ${mode.warningCount} | ${mode.compileFailureCount} | ${mode.promotionCount} | ${mode.exportTurnCount} | ${mode.attributedTurnCount} |`);
|
|
5278
|
+
return [
|
|
5279
|
+
"# Recorded Session Replay Proof Bundle",
|
|
5280
|
+
"",
|
|
5281
|
+
`- trace id: \`${input.bundle.traceId}\``,
|
|
5282
|
+
`- winner mode: \`${input.bundle.summary.winnerMode ?? "none"}\``,
|
|
5283
|
+
`- trace hash: \`${input.semanticHashes.traceHash}\``,
|
|
5284
|
+
`- fixture hash: \`${input.semanticHashes.fixtureHash}\``,
|
|
5285
|
+
`- score hash: \`${input.semanticHashes.scoreHash}\``,
|
|
5286
|
+
`- bundle hash: \`${input.semanticHashes.bundleHash}\``,
|
|
5287
|
+
"",
|
|
5288
|
+
"## Ranking",
|
|
5289
|
+
"| rank | mode | quality score |",
|
|
5290
|
+
"| --- | --- | ---: |",
|
|
5291
|
+
...(rankingRows.length === 0 ? ["| - | none | 0 |"] : rankingRows),
|
|
5292
|
+
"",
|
|
5293
|
+
"## Coverage Snapshot",
|
|
5294
|
+
`- compile ok turns: ${input.coverageSnapshot.compileOkTurnCount}/${input.coverageSnapshot.totalTurns}`,
|
|
5295
|
+
`- compile ok rate: ${input.coverageSnapshot.compileOkRate ?? "none"}`,
|
|
5296
|
+
`- phrase hits: ${input.coverageSnapshot.phraseHitCount}/${input.coverageSnapshot.phraseCount}`,
|
|
5297
|
+
`- phrase hit rate: ${input.coverageSnapshot.phraseHitRate ?? "none"}`,
|
|
5298
|
+
"",
|
|
5299
|
+
"| mode | turns | compile ok rate | phrase hit rate | learned route turn rate | attributed turn rate |",
|
|
5300
|
+
"| --- | ---: | ---: | ---: | ---: | ---: |",
|
|
5301
|
+
...(coverageRows.length === 0 ? ["| - | 0 | none | none | none | none |"] : coverageRows),
|
|
5302
|
+
"",
|
|
5303
|
+
"## Hardening Snapshot",
|
|
5304
|
+
`- compile failures: ${input.hardeningSnapshot.compileFailureCount}/${input.hardeningSnapshot.totalTurns}`,
|
|
5305
|
+
`- compile failure rate: ${input.hardeningSnapshot.compileFailureRate ?? "none"}`,
|
|
5306
|
+
`- warnings: ${input.hardeningSnapshot.warningCount}`,
|
|
5307
|
+
`- promotions: ${input.hardeningSnapshot.promotionCount}`,
|
|
5308
|
+
"",
|
|
5309
|
+
"| mode | warnings | compile failures | promotions | export turns | attributed turns |",
|
|
5310
|
+
"| --- | ---: | ---: | ---: | ---: | ---: |",
|
|
5311
|
+
...(hardeningRows.length === 0 ? ["| - | 0 | 0 | 0 | 0 | 0 |"] : hardeningRows),
|
|
5312
|
+
"",
|
|
5313
|
+
"## Mode Table",
|
|
5314
|
+
"| mode | turns | compile ok | phrase hits | learned route turns | promotions | export turns | human labels | warnings | score hash |",
|
|
5315
|
+
"| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |",
|
|
5316
|
+
...(modeRows.length === 0 ? ["| - | 0 | 0 | 0/0 | 0 | 0 | 0 | 0 | 0 | none |"] : modeRows),
|
|
5317
|
+
"",
|
|
5318
|
+
"## Turn Table",
|
|
5319
|
+
"| mode | turn | quality | compile ok | phrase hits | learned route | promoted | active pack | selection digest |",
|
|
5320
|
+
"| --- | --- | ---: | --- | ---: | --- | --- | --- | --- |",
|
|
5321
|
+
...(turnRows.length === 0 ? ["| - | none | 0 | no | 0/0 | no | no | none | none |"] : turnRows),
|
|
5322
|
+
""
|
|
5323
|
+
].join("\n");
|
|
5324
|
+
}
|
|
5325
|
+
function buildRecordedSessionReplayProofManifest(bundle) {
|
|
5326
|
+
return {
|
|
5327
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_MANIFEST_CONTRACT,
|
|
5328
|
+
traceId: bundle.traceId,
|
|
5329
|
+
source: bundle.source,
|
|
5330
|
+
recordedAt: bundle.recordedAt,
|
|
5331
|
+
generatedAt: bundle.generatedAt,
|
|
5332
|
+
hashAlgorithm: "sha256",
|
|
5333
|
+
modeOrder: [...RECORDED_SESSION_REPLAY_MODE_ORDER],
|
|
5334
|
+
contracts: {
|
|
5335
|
+
trace: RECORDED_SESSION_TRACE_CONTRACT,
|
|
5336
|
+
fixture: RECORDED_SESSION_FIXTURE_CONTRACT,
|
|
5337
|
+
bundle: RECORDED_SESSION_BUNDLE_CONTRACT,
|
|
5338
|
+
environment: RECORDED_SESSION_REPLAY_PROOF_ENVIRONMENT_CONTRACT,
|
|
5339
|
+
summaryTables: RECORDED_SESSION_REPLAY_PROOF_SUMMARY_TABLES_CONTRACT,
|
|
5340
|
+
coverageSnapshot: RECORDED_SESSION_REPLAY_PROOF_COVERAGE_SNAPSHOT_CONTRACT,
|
|
5341
|
+
hardeningSnapshot: RECORDED_SESSION_REPLAY_PROOF_HARDENING_SNAPSHOT_CONTRACT,
|
|
5342
|
+
hashes: RECORDED_SESSION_REPLAY_PROOF_HASHES_CONTRACT
|
|
5343
|
+
},
|
|
5344
|
+
hashes: {
|
|
5345
|
+
traceHash: bundle.traceHash,
|
|
5346
|
+
fixtureHash: bundle.fixtureHash,
|
|
5347
|
+
scoreHash: bundle.scoreHash,
|
|
5348
|
+
bundleHash: bundle.bundleHash
|
|
5349
|
+
},
|
|
5350
|
+
files: {
|
|
5351
|
+
trace: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.trace,
|
|
5352
|
+
fixture: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.fixture,
|
|
5353
|
+
bundle: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.bundle,
|
|
5354
|
+
environment: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.environment,
|
|
5355
|
+
summary: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.summary,
|
|
5356
|
+
summaryTables: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.summaryTables,
|
|
5357
|
+
coverageSnapshot: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.coverageSnapshot,
|
|
5358
|
+
hardeningSnapshot: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.hardeningSnapshot,
|
|
5359
|
+
hashes: RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.hashes,
|
|
5360
|
+
modes: RECORDED_SESSION_REPLAY_MODE_ORDER.map((mode) => ({
|
|
5361
|
+
mode,
|
|
5362
|
+
path: recordedSessionReplayModeOutputPath(mode)
|
|
5363
|
+
}))
|
|
5364
|
+
}
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5367
|
+
function validateRecordedSessionReplayProofManifest(manifest) {
|
|
5368
|
+
const errors = [];
|
|
5369
|
+
if (manifest.contract !== RECORDED_SESSION_REPLAY_PROOF_MANIFEST_CONTRACT) {
|
|
5370
|
+
errors.push("manifest contract is invalid");
|
|
5371
|
+
}
|
|
5372
|
+
if (canonicalJson(manifest.modeOrder ?? []) !== canonicalJson(RECORDED_SESSION_REPLAY_MODE_ORDER)) {
|
|
5373
|
+
errors.push("manifest modeOrder must stay fixed at no_brain, vector_only, graph_prior_only, learned_route");
|
|
5374
|
+
}
|
|
5375
|
+
if (manifest.hashAlgorithm !== "sha256") {
|
|
5376
|
+
errors.push("manifest hashAlgorithm must be sha256");
|
|
5377
|
+
}
|
|
5378
|
+
if (manifest.contracts?.trace !== RECORDED_SESSION_TRACE_CONTRACT ||
|
|
5379
|
+
manifest.contracts?.fixture !== RECORDED_SESSION_FIXTURE_CONTRACT ||
|
|
5380
|
+
manifest.contracts?.bundle !== RECORDED_SESSION_BUNDLE_CONTRACT ||
|
|
5381
|
+
manifest.contracts?.environment !== RECORDED_SESSION_REPLAY_PROOF_ENVIRONMENT_CONTRACT ||
|
|
5382
|
+
manifest.contracts?.summaryTables !== RECORDED_SESSION_REPLAY_PROOF_SUMMARY_TABLES_CONTRACT ||
|
|
5383
|
+
manifest.contracts?.coverageSnapshot !== RECORDED_SESSION_REPLAY_PROOF_COVERAGE_SNAPSHOT_CONTRACT ||
|
|
5384
|
+
manifest.contracts?.hardeningSnapshot !== RECORDED_SESSION_REPLAY_PROOF_HARDENING_SNAPSHOT_CONTRACT ||
|
|
5385
|
+
manifest.contracts?.hashes !== RECORDED_SESSION_REPLAY_PROOF_HASHES_CONTRACT) {
|
|
5386
|
+
errors.push("manifest contracts block is invalid");
|
|
5387
|
+
}
|
|
5388
|
+
if (manifest.files?.trace !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.trace ||
|
|
5389
|
+
manifest.files?.fixture !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.fixture ||
|
|
5390
|
+
manifest.files?.bundle !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.bundle ||
|
|
5391
|
+
manifest.files?.environment !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.environment ||
|
|
5392
|
+
manifest.files?.summary !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.summary ||
|
|
5393
|
+
manifest.files?.summaryTables !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.summaryTables ||
|
|
5394
|
+
manifest.files?.coverageSnapshot !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.coverageSnapshot ||
|
|
5395
|
+
manifest.files?.hardeningSnapshot !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.hardeningSnapshot ||
|
|
5396
|
+
manifest.files?.hashes !== RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.hashes) {
|
|
5397
|
+
errors.push("manifest top-level file layout is invalid");
|
|
5398
|
+
}
|
|
5399
|
+
const expectedModeFiles = RECORDED_SESSION_REPLAY_MODE_ORDER.map((mode) => ({
|
|
5400
|
+
mode,
|
|
5401
|
+
path: recordedSessionReplayModeOutputPath(mode)
|
|
5402
|
+
}));
|
|
5403
|
+
if (canonicalJson(manifest.files?.modes ?? []) !== canonicalJson(expectedModeFiles)) {
|
|
5404
|
+
errors.push("manifest mode outputs must stay under modes/<mode>.json in canonical order");
|
|
5405
|
+
}
|
|
5406
|
+
return errors;
|
|
5407
|
+
}
|
|
5408
|
+
function validateRecordedSessionReplayProofEnvironment(environment) {
|
|
5409
|
+
const errors = [];
|
|
5410
|
+
if (environment.contract !== RECORDED_SESSION_REPLAY_PROOF_ENVIRONMENT_CONTRACT) {
|
|
5411
|
+
errors.push("environment contract is invalid");
|
|
5412
|
+
}
|
|
5413
|
+
if (environment.runtimeOwner !== "openclaw") {
|
|
5414
|
+
errors.push("environment runtimeOwner must be openclaw");
|
|
5415
|
+
}
|
|
5416
|
+
if (environment.generator?.packageName !== "@openclawbrain/cli" ||
|
|
5417
|
+
environment.generator?.entrypoint !== "writeRecordedSessionReplayProofBundle") {
|
|
5418
|
+
errors.push("environment generator metadata is invalid");
|
|
5419
|
+
}
|
|
5420
|
+
if (normalizeOptionalString(environment.generator?.nodeVersion) === undefined ||
|
|
5421
|
+
normalizeOptionalString(environment.generator?.platform) === undefined ||
|
|
5422
|
+
normalizeOptionalString(environment.generator?.arch) === undefined) {
|
|
5423
|
+
errors.push("environment generator runtime fields are required");
|
|
5424
|
+
}
|
|
5425
|
+
if (environment.determinism?.hashAlgorithm !== "sha256" ||
|
|
5426
|
+
environment.determinism?.canonicalJson !== true ||
|
|
5427
|
+
canonicalJson(environment.determinism?.modeOrder ?? []) !== canonicalJson(RECORDED_SESSION_REPLAY_MODE_ORDER) ||
|
|
5428
|
+
environment.determinism?.scratchReplayRoot !== "temporary_directory") {
|
|
5429
|
+
errors.push("environment determinism block is invalid");
|
|
5430
|
+
}
|
|
5431
|
+
return errors;
|
|
5432
|
+
}
|
|
5433
|
+
function buildRecordedSessionReplayProofFileDigestEntries(rootDir, manifest) {
|
|
5434
|
+
const relativePaths = [
|
|
5435
|
+
manifest.files.trace,
|
|
5436
|
+
manifest.files.fixture,
|
|
5437
|
+
manifest.files.bundle,
|
|
5438
|
+
manifest.files.environment,
|
|
5439
|
+
manifest.files.summary,
|
|
5440
|
+
manifest.files.summaryTables,
|
|
5441
|
+
manifest.files.coverageSnapshot,
|
|
5442
|
+
manifest.files.hardeningSnapshot,
|
|
5443
|
+
manifest.files.modes.map((entry) => entry.path),
|
|
5444
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.manifest
|
|
5445
|
+
].flat();
|
|
5446
|
+
return relativePaths.map((relativePath, index) => ({
|
|
5447
|
+
path: relativePath,
|
|
5448
|
+
digest: checksumTextPayload(readRecordedSessionReplayProofText(rootDir, relativePath, `files[${index}]`))
|
|
5449
|
+
}));
|
|
5450
|
+
}
|
|
5451
|
+
function buildRecordedSessionReplayProofHashes(rootDir, manifest) {
|
|
5452
|
+
return {
|
|
5453
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_HASHES_CONTRACT,
|
|
5454
|
+
algorithm: "sha256",
|
|
5455
|
+
semantic: {
|
|
5456
|
+
traceHash: manifest.hashes.traceHash,
|
|
5457
|
+
fixtureHash: manifest.hashes.fixtureHash,
|
|
5458
|
+
scoreHash: manifest.hashes.scoreHash,
|
|
5459
|
+
bundleHash: manifest.hashes.bundleHash
|
|
5460
|
+
},
|
|
5461
|
+
files: buildRecordedSessionReplayProofFileDigestEntries(rootDir, manifest)
|
|
5462
|
+
};
|
|
5463
|
+
}
|
|
5464
|
+
export function loadRecordedSessionReplayProofBundle(rootDir) {
|
|
5465
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
5466
|
+
const manifestPath = path.join(resolvedRoot, RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.manifest);
|
|
5467
|
+
const manifest = readJsonFile(manifestPath);
|
|
5468
|
+
const tracePath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.trace, "manifest.files.trace");
|
|
5469
|
+
const fixturePath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.fixture, "manifest.files.fixture");
|
|
5470
|
+
const bundlePath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.bundle, "manifest.files.bundle");
|
|
5471
|
+
const environmentPath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.environment, "manifest.files.environment");
|
|
5472
|
+
const summaryPath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.summary, "manifest.files.summary");
|
|
5473
|
+
const summaryTablesPath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.summaryTables, "manifest.files.summaryTables");
|
|
5474
|
+
const coverageSnapshotPath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.coverageSnapshot, "manifest.files.coverageSnapshot");
|
|
5475
|
+
const hardeningSnapshotPath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.hardeningSnapshot, "manifest.files.hardeningSnapshot");
|
|
5476
|
+
const hashesPath = resolveRecordedSessionReplayProofPath(resolvedRoot, manifest.files.hashes, "manifest.files.hashes");
|
|
5477
|
+
const modeOutputs = (manifest.files.modes ?? []).map((entry, index) => {
|
|
5478
|
+
const outputPath = resolveRecordedSessionReplayProofPath(resolvedRoot, entry.path, `manifest.files.modes[${index}].path`);
|
|
5479
|
+
return {
|
|
5480
|
+
mode: entry.mode,
|
|
5481
|
+
path: outputPath,
|
|
5482
|
+
report: readJsonFile(outputPath)
|
|
5483
|
+
};
|
|
5484
|
+
});
|
|
5485
|
+
return {
|
|
5486
|
+
rootDir: resolvedRoot,
|
|
5487
|
+
manifestPath,
|
|
5488
|
+
tracePath,
|
|
5489
|
+
fixturePath,
|
|
5490
|
+
bundlePath,
|
|
5491
|
+
environmentPath,
|
|
5492
|
+
summaryPath,
|
|
5493
|
+
summaryTablesPath,
|
|
5494
|
+
coverageSnapshotPath,
|
|
5495
|
+
hardeningSnapshotPath,
|
|
5496
|
+
hashesPath,
|
|
5497
|
+
manifest,
|
|
5498
|
+
trace: readJsonFile(tracePath),
|
|
5499
|
+
fixture: readJsonFile(fixturePath),
|
|
5500
|
+
bundle: readJsonFile(bundlePath),
|
|
5501
|
+
environment: readJsonFile(environmentPath),
|
|
5502
|
+
summaryText: readFileSync(summaryPath, "utf8"),
|
|
5503
|
+
summaryTables: readJsonFile(summaryTablesPath),
|
|
5504
|
+
coverageSnapshot: readJsonFile(coverageSnapshotPath),
|
|
5505
|
+
hardeningSnapshot: readJsonFile(hardeningSnapshotPath),
|
|
5506
|
+
hashes: readJsonFile(hashesPath),
|
|
5507
|
+
modeOutputs
|
|
5508
|
+
};
|
|
5509
|
+
}
|
|
5510
|
+
export function validateRecordedSessionReplayProofBundle(rootDir) {
|
|
5511
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
5512
|
+
try {
|
|
5513
|
+
const descriptor = loadRecordedSessionReplayProofBundle(resolvedRoot);
|
|
5514
|
+
const errors = [];
|
|
5515
|
+
errors.push(...validateRecordedSessionReplayProofManifest(descriptor.manifest));
|
|
5516
|
+
errors.push(...validateRecordedSessionReplayProofEnvironment(descriptor.environment));
|
|
5517
|
+
if (descriptor.trace.contract !== RECORDED_SESSION_TRACE_CONTRACT) {
|
|
5518
|
+
errors.push("trace.json contract is invalid");
|
|
5519
|
+
}
|
|
5520
|
+
if (descriptor.fixture.contract !== RECORDED_SESSION_FIXTURE_CONTRACT) {
|
|
5521
|
+
errors.push("fixture.json contract is invalid");
|
|
5522
|
+
}
|
|
5523
|
+
if (descriptor.bundle.contract !== RECORDED_SESSION_BUNDLE_CONTRACT) {
|
|
5524
|
+
errors.push("bundle.json contract is invalid");
|
|
5525
|
+
}
|
|
5526
|
+
if (descriptor.summaryTables.contract !== RECORDED_SESSION_REPLAY_PROOF_SUMMARY_TABLES_CONTRACT) {
|
|
5527
|
+
errors.push("summary-tables.json contract is invalid");
|
|
5528
|
+
}
|
|
5529
|
+
if (descriptor.coverageSnapshot.contract !== RECORDED_SESSION_REPLAY_PROOF_COVERAGE_SNAPSHOT_CONTRACT) {
|
|
5530
|
+
errors.push("coverage-snapshot.json contract is invalid");
|
|
5531
|
+
}
|
|
5532
|
+
if (descriptor.hardeningSnapshot.contract !== RECORDED_SESSION_REPLAY_PROOF_HARDENING_SNAPSHOT_CONTRACT) {
|
|
5533
|
+
errors.push("hardening-snapshot.json contract is invalid");
|
|
5534
|
+
}
|
|
5535
|
+
if (descriptor.hashes.contract !== RECORDED_SESSION_REPLAY_PROOF_HASHES_CONTRACT) {
|
|
5536
|
+
errors.push("hashes.json contract is invalid");
|
|
5537
|
+
}
|
|
5538
|
+
if (descriptor.hashes.algorithm !== "sha256") {
|
|
5539
|
+
errors.push("hashes.json algorithm must be sha256");
|
|
5540
|
+
}
|
|
5541
|
+
const rebuiltFixture = buildRecordedSessionReplayFixture(structuredClone(descriptor.trace));
|
|
5542
|
+
if (canonicalJson(rebuiltFixture) !== canonicalJson(descriptor.fixture)) {
|
|
5543
|
+
errors.push("fixture.json does not match the canonical fixture rebuilt from trace.json");
|
|
5544
|
+
}
|
|
5545
|
+
if (descriptor.bundle.traceHash !== descriptor.fixture.traceHash) {
|
|
5546
|
+
errors.push("bundle traceHash does not match fixture traceHash");
|
|
5547
|
+
}
|
|
5548
|
+
if (descriptor.bundle.fixtureHash !== descriptor.fixture.fixtureHash) {
|
|
5549
|
+
errors.push("bundle fixtureHash does not match fixture fixtureHash");
|
|
5550
|
+
}
|
|
5551
|
+
if (descriptor.manifest.hashes.traceHash !== descriptor.fixture.traceHash ||
|
|
5552
|
+
descriptor.manifest.hashes.fixtureHash !== descriptor.fixture.fixtureHash ||
|
|
5553
|
+
descriptor.manifest.hashes.scoreHash !== descriptor.bundle.scoreHash ||
|
|
5554
|
+
descriptor.manifest.hashes.bundleHash !== descriptor.bundle.bundleHash) {
|
|
5555
|
+
errors.push("manifest hashes do not match the replay artifacts");
|
|
5556
|
+
}
|
|
5557
|
+
const summaryTables = buildRecordedSessionReplayProofSummaryTables(descriptor.bundle);
|
|
5558
|
+
if (canonicalJson(summaryTables) !== canonicalJson(descriptor.summaryTables)) {
|
|
5559
|
+
errors.push("summary-tables.json does not match bundle.json");
|
|
5560
|
+
}
|
|
5561
|
+
const coverageSnapshot = buildRecordedSessionReplayProofCoverageSnapshot(descriptor.bundle);
|
|
5562
|
+
if (canonicalJson(coverageSnapshot) !== canonicalJson(descriptor.coverageSnapshot)) {
|
|
5563
|
+
errors.push("coverage-snapshot.json does not match bundle.json");
|
|
5564
|
+
}
|
|
5565
|
+
const hardeningSnapshot = buildRecordedSessionReplayProofHardeningSnapshot(descriptor.bundle);
|
|
5566
|
+
if (canonicalJson(hardeningSnapshot) !== canonicalJson(descriptor.hardeningSnapshot)) {
|
|
5567
|
+
errors.push("hardening-snapshot.json does not match bundle.json");
|
|
5568
|
+
}
|
|
5569
|
+
const expectedSummaryText = buildRecordedSessionReplayProofSummary({
|
|
5570
|
+
bundle: descriptor.bundle,
|
|
5571
|
+
summaryTables: descriptor.summaryTables,
|
|
5572
|
+
coverageSnapshot: descriptor.coverageSnapshot,
|
|
5573
|
+
hardeningSnapshot: descriptor.hardeningSnapshot,
|
|
5574
|
+
semanticHashes: descriptor.hashes.semantic
|
|
5575
|
+
});
|
|
5576
|
+
if (descriptor.summaryText !== expectedSummaryText) {
|
|
5577
|
+
errors.push("summary.md does not match the canonical replay proof summary");
|
|
5578
|
+
}
|
|
5579
|
+
const expectedModeCount = descriptor.bundle.modes.length;
|
|
5580
|
+
if (descriptor.modeOutputs.length !== expectedModeCount) {
|
|
5581
|
+
errors.push("mode output count does not match bundle modes");
|
|
5582
|
+
}
|
|
5583
|
+
const bundleModes = new Map(descriptor.bundle.modes.map((mode) => [mode.mode, mode]));
|
|
5584
|
+
for (const modeOutput of descriptor.modeOutputs) {
|
|
5585
|
+
const expectedMode = bundleModes.get(modeOutput.mode);
|
|
5586
|
+
if (expectedMode === undefined) {
|
|
5587
|
+
errors.push(`unexpected mode output: ${modeOutput.mode}`);
|
|
5588
|
+
continue;
|
|
5589
|
+
}
|
|
5590
|
+
if (canonicalJson(expectedMode) !== canonicalJson(modeOutput.report)) {
|
|
5591
|
+
errors.push(`mode output drift detected for ${modeOutput.mode}`);
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
const semanticHashVerification = verifyRecordedSessionReplayBundleHashes(descriptor.bundle);
|
|
5595
|
+
if (!semanticHashVerification.bundleHashMatches) {
|
|
5596
|
+
errors.push("bundleHash verification failed");
|
|
5597
|
+
}
|
|
5598
|
+
if (!semanticHashVerification.scoreHashMatches) {
|
|
5599
|
+
errors.push("scoreHash verification failed");
|
|
5600
|
+
}
|
|
5601
|
+
const expectedHashes = buildRecordedSessionReplayProofHashes(resolvedRoot, descriptor.manifest);
|
|
5602
|
+
const expectedFileDigests = new Map(expectedHashes.files.map((entry) => [entry.path, entry.digest]));
|
|
5603
|
+
let verifiedFileCount = 0;
|
|
5604
|
+
for (const fileEntry of descriptor.hashes.files ?? []) {
|
|
5605
|
+
if (expectedFileDigests.get(fileEntry.path) === fileEntry.digest) {
|
|
5606
|
+
verifiedFileCount += 1;
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5609
|
+
const fileHashesMatch = canonicalJson(expectedHashes.files) === canonicalJson(descriptor.hashes.files ?? []);
|
|
5610
|
+
if (!fileHashesMatch) {
|
|
5611
|
+
errors.push("hashes.json file digests do not match the written artifacts");
|
|
5612
|
+
}
|
|
5613
|
+
if (canonicalJson(expectedHashes.semantic) !== canonicalJson(descriptor.hashes.semantic ?? {})) {
|
|
5614
|
+
errors.push("hashes.json semantic hashes do not match the manifest");
|
|
5615
|
+
}
|
|
5616
|
+
return {
|
|
5617
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_VALIDATION_CONTRACT,
|
|
5618
|
+
ok: errors.length === 0,
|
|
5619
|
+
rootDir: resolvedRoot,
|
|
5620
|
+
expectedFileCount: expectedHashes.files.length,
|
|
5621
|
+
verifiedFileCount,
|
|
5622
|
+
fileHashesMatch,
|
|
5623
|
+
bundleHashMatches: semanticHashVerification.bundleHashMatches,
|
|
5624
|
+
scoreHashMatches: semanticHashVerification.scoreHashMatches,
|
|
5625
|
+
errors
|
|
5626
|
+
};
|
|
5627
|
+
}
|
|
5628
|
+
catch (error) {
|
|
5629
|
+
return {
|
|
5630
|
+
contract: RECORDED_SESSION_REPLAY_PROOF_VALIDATION_CONTRACT,
|
|
5631
|
+
ok: false,
|
|
5632
|
+
rootDir: resolvedRoot,
|
|
5633
|
+
expectedFileCount: 0,
|
|
5634
|
+
verifiedFileCount: 0,
|
|
5635
|
+
fileHashesMatch: false,
|
|
5636
|
+
bundleHashMatches: false,
|
|
5637
|
+
scoreHashMatches: false,
|
|
5638
|
+
errors: [toErrorMessage(error)]
|
|
5639
|
+
};
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
export function writeRecordedSessionReplayProofBundle(input) {
|
|
5643
|
+
const resolvedRoot = path.resolve(normalizeNonEmptyString(input.rootDir, "rootDir"));
|
|
5644
|
+
const scratchRootParent = path.resolve(normalizeOptionalString(input.scratchRootDir) ?? tmpdir());
|
|
5645
|
+
mkdirSync(resolvedRoot, { recursive: true });
|
|
5646
|
+
mkdirSync(scratchRootParent, { recursive: true });
|
|
5647
|
+
rmSync(path.join(resolvedRoot, RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.modeDir), {
|
|
5648
|
+
recursive: true,
|
|
5649
|
+
force: true
|
|
5650
|
+
});
|
|
5651
|
+
for (const relativePath of [
|
|
5652
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.trace,
|
|
5653
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.fixture,
|
|
5654
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.bundle,
|
|
5655
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.environment,
|
|
5656
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.summary,
|
|
5657
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.summaryTables,
|
|
5658
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.coverageSnapshot,
|
|
5659
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.hardeningSnapshot,
|
|
5660
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.manifest,
|
|
5661
|
+
RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.hashes
|
|
5662
|
+
]) {
|
|
5663
|
+
rmSync(path.join(resolvedRoot, relativePath), { force: true });
|
|
5664
|
+
}
|
|
5665
|
+
const trace = structuredClone(input.trace);
|
|
5666
|
+
const fixture = buildRecordedSessionReplayFixture(trace);
|
|
5667
|
+
const scratchRoot = mkdtempSync(path.join(scratchRootParent, "openclawbrain-recorded-session-replay-"));
|
|
5668
|
+
let bundle;
|
|
5669
|
+
try {
|
|
5670
|
+
bundle = runRecordedSessionReplay(scratchRoot, fixture);
|
|
5671
|
+
}
|
|
5672
|
+
finally {
|
|
5673
|
+
rmSync(scratchRoot, { recursive: true, force: true });
|
|
5674
|
+
}
|
|
5675
|
+
const environment = buildRecordedSessionReplayProofEnvironment();
|
|
5676
|
+
const summaryTables = buildRecordedSessionReplayProofSummaryTables(bundle);
|
|
5677
|
+
const coverageSnapshot = buildRecordedSessionReplayProofCoverageSnapshot(bundle);
|
|
5678
|
+
const hardeningSnapshot = buildRecordedSessionReplayProofHardeningSnapshot(bundle);
|
|
5679
|
+
const manifest = buildRecordedSessionReplayProofManifest(bundle);
|
|
5680
|
+
const summaryText = buildRecordedSessionReplayProofSummary({
|
|
5681
|
+
bundle,
|
|
5682
|
+
summaryTables,
|
|
5683
|
+
coverageSnapshot,
|
|
5684
|
+
hardeningSnapshot,
|
|
5685
|
+
semanticHashes: manifest.hashes
|
|
5686
|
+
});
|
|
5687
|
+
const modeRoot = path.join(resolvedRoot, RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.modeDir);
|
|
5688
|
+
mkdirSync(modeRoot, { recursive: true });
|
|
5689
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.trace), canonicalJson(trace), "utf8");
|
|
5690
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.fixture), canonicalJson(fixture), "utf8");
|
|
5691
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.bundle), canonicalJson(bundle), "utf8");
|
|
5692
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.environment), canonicalJson(environment), "utf8");
|
|
5693
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.summaryTables), canonicalJson(summaryTables), "utf8");
|
|
5694
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.coverageSnapshot), canonicalJson(coverageSnapshot), "utf8");
|
|
5695
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.hardeningSnapshot), canonicalJson(hardeningSnapshot), "utf8");
|
|
5696
|
+
for (const mode of orderedRecordedSessionReplayModes(bundle.modes)) {
|
|
5697
|
+
writeFileSync(path.join(resolvedRoot, recordedSessionReplayModeOutputPath(mode.mode)), canonicalJson(mode), "utf8");
|
|
5698
|
+
}
|
|
5699
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.summary), summaryText, "utf8");
|
|
5700
|
+
writeFileSync(path.join(resolvedRoot, RECORDED_SESSION_REPLAY_PROOF_BUNDLE_LAYOUT.manifest), canonicalJson(manifest), "utf8");
|
|
5701
|
+
const hashes = buildRecordedSessionReplayProofHashes(resolvedRoot, manifest);
|
|
5702
|
+
writeFileSync(path.join(resolvedRoot, manifest.files.hashes), canonicalJson(hashes), "utf8");
|
|
5703
|
+
const validation = validateRecordedSessionReplayProofBundle(resolvedRoot);
|
|
5704
|
+
if (!validation.ok) {
|
|
5705
|
+
throw new Error(`recorded session replay proof bundle is invalid: ${validation.errors.join("; ")}`);
|
|
5706
|
+
}
|
|
5707
|
+
return loadRecordedSessionReplayProofBundle(resolvedRoot);
|
|
5708
|
+
}
|
|
4778
5709
|
export const OPERATOR_API_CONTRACT_ID = "openclaw_operator_api.v1";
|
|
4779
5710
|
export const SUPPORTED_OPERATOR_API_FAMILIES = [
|
|
4780
5711
|
"bootstrap_attach",
|
|
@@ -5737,7 +6668,128 @@ function summarizeTeacherLoopWatchState(input) {
|
|
|
5737
6668
|
snapshotUpdatedAt: input.watchSnapshot.updatedAt,
|
|
5738
6669
|
lastWatchHeartbeatAt,
|
|
5739
6670
|
pollIntervalSeconds,
|
|
5740
|
-
|
|
6671
|
+
watchState: Number.isFinite(lagMs) && lagMs >= 0 && lagMs <= staleAfterMs ? "watching" : "stale_snapshot"
|
|
6672
|
+
};
|
|
6673
|
+
}
|
|
6674
|
+
function emptyOperatorLearningAttribution(source, snapshotKind, detail) {
|
|
6675
|
+
return {
|
|
6676
|
+
available: false,
|
|
6677
|
+
source,
|
|
6678
|
+
snapshotKind,
|
|
6679
|
+
quality: "unavailable",
|
|
6680
|
+
nonZeroObservationCount: 0,
|
|
6681
|
+
skippedZeroRewardCount: 0,
|
|
6682
|
+
exactMatchCount: 0,
|
|
6683
|
+
heuristicMatchCount: 0,
|
|
6684
|
+
unmatchedCount: 0,
|
|
6685
|
+
ambiguousCount: 0,
|
|
6686
|
+
matchedByMode: {
|
|
6687
|
+
exactDecisionId: 0,
|
|
6688
|
+
exactSelectionDigest: 0,
|
|
6689
|
+
turnCompileEventId: 0,
|
|
6690
|
+
legacyHeuristic: 0
|
|
6691
|
+
},
|
|
6692
|
+
unmatchedByMode: {
|
|
6693
|
+
exactDecisionId: 0,
|
|
6694
|
+
exactSelectionDigest: 0,
|
|
6695
|
+
turnCompileEventId: 0,
|
|
6696
|
+
legacyHeuristic: 0
|
|
6697
|
+
},
|
|
6698
|
+
ambiguousByMode: {
|
|
6699
|
+
exactDecisionId: 0,
|
|
6700
|
+
exactSelectionDigest: 0,
|
|
6701
|
+
turnCompileEventId: 0,
|
|
6702
|
+
legacyHeuristic: 0
|
|
6703
|
+
},
|
|
6704
|
+
detail
|
|
6705
|
+
};
|
|
6706
|
+
}
|
|
6707
|
+
function sumObservationBindingModes(counts) {
|
|
6708
|
+
return (counts.exactDecisionId ?? 0)
|
|
6709
|
+
+ (counts.exactSelectionDigest ?? 0)
|
|
6710
|
+
+ (counts.turnCompileEventId ?? 0)
|
|
6711
|
+
+ (counts.legacyHeuristic ?? 0);
|
|
6712
|
+
}
|
|
6713
|
+
function deriveObservationBindingQuality(summary) {
|
|
6714
|
+
if (summary.nonZeroObservationCount === 0) {
|
|
6715
|
+
return "no_nonzero_observations";
|
|
6716
|
+
}
|
|
6717
|
+
if (summary.ambiguousCount > 0) {
|
|
6718
|
+
if (summary.exactMatchCount > 0) {
|
|
6719
|
+
return "exact_with_ambiguous";
|
|
6720
|
+
}
|
|
6721
|
+
if (summary.heuristicMatchCount > 0) {
|
|
6722
|
+
return "heuristic_with_ambiguous";
|
|
6723
|
+
}
|
|
6724
|
+
return "ambiguous_present";
|
|
6725
|
+
}
|
|
6726
|
+
if (summary.unmatchedCount > 0) {
|
|
6727
|
+
if (summary.exactMatchCount > 0) {
|
|
6728
|
+
return "exact_with_unmatched";
|
|
6729
|
+
}
|
|
6730
|
+
if (summary.heuristicMatchCount > 0) {
|
|
6731
|
+
return "heuristic_with_unmatched";
|
|
6732
|
+
}
|
|
6733
|
+
return "unmatched_present";
|
|
6734
|
+
}
|
|
6735
|
+
if (summary.exactMatchCount > 0 && summary.heuristicMatchCount > 0) {
|
|
6736
|
+
return "exact_plus_heuristic";
|
|
6737
|
+
}
|
|
6738
|
+
if (summary.exactMatchCount > 0) {
|
|
6739
|
+
return "exact_only";
|
|
6740
|
+
}
|
|
6741
|
+
if (summary.heuristicMatchCount > 0) {
|
|
6742
|
+
return "heuristic_only";
|
|
6743
|
+
}
|
|
6744
|
+
return "no_matches";
|
|
6745
|
+
}
|
|
6746
|
+
function summarizeTeacherLoopObservationBinding(snapshot, sourceKind) {
|
|
6747
|
+
const stats = snapshot.learner.lastMaterialization?.candidate.routingBuild?.observationBindingStats ?? null;
|
|
6748
|
+
if (stats === null) {
|
|
6749
|
+
return emptyOperatorLearningAttribution("latest_materialization", sourceKind, snapshot.learner.lastMaterialization === null
|
|
6750
|
+
? "no materialized candidate pack is visible in the teacher snapshot"
|
|
6751
|
+
: "latest materialization did not expose observation binding stats");
|
|
6752
|
+
}
|
|
6753
|
+
const exactMatchCount = (stats.matched.exactDecisionId ?? 0)
|
|
6754
|
+
+ (stats.matched.exactSelectionDigest ?? 0)
|
|
6755
|
+
+ (stats.matched.turnCompileEventId ?? 0);
|
|
6756
|
+
const heuristicMatchCount = stats.matched.legacyHeuristic ?? 0;
|
|
6757
|
+
const unmatchedCount = sumObservationBindingModes(stats.unmatched);
|
|
6758
|
+
const ambiguousCount = sumObservationBindingModes(stats.ambiguous);
|
|
6759
|
+
const summary = {
|
|
6760
|
+
available: true,
|
|
6761
|
+
source: "latest_materialization",
|
|
6762
|
+
snapshotKind: sourceKind,
|
|
6763
|
+
quality: "unavailable",
|
|
6764
|
+
nonZeroObservationCount: stats.nonZeroObservationCount ?? 0,
|
|
6765
|
+
skippedZeroRewardCount: stats.skippedZeroRewardCount ?? 0,
|
|
6766
|
+
exactMatchCount,
|
|
6767
|
+
heuristicMatchCount,
|
|
6768
|
+
unmatchedCount,
|
|
6769
|
+
ambiguousCount,
|
|
6770
|
+
matchedByMode: {
|
|
6771
|
+
exactDecisionId: stats.matched.exactDecisionId ?? 0,
|
|
6772
|
+
exactSelectionDigest: stats.matched.exactSelectionDigest ?? 0,
|
|
6773
|
+
turnCompileEventId: stats.matched.turnCompileEventId ?? 0,
|
|
6774
|
+
legacyHeuristic: stats.matched.legacyHeuristic ?? 0
|
|
6775
|
+
},
|
|
6776
|
+
unmatchedByMode: {
|
|
6777
|
+
exactDecisionId: stats.unmatched.exactDecisionId ?? 0,
|
|
6778
|
+
exactSelectionDigest: stats.unmatched.exactSelectionDigest ?? 0,
|
|
6779
|
+
turnCompileEventId: stats.unmatched.turnCompileEventId ?? 0,
|
|
6780
|
+
legacyHeuristic: stats.unmatched.legacyHeuristic ?? 0
|
|
6781
|
+
},
|
|
6782
|
+
ambiguousByMode: {
|
|
6783
|
+
exactDecisionId: stats.ambiguous.exactDecisionId ?? 0,
|
|
6784
|
+
exactSelectionDigest: stats.ambiguous.exactSelectionDigest ?? 0,
|
|
6785
|
+
turnCompileEventId: stats.ambiguous.turnCompileEventId ?? 0,
|
|
6786
|
+
legacyHeuristic: stats.ambiguous.legacyHeuristic ?? 0
|
|
6787
|
+
},
|
|
6788
|
+
detail: "teacher observation binding stats came from the latest materialized candidate"
|
|
6789
|
+
};
|
|
6790
|
+
return {
|
|
6791
|
+
...summary,
|
|
6792
|
+
quality: deriveObservationBindingQuality(summary)
|
|
5741
6793
|
};
|
|
5742
6794
|
}
|
|
5743
6795
|
function summarizeTeacherLoop(input) {
|
|
@@ -5779,6 +6831,7 @@ function summarizeTeacherLoop(input) {
|
|
|
5779
6831
|
failureDetail: null,
|
|
5780
6832
|
lastAppliedMaterializationJobId: null,
|
|
5781
6833
|
lastMaterializedPackId: null,
|
|
6834
|
+
observationBinding: emptyOperatorLearningAttribution("unavailable", "missing", "no teacher snapshot path supplied"),
|
|
5782
6835
|
lastObservedDelta: unavailableFromMissing,
|
|
5783
6836
|
notes: [],
|
|
5784
6837
|
detail: "no teacher snapshot path supplied"
|
|
@@ -5818,6 +6871,7 @@ function summarizeTeacherLoop(input) {
|
|
|
5818
6871
|
failureDetail: null,
|
|
5819
6872
|
lastAppliedMaterializationJobId: null,
|
|
5820
6873
|
lastMaterializedPackId: null,
|
|
6874
|
+
observationBinding: emptyOperatorLearningAttribution("unavailable", "missing", "teacher snapshot could not be loaded"),
|
|
5821
6875
|
lastObservedDelta: unavailableFromMissing,
|
|
5822
6876
|
notes: [],
|
|
5823
6877
|
detail: "teacher snapshot could not be loaded"
|
|
@@ -5866,6 +6920,7 @@ function summarizeTeacherLoop(input) {
|
|
|
5866
6920
|
snapshot.learner.lastMaterialization?.jobId ??
|
|
5867
6921
|
null,
|
|
5868
6922
|
lastMaterializedPackId: snapshot.learner.lastMaterialization?.candidate.summary.packId ?? null,
|
|
6923
|
+
observationBinding: summarizeTeacherLoopObservationBinding(snapshot, loaded.sourceKind),
|
|
5869
6924
|
lastObservedDelta: loaded.sourceKind === "watch_snapshot" && watchSnapshot !== null
|
|
5870
6925
|
? cloneLastObservedDelta(watchSnapshot.lastObservedDelta)
|
|
5871
6926
|
: unavailableFromAsync,
|
|
@@ -5994,14 +7049,107 @@ function summarizeLearningWarningStates(input) {
|
|
|
5994
7049
|
input.teacherSnapshot.queue.depth >= input.teacherSnapshot.queue.capacity) {
|
|
5995
7050
|
warnings.add("teacher_queue_full");
|
|
5996
7051
|
}
|
|
5997
|
-
if (input.teacherSnapshot.diagnostics.latestFreshness === "stale") {
|
|
7052
|
+
if (input.teacherSnapshot.diagnostics.latestFreshness === "stale" && input.teacherSnapshot.diagnostics.lastNoOpReason !== "no_teacher_artifacts") {
|
|
5998
7053
|
warnings.add("teacher_labels_stale");
|
|
5999
7054
|
}
|
|
6000
|
-
if (input.teacherSnapshot.diagnostics.lastNoOpReason === "no_teacher_artifacts"
|
|
7055
|
+
if (input.teacherSnapshot.diagnostics.lastNoOpReason === "no_teacher_artifacts" &&
|
|
7056
|
+
summarizeTeacherNoArtifactCycle(input.teacherSnapshot.diagnostics.notes).shouldWarn) {
|
|
6001
7057
|
warnings.add("teacher_no_artifacts");
|
|
6002
7058
|
}
|
|
6003
7059
|
return [...warnings];
|
|
6004
7060
|
}
|
|
7061
|
+
export function summarizeTeacherNoArtifactCycle(notes) {
|
|
7062
|
+
const values = new Map();
|
|
7063
|
+
for (const note of notes ?? []) {
|
|
7064
|
+
const separator = note.indexOf("=");
|
|
7065
|
+
if (separator <= 0) {
|
|
7066
|
+
continue;
|
|
7067
|
+
}
|
|
7068
|
+
values.set(note.slice(0, separator), note.slice(separator + 1));
|
|
7069
|
+
}
|
|
7070
|
+
const readNullableNumber = (key) => {
|
|
7071
|
+
const raw = values.get(key);
|
|
7072
|
+
if (raw === undefined || raw === "unknown") {
|
|
7073
|
+
return null;
|
|
7074
|
+
}
|
|
7075
|
+
const parsed = Number.parseInt(raw, 10);
|
|
7076
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
7077
|
+
};
|
|
7078
|
+
const readNullableString = (key) => {
|
|
7079
|
+
const raw = values.get(key);
|
|
7080
|
+
return raw === undefined || raw === "unknown" ? null : raw;
|
|
7081
|
+
};
|
|
7082
|
+
const deterministicArtifactCount = readNullableNumber("teacher_last_cycle_deterministic_artifacts");
|
|
7083
|
+
const newDeterministicArtifactCount = readNullableNumber("teacher_last_cycle_new_deterministic_artifacts");
|
|
7084
|
+
const labelerCandidateCount = readNullableNumber("teacher_last_cycle_labeler_candidates");
|
|
7085
|
+
const labelerBudgetedCandidateCount = readNullableNumber("teacher_last_cycle_labeler_budgeted_candidates");
|
|
7086
|
+
const labelerStatus = readNullableString("teacher_last_cycle_labeler_status");
|
|
7087
|
+
const labelerDetail = readNullableString("teacher_last_cycle_labeler_detail");
|
|
7088
|
+
if (deterministicArtifactCount === null && labelerCandidateCount === null) {
|
|
7089
|
+
return {
|
|
7090
|
+
shouldWarn: true,
|
|
7091
|
+
detail: "the latest cycle produced no new teacher artifacts, and the snapshot did not say whether any teachable material was present"
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
if ((deterministicArtifactCount ?? 0) === 0 && (labelerCandidateCount ?? 0) === 0) {
|
|
7095
|
+
return {
|
|
7096
|
+
shouldWarn: false,
|
|
7097
|
+
detail: "the latest cycle produced no new teacher artifacts because the current export had no eligible feedback, operator overrides, or matched interaction text"
|
|
7098
|
+
};
|
|
7099
|
+
}
|
|
7100
|
+
if ((deterministicArtifactCount ?? 0) > 0 &&
|
|
7101
|
+
(newDeterministicArtifactCount ?? 0) === 0 &&
|
|
7102
|
+
(labelerCandidateCount ?? 0) === 0) {
|
|
7103
|
+
return {
|
|
7104
|
+
shouldWarn: false,
|
|
7105
|
+
detail: "the latest cycle produced no new teacher artifacts because the current export only repeated supervision that was already captured"
|
|
7106
|
+
};
|
|
7107
|
+
}
|
|
7108
|
+
if ((labelerCandidateCount ?? 0) > 0) {
|
|
7109
|
+
if ((labelerBudgetedCandidateCount ?? labelerCandidateCount) === 0) {
|
|
7110
|
+
return {
|
|
7111
|
+
shouldWarn: true,
|
|
7112
|
+
detail: "the latest cycle produced no new teacher artifacts because candidate interactions exceeded the teacher labeler prompt budget"
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
7115
|
+
if (labelerStatus === "disabled") {
|
|
7116
|
+
return {
|
|
7117
|
+
shouldWarn: false,
|
|
7118
|
+
detail: "the latest cycle produced no new teacher artifacts because candidate interactions existed, but the background teacher labeler is disabled"
|
|
7119
|
+
};
|
|
7120
|
+
}
|
|
7121
|
+
if (labelerStatus === "ok") {
|
|
7122
|
+
return {
|
|
7123
|
+
shouldWarn: false,
|
|
7124
|
+
detail: "the latest cycle produced no new teacher artifacts because candidate interactions were evaluated but did not add a new reusable label"
|
|
7125
|
+
};
|
|
7126
|
+
}
|
|
7127
|
+
if (labelerStatus === "fail_open") {
|
|
7128
|
+
return {
|
|
7129
|
+
shouldWarn: true,
|
|
7130
|
+
detail: labelerDetail === null
|
|
7131
|
+
? "the latest cycle produced no new teacher artifacts because the teacher labeler failed open while candidate interactions were present"
|
|
7132
|
+
: `the latest cycle produced no new teacher artifacts because the teacher labeler failed open while candidate interactions were present: ${labelerDetail}`
|
|
7133
|
+
};
|
|
7134
|
+
}
|
|
7135
|
+
if (labelerDetail === "no_labels_emitted") {
|
|
7136
|
+
return {
|
|
7137
|
+
shouldWarn: true,
|
|
7138
|
+
detail: "the latest cycle produced no new teacher artifacts even though candidate interactions were present; the teacher labeler emitted no reusable labels"
|
|
7139
|
+
};
|
|
7140
|
+
}
|
|
7141
|
+
return {
|
|
7142
|
+
shouldWarn: true,
|
|
7143
|
+
detail: labelerDetail === null
|
|
7144
|
+
? "the latest cycle produced no new teacher artifacts even though candidate interactions were present"
|
|
7145
|
+
: `the latest cycle produced no new teacher artifacts even though candidate interactions were present: ${labelerDetail}`
|
|
7146
|
+
};
|
|
7147
|
+
}
|
|
7148
|
+
return {
|
|
7149
|
+
shouldWarn: true,
|
|
7150
|
+
detail: "the latest cycle produced no new teacher artifacts even though teachable material was present"
|
|
7151
|
+
};
|
|
7152
|
+
}
|
|
6005
7153
|
function summarizeAlwaysOnLearning(input, active) {
|
|
6006
7154
|
const unavailableLag = {
|
|
6007
7155
|
activeEventRangeEnd: active?.eventRange.end ?? null,
|
|
@@ -6619,6 +7767,10 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
|
|
|
6619
7767
|
installState: report.hook.installState,
|
|
6620
7768
|
loadability: report.hook.loadability,
|
|
6621
7769
|
loadProof: report.hook.loadProof,
|
|
7770
|
+
guardSeverity: report.hook.guardSeverity,
|
|
7771
|
+
guardActionability: report.hook.guardActionability,
|
|
7772
|
+
guardSummary: report.hook.guardSummary,
|
|
7773
|
+
guardAction: report.hook.guardAction,
|
|
6622
7774
|
desynced: report.hook.desynced,
|
|
6623
7775
|
detail: report.hook.detail
|
|
6624
7776
|
},
|
|
@@ -6665,7 +7817,10 @@ function buildCurrentProfileBrainStatusFromReport(report, policyMode, profileId)
|
|
|
6665
7817
|
? "current profile would fail open to static context because no serving pack is available"
|
|
6666
7818
|
: report.servePath.state === "hard_fail"
|
|
6667
7819
|
? "current profile cannot serve because the learned-route or activation requirement hard-failed"
|
|
6668
|
-
|
|
7820
|
+
: "current profile serve state has not been compile-probed yet"
|
|
7821
|
+
},
|
|
7822
|
+
learningAttribution: {
|
|
7823
|
+
...report.teacherLoop.observationBinding
|
|
6669
7824
|
},
|
|
6670
7825
|
passiveLearning,
|
|
6671
7826
|
currentTurnAttribution: buildCurrentProfileTurnAttributionFromReport(report, policyMode, profileId)
|