@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
|
@@ -142,9 +142,10 @@ export function classifyOpenClawBrainConvergeVerification(input) {
|
|
|
142
142
|
if (displayedStatus === "fail") {
|
|
143
143
|
blockingReasons.push("status still reports fail");
|
|
144
144
|
}
|
|
145
|
-
const runtimeTruthAlreadyProven =
|
|
146
|
-
&&
|
|
147
|
-
&&
|
|
145
|
+
const runtimeTruthAlreadyProven = runtimeLoad === "proven"
|
|
146
|
+
&& loadProof === "status_probe_ready"
|
|
147
|
+
&& installState === "installed"
|
|
148
|
+
&& loadability === "loadable";
|
|
148
149
|
if (input.restartRequired === true && input.restartPerformed !== true && !runtimeTruthAlreadyProven) {
|
|
149
150
|
blockingReasons.push("restart is still required before runtime load can be trusted");
|
|
150
151
|
}
|
|
@@ -23,6 +23,48 @@ function softmax(values) {
|
|
|
23
23
|
}
|
|
24
24
|
return numerators.map((value) => roundNumber(value / denominator));
|
|
25
25
|
}
|
|
26
|
+
function isPlainObject(value) {
|
|
27
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const prototype = Object.getPrototypeOf(value);
|
|
31
|
+
return prototype === Object.prototype || prototype === null;
|
|
32
|
+
}
|
|
33
|
+
function compactStructuralSignalValue(value, depth = 0) {
|
|
34
|
+
if (value === null || typeof value === "boolean") {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "number") {
|
|
38
|
+
return Number.isFinite(value) ? roundNumber(value) : undefined;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "string") {
|
|
41
|
+
return value.length <= 256 ? value : `${value.slice(0, 256)}...`;
|
|
42
|
+
}
|
|
43
|
+
if (depth >= 3) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
const compacted = value
|
|
48
|
+
.slice(0, 32)
|
|
49
|
+
.map((entry) => compactStructuralSignalValue(entry, depth + 1))
|
|
50
|
+
.filter((entry) => entry !== undefined);
|
|
51
|
+
return compacted;
|
|
52
|
+
}
|
|
53
|
+
if (!isPlainObject(value)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const compactedEntries = Object.entries(value)
|
|
57
|
+
.slice(0, 32)
|
|
58
|
+
.flatMap(([key, entry]) => {
|
|
59
|
+
const compacted = compactStructuralSignalValue(entry, depth + 1);
|
|
60
|
+
return compacted === undefined ? [] : [[key, compacted]];
|
|
61
|
+
});
|
|
62
|
+
return compactedEntries.length > 0 ? Object.fromEntries(compactedEntries) : undefined;
|
|
63
|
+
}
|
|
64
|
+
function compactStructuralSignals(value) {
|
|
65
|
+
const compacted = compactStructuralSignalValue(value);
|
|
66
|
+
return compacted !== undefined && isPlainObject(compacted) ? compacted : null;
|
|
67
|
+
}
|
|
26
68
|
function isStableKernelContextBlock(block) {
|
|
27
69
|
if (block.id.includes(":event:") || block.id.includes(":teacher:")) {
|
|
28
70
|
return false;
|
|
@@ -264,7 +306,7 @@ export function appendServeTimeRouteDecisionLog(input) {
|
|
|
264
306
|
actionScore: roundNumber(entry.score),
|
|
265
307
|
actionProbability: probabilities[index] ?? 0
|
|
266
308
|
})),
|
|
267
|
-
structuralSignals: null,
|
|
309
|
+
structuralSignals: compactStructuralSignals(input.compileResult.ok ? input.compileResult.compileResponse.structuralSignals : null),
|
|
268
310
|
fallbackReason: serveFallbackReason(input.compileResult),
|
|
269
311
|
hotPathTiming: input.compileResult.timing,
|
|
270
312
|
kernelContextCount: selectedKernelContextIds.length,
|
|
@@ -85,6 +85,12 @@ export interface TeacherObservationBindingStatsV1 {
|
|
|
85
85
|
totalObservationCount: number;
|
|
86
86
|
nonZeroObservationCount: number;
|
|
87
87
|
skippedZeroRewardCount: number;
|
|
88
|
+
accounting: {
|
|
89
|
+
exact: number;
|
|
90
|
+
heuristic: number;
|
|
91
|
+
unmatched: number;
|
|
92
|
+
ambiguous: number;
|
|
93
|
+
};
|
|
88
94
|
matched: {
|
|
89
95
|
exactDecisionId: number;
|
|
90
96
|
exactSelectionDigest: number;
|
|
@@ -3,9 +3,10 @@ import path from "node:path";
|
|
|
3
3
|
import { DatabaseSync } from "node:sqlite";
|
|
4
4
|
import { buildRouteArtifactReference, CONTRACT_IDS, PACK_GRAPH_SCHEMAS, ROUTER_PG_PROFILE_V1, ROUTER_PG_PROFILE_V2, checksumJsonPayload, computeRouterCollectedLabelCounts, computeRouterFreshnessChecksum, computeRouterObjectiveChecksum, computeRouterQueryChecksum, computeRouterWeightsChecksum, sortNormalizedEvents, validateTeacherSupervisionArtifact } from "@openclawbrain/contracts";
|
|
5
5
|
import { buildNormalizedEventDedupId, buildNormalizedEventExport, buildNormalizedEventExportBridge, createDefaultLearningSurface, createEventExportCursor, createExplicitEventRange, validateNormalizedEventExport, validateNormalizedEventExportBridge, validateNormalizedEventExportSlice } from "@openclawbrain/event-export";
|
|
6
|
-
import { computePayloadChecksum, loadPack, PACK_LAYOUT, summarizeStructuralGraphEvolution, writePackFile } from "@openclawbrain/pack-format";
|
|
6
|
+
import { computePayloadChecksum, loadPack, loadPackFromActivation, PACK_LAYOUT, summarizeStructuralGraphEvolution, writePackFile } from "@openclawbrain/pack-format";
|
|
7
7
|
import { buildArtifactProvenance } from "@openclawbrain/provenance";
|
|
8
8
|
import { createWorkspaceMetadata } from "@openclawbrain/workspace-metadata";
|
|
9
|
+
import { createServeTimeDecisionMatcher } from "./teacher-decision-match.js";
|
|
9
10
|
export const DEFAULT_ALWAYS_ON_LEARNING_LIVE_SLICES_PER_CYCLE = 1;
|
|
10
11
|
export const DEFAULT_ALWAYS_ON_LEARNING_BACKFILL_SLICES_PER_CYCLE = 1;
|
|
11
12
|
export const DEFAULT_TEACHER_SUPERVISION_STALE_AFTER_MS = 5 * 60 * 1_000;
|
|
@@ -26,6 +27,7 @@ export const ALWAYS_ON_STRUCTURAL_PLASTICITY_OP_CEILING = {
|
|
|
26
27
|
export const ALWAYS_ON_STRUCTURAL_PLASTICITY_MIN_INTERACTIONS = 2;
|
|
27
28
|
export const ALWAYS_ON_STRUCTURAL_PLASTICITY_MIN_FEEDBACK = 1;
|
|
28
29
|
const CONNECT_PAIR_SCORE_THRESHOLD = 2;
|
|
30
|
+
const MAX_CARRY_FORWARD_SEED_BLOCKS = 12;
|
|
29
31
|
export const DEFAULT_SPARSE_FEEDBACK_POLICY = {
|
|
30
32
|
teacherBudget: 32,
|
|
31
33
|
teacherDelayMs: 0,
|
|
@@ -4254,6 +4256,12 @@ function emptyTeacherObservationBindingStats() {
|
|
|
4254
4256
|
totalObservationCount: 0,
|
|
4255
4257
|
nonZeroObservationCount: 0,
|
|
4256
4258
|
skippedZeroRewardCount: 0,
|
|
4259
|
+
accounting: {
|
|
4260
|
+
exact: 0,
|
|
4261
|
+
heuristic: 0,
|
|
4262
|
+
unmatched: 0,
|
|
4263
|
+
ambiguous: 0
|
|
4264
|
+
},
|
|
4257
4265
|
matched: {
|
|
4258
4266
|
exactDecisionId: 0,
|
|
4259
4267
|
exactSelectionDigest: 0,
|
|
@@ -4276,6 +4284,7 @@ function emptyTeacherObservationBindingStats() {
|
|
|
4276
4284
|
}
|
|
4277
4285
|
function summarizeTeacherObservationBindingStats(stats) {
|
|
4278
4286
|
return [
|
|
4287
|
+
`accounting(exact=${stats.accounting.exact},heuristic=${stats.accounting.heuristic},unmatched=${stats.accounting.unmatched},ambiguous=${stats.accounting.ambiguous})`,
|
|
4279
4288
|
`matched(exact_decision_id=${stats.matched.exactDecisionId},exact_selection_digest=${stats.matched.exactSelectionDigest},turn_compile_event_id=${stats.matched.turnCompileEventId},legacy_heuristic=${stats.matched.legacyHeuristic})`,
|
|
4280
4289
|
`unmatched(exact_decision_id=${stats.unmatched.exactDecisionId},exact_selection_digest=${stats.unmatched.exactSelectionDigest},turn_compile_event_id=${stats.unmatched.turnCompileEventId},legacy_heuristic=${stats.unmatched.legacyHeuristic})`,
|
|
4281
4290
|
`ambiguous(exact_decision_id=${stats.ambiguous.exactDecisionId},exact_selection_digest=${stats.ambiguous.exactSelectionDigest},turn_compile_event_id=${stats.ambiguous.turnCompileEventId},legacy_heuristic=${stats.ambiguous.legacyHeuristic})`,
|
|
@@ -4365,8 +4374,7 @@ function resolveTeacherObservationDecisionMatch(decisions, observation, indexes)
|
|
|
4365
4374
|
? { kind: "unmatched", mode: "exactSelectionDigest" }
|
|
4366
4375
|
: { kind: "matched", mode: "exactSelectionDigest", decision };
|
|
4367
4376
|
}
|
|
4368
|
-
if (normalizeObservationOptionalString(observation.selectionDigest) !== null
|
|
4369
|
-
|| normalizeObservationOptionalString(observation.activePackGraphChecksum) !== null) {
|
|
4377
|
+
if (normalizeObservationOptionalString(observation.selectionDigest) !== null) {
|
|
4370
4378
|
return { kind: "unmatched", mode: "exactSelectionDigest" };
|
|
4371
4379
|
}
|
|
4372
4380
|
const turnCompileEventId = normalizeObservationOptionalString(observation.turnCompileEventId);
|
|
@@ -4401,13 +4409,21 @@ function joinDecisionsWithTeacherObservationOutcomes(decisions, observationOutco
|
|
|
4401
4409
|
stats.nonZeroObservationCount += 1;
|
|
4402
4410
|
const match = resolveTeacherObservationDecisionMatch(decisions, observation, indexes);
|
|
4403
4411
|
if (match.kind === "unmatched") {
|
|
4412
|
+
stats.accounting.unmatched += 1;
|
|
4404
4413
|
stats.unmatched[match.mode] += 1;
|
|
4405
4414
|
continue;
|
|
4406
4415
|
}
|
|
4407
4416
|
if (match.kind === "ambiguous") {
|
|
4417
|
+
stats.accounting.ambiguous += 1;
|
|
4408
4418
|
stats.ambiguous[match.mode] += 1;
|
|
4409
4419
|
continue;
|
|
4410
4420
|
}
|
|
4421
|
+
if (match.mode === "legacyHeuristic") {
|
|
4422
|
+
stats.accounting.heuristic += 1;
|
|
4423
|
+
}
|
|
4424
|
+
else {
|
|
4425
|
+
stats.accounting.exact += 1;
|
|
4426
|
+
}
|
|
4411
4427
|
stats.matched[match.mode] += 1;
|
|
4412
4428
|
const current = outcomes.get(match.decision.recordId) ?? 0;
|
|
4413
4429
|
if (current === 0 || Math.abs(reward) > Math.abs(current)) {
|
|
@@ -4426,149 +4442,7 @@ export function joinDecisionsWithFeedback(decisions, eventExport, maxDelayMs = 3
|
|
|
4426
4442
|
return outcomes;
|
|
4427
4443
|
}
|
|
4428
4444
|
const normalizeOptionalString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
4429
|
-
const
|
|
4430
|
-
const normalized = normalizeOptionalString(value);
|
|
4431
|
-
if (normalized === undefined) {
|
|
4432
|
-
return null;
|
|
4433
|
-
}
|
|
4434
|
-
const parsed = Date.parse(normalized);
|
|
4435
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
4436
|
-
};
|
|
4437
|
-
const buildSessionChannelKey = (sessionId, channel) => {
|
|
4438
|
-
const normalizedSessionId = normalizeOptionalString(sessionId);
|
|
4439
|
-
const normalizedChannel = normalizeOptionalString(channel);
|
|
4440
|
-
if (normalizedSessionId === undefined || normalizedChannel === undefined) {
|
|
4441
|
-
return null;
|
|
4442
|
-
}
|
|
4443
|
-
return `${normalizedSessionId}|${normalizedChannel}`;
|
|
4444
|
-
};
|
|
4445
|
-
const buildCandidateKey = (sessionId, channel, createdAt) => {
|
|
4446
|
-
const sessionChannelKey = buildSessionChannelKey(sessionId, channel);
|
|
4447
|
-
const normalizedCreatedAt = normalizeOptionalString(createdAt);
|
|
4448
|
-
if (sessionChannelKey === null || normalizedCreatedAt === undefined) {
|
|
4449
|
-
return null;
|
|
4450
|
-
}
|
|
4451
|
-
return `${sessionChannelKey}|${normalizedCreatedAt}`;
|
|
4452
|
-
};
|
|
4453
|
-
const buildDecisionTimestamps = (decision) => {
|
|
4454
|
-
const timestamps = [];
|
|
4455
|
-
const turnCreatedAt = toTimestamp(decision.turnCreatedAt);
|
|
4456
|
-
const recordedAt = toTimestamp(decision.recordedAt);
|
|
4457
|
-
if (turnCreatedAt !== null) {
|
|
4458
|
-
timestamps.push(turnCreatedAt);
|
|
4459
|
-
}
|
|
4460
|
-
if (recordedAt !== null && !timestamps.includes(recordedAt)) {
|
|
4461
|
-
timestamps.push(recordedAt);
|
|
4462
|
-
}
|
|
4463
|
-
return timestamps;
|
|
4464
|
-
};
|
|
4465
|
-
const operationalPatterns = [
|
|
4466
|
-
/^NO_REPLY$/i,
|
|
4467
|
-
/^HEARTBEAT_OK$/i,
|
|
4468
|
-
/^read heartbeat\.md if it exists/i,
|
|
4469
|
-
/^a new session was started via \/new or \/reset\./i,
|
|
4470
|
-
/\[cron:[a-f0-9-]+\s/i,
|
|
4471
|
-
/\[system message\]\s*\[sessionid:/i,
|
|
4472
|
-
];
|
|
4473
|
-
const isOperationalDecision = (decision) => {
|
|
4474
|
-
const userMessage = normalizeOptionalString(decision.userMessage);
|
|
4475
|
-
if (userMessage === undefined) {
|
|
4476
|
-
return true;
|
|
4477
|
-
}
|
|
4478
|
-
return operationalPatterns.some((pattern) => pattern.test(userMessage));
|
|
4479
|
-
};
|
|
4480
|
-
const selectNearestDecision = (entries, interactionAt) => {
|
|
4481
|
-
const candidates = entries
|
|
4482
|
-
.map((entry) => {
|
|
4483
|
-
const deltas = entry.timestamps.map((timestamp) => Math.abs(timestamp - interactionAt));
|
|
4484
|
-
const bestDelta = deltas.length === 0 ? null : Math.min(...deltas);
|
|
4485
|
-
return bestDelta === null || bestDelta > maxDelayMs
|
|
4486
|
-
? null
|
|
4487
|
-
: {
|
|
4488
|
-
decision: entry.decision,
|
|
4489
|
-
deltaMs: bestDelta,
|
|
4490
|
-
recordedAt: toTimestamp(entry.decision.recordedAt) ?? 0,
|
|
4491
|
-
};
|
|
4492
|
-
})
|
|
4493
|
-
.filter((entry) => entry !== null)
|
|
4494
|
-
.sort((left, right) => {
|
|
4495
|
-
if (left.deltaMs !== right.deltaMs) {
|
|
4496
|
-
return left.deltaMs - right.deltaMs;
|
|
4497
|
-
}
|
|
4498
|
-
return right.recordedAt - left.recordedAt;
|
|
4499
|
-
});
|
|
4500
|
-
const best = candidates[0] ?? null;
|
|
4501
|
-
const runnerUp = candidates[1] ?? null;
|
|
4502
|
-
if (best === null) {
|
|
4503
|
-
return null;
|
|
4504
|
-
}
|
|
4505
|
-
if (runnerUp !== null && runnerUp.deltaMs === best.deltaMs && runnerUp.decision !== best.decision) {
|
|
4506
|
-
return null;
|
|
4507
|
-
}
|
|
4508
|
-
return best.decision;
|
|
4509
|
-
};
|
|
4510
|
-
const exactDecisions = new Map();
|
|
4511
|
-
const fallbackDecisions = new Map();
|
|
4512
|
-
const decisionsBySessionChannel = new Map();
|
|
4513
|
-
const globalFallbackDecisions = [];
|
|
4514
|
-
for (const decision of [...decisions].sort((left, right) => Date.parse(right.recordedAt) - Date.parse(left.recordedAt))) {
|
|
4515
|
-
const userMessage = normalizeOptionalString(decision.userMessage);
|
|
4516
|
-
if (userMessage === undefined) {
|
|
4517
|
-
continue;
|
|
4518
|
-
}
|
|
4519
|
-
const turnCompileEventId = normalizeOptionalString(decision.turnCompileEventId);
|
|
4520
|
-
if (turnCompileEventId !== undefined && !exactDecisions.has(turnCompileEventId)) {
|
|
4521
|
-
exactDecisions.set(turnCompileEventId, decision);
|
|
4522
|
-
}
|
|
4523
|
-
for (const candidateKey of [
|
|
4524
|
-
buildCandidateKey(decision.sessionId, decision.channel, decision.turnCreatedAt),
|
|
4525
|
-
buildCandidateKey(decision.sessionId, decision.channel, decision.recordedAt),
|
|
4526
|
-
]) {
|
|
4527
|
-
if (candidateKey !== null && !fallbackDecisions.has(candidateKey)) {
|
|
4528
|
-
fallbackDecisions.set(candidateKey, decision);
|
|
4529
|
-
}
|
|
4530
|
-
}
|
|
4531
|
-
const sessionChannelKey = buildSessionChannelKey(decision.sessionId, decision.channel);
|
|
4532
|
-
const indexedEntry = {
|
|
4533
|
-
decision,
|
|
4534
|
-
timestamps: buildDecisionTimestamps(decision),
|
|
4535
|
-
operational: isOperationalDecision(decision),
|
|
4536
|
-
};
|
|
4537
|
-
if (sessionChannelKey !== null) {
|
|
4538
|
-
const indexed = decisionsBySessionChannel.get(sessionChannelKey) ?? [];
|
|
4539
|
-
indexed.push(indexedEntry);
|
|
4540
|
-
decisionsBySessionChannel.set(sessionChannelKey, indexed);
|
|
4541
|
-
}
|
|
4542
|
-
if (!indexedEntry.operational) {
|
|
4543
|
-
globalFallbackDecisions.push(indexedEntry);
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
const matchInteractionToDecision = (interaction) => {
|
|
4547
|
-
const exact = exactDecisions.get(interaction.eventId);
|
|
4548
|
-
if (exact !== undefined) {
|
|
4549
|
-
return exact;
|
|
4550
|
-
}
|
|
4551
|
-
const exactFallbackKey = buildCandidateKey(interaction.sessionId, interaction.channel, interaction.createdAt);
|
|
4552
|
-
if (exactFallbackKey !== null) {
|
|
4553
|
-
const fallback = fallbackDecisions.get(exactFallbackKey);
|
|
4554
|
-
if (fallback !== undefined) {
|
|
4555
|
-
return fallback;
|
|
4556
|
-
}
|
|
4557
|
-
}
|
|
4558
|
-
const interactionAt = toTimestamp(interaction.createdAt);
|
|
4559
|
-
const sessionChannelKey = buildSessionChannelKey(interaction.sessionId, interaction.channel);
|
|
4560
|
-
if (interactionAt === null) {
|
|
4561
|
-
return null;
|
|
4562
|
-
}
|
|
4563
|
-
if (sessionChannelKey !== null) {
|
|
4564
|
-
const sessionEntries = (decisionsBySessionChannel.get(sessionChannelKey) ?? []).filter((entry) => entry.operational !== true);
|
|
4565
|
-
const sessionMatch = selectNearestDecision(sessionEntries, interactionAt);
|
|
4566
|
-
if (sessionMatch !== null) {
|
|
4567
|
-
return sessionMatch;
|
|
4568
|
-
}
|
|
4569
|
-
}
|
|
4570
|
-
return selectNearestDecision(globalFallbackDecisions, interactionAt);
|
|
4571
|
-
};
|
|
4445
|
+
const matchInteractionToDecision = createServeTimeDecisionMatcher(decisions, { maxTimeDeltaMs: maxDelayMs });
|
|
4572
4446
|
const interactionById = new Map();
|
|
4573
4447
|
for (const interaction of eventExport.interactionEvents ?? []) {
|
|
4574
4448
|
const interactionId = normalizeOptionalString(interaction.eventId);
|
|
@@ -4576,7 +4450,7 @@ export function joinDecisionsWithFeedback(decisions, eventExport, maxDelayMs = 3
|
|
|
4576
4450
|
interactionById.set(interactionId, interaction);
|
|
4577
4451
|
}
|
|
4578
4452
|
}
|
|
4579
|
-
const
|
|
4453
|
+
const consumedFeedbackIds = new Set();
|
|
4580
4454
|
for (const event of eventExport.feedbackEvents ?? []) {
|
|
4581
4455
|
const relatedInteractionId = normalizeOptionalString(event.relatedInteractionId);
|
|
4582
4456
|
if (relatedInteractionId === undefined) {
|
|
@@ -4586,6 +4460,8 @@ export function joinDecisionsWithFeedback(decisions, eventExport, maxDelayMs = 3
|
|
|
4586
4460
|
if (interaction === undefined) {
|
|
4587
4461
|
continue;
|
|
4588
4462
|
}
|
|
4463
|
+
const feedbackId = normalizeOptionalString(event.eventId) ?? `${relatedInteractionId}|${normalizeOptionalString(event.createdAt) ?? 'unknown'}`;
|
|
4464
|
+
consumedFeedbackIds.add(feedbackId);
|
|
4589
4465
|
const matchedDecision = matchInteractionToDecision(interaction);
|
|
4590
4466
|
if (matchedDecision === null) {
|
|
4591
4467
|
continue;
|
|
@@ -4597,13 +4473,11 @@ export function joinDecisionsWithFeedback(decisions, eventExport, maxDelayMs = 3
|
|
|
4597
4473
|
outcomes.set(matchedDecision.recordId, reward);
|
|
4598
4474
|
}
|
|
4599
4475
|
}
|
|
4600
|
-
const feedbackId = normalizeOptionalString(event.eventId) ?? `${relatedInteractionId}|${normalizeOptionalString(event.createdAt) ?? 'unknown'}`;
|
|
4601
|
-
matchedFeedbackIds.add(feedbackId);
|
|
4602
4476
|
}
|
|
4603
4477
|
const feedbackBySession = new Map();
|
|
4604
4478
|
for (const event of eventExport.feedbackEvents ?? []) {
|
|
4605
4479
|
const feedbackId = normalizeOptionalString(event.eventId) ?? `${normalizeOptionalString(event.relatedInteractionId) ?? '__none__'}|${normalizeOptionalString(event.createdAt) ?? 'unknown'}`;
|
|
4606
|
-
if (
|
|
4480
|
+
if (consumedFeedbackIds.has(feedbackId)) {
|
|
4607
4481
|
continue;
|
|
4608
4482
|
}
|
|
4609
4483
|
const sessionId = event.sessionId ?? "__none__";
|
|
@@ -4972,6 +4846,57 @@ function defaultLearningSurface(workspace, offlineArtifacts, workspaceInit) {
|
|
|
4972
4846
|
...offlineArtifacts.map((artifact) => `offline:${artifact}`)
|
|
4973
4847
|
]));
|
|
4974
4848
|
}
|
|
4849
|
+
function isCarryForwardSeedBlock(block) {
|
|
4850
|
+
const role = block.learning?.role ?? "";
|
|
4851
|
+
if (role !== "interaction" && role !== "feedback" && role !== "teacher_supervision" && role !== "label_surface") {
|
|
4852
|
+
return false;
|
|
4853
|
+
}
|
|
4854
|
+
if (typeof block.text !== "string" || block.text.trim().length === 0) {
|
|
4855
|
+
return false;
|
|
4856
|
+
}
|
|
4857
|
+
// Only true seed-session evidence should survive prefix-changing promotions.
|
|
4858
|
+
// Ordinary runtime-turn feedback is re-materialized from learnedEventExport;
|
|
4859
|
+
// carrying it forward under the old runtime-graph id duplicates evidence.
|
|
4860
|
+
return block.semantic?.sourceKind === "recorded_session_seed" ||
|
|
4861
|
+
block.source.includes("/seed:");
|
|
4862
|
+
}
|
|
4863
|
+
function carryForwardSeedBlockScore(block) {
|
|
4864
|
+
return (block.initSeed?.score ?? 0) +
|
|
4865
|
+
(block.state?.strength ?? 0) +
|
|
4866
|
+
(block.learning?.humanLabels ?? 0) * 2 +
|
|
4867
|
+
(block.learning?.selfLabels ?? 0);
|
|
4868
|
+
}
|
|
4869
|
+
function loadCarryForwardSeedBlocksFromActivation(activationRoot, currentGraphBlockIds) {
|
|
4870
|
+
try {
|
|
4871
|
+
const activePack = loadPackFromActivation(activationRoot, "active", { requireActivationReady: true });
|
|
4872
|
+
if (activePack === null) {
|
|
4873
|
+
return [];
|
|
4874
|
+
}
|
|
4875
|
+
const candidates = activePack.graph.blocks
|
|
4876
|
+
.filter((block) => isCarryForwardSeedBlock(block) && !currentGraphBlockIds.has(block.id));
|
|
4877
|
+
const preferred = candidates.filter((block) => block.learning?.role !== "interaction");
|
|
4878
|
+
const selected = (preferred.length > 0 ? preferred : candidates)
|
|
4879
|
+
.sort((left, right) => carryForwardSeedBlockScore(right) - carryForwardSeedBlockScore(left) ||
|
|
4880
|
+
right.priority - left.priority ||
|
|
4881
|
+
left.id.localeCompare(right.id))
|
|
4882
|
+
.slice(0, MAX_CARRY_FORWARD_SEED_BLOCKS);
|
|
4883
|
+
if (selected.length === 0) {
|
|
4884
|
+
return [];
|
|
4885
|
+
}
|
|
4886
|
+
const selectedIds = new Set(selected.map((block) => block.id));
|
|
4887
|
+
return selected.map((block) => ({
|
|
4888
|
+
...structuredClone(block),
|
|
4889
|
+
...(block.edges === undefined
|
|
4890
|
+
? {}
|
|
4891
|
+
: {
|
|
4892
|
+
edges: block.edges.filter((edge) => selectedIds.has(edge.targetBlockId))
|
|
4893
|
+
})
|
|
4894
|
+
}));
|
|
4895
|
+
}
|
|
4896
|
+
catch {
|
|
4897
|
+
return [];
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4975
4900
|
function cloneRuntimeGraphForPack(packId, runtimeGraph, builtAt) {
|
|
4976
4901
|
const cloned = structuredClone(runtimeGraph);
|
|
4977
4902
|
cloned.packId = packId;
|
|
@@ -5045,9 +4970,18 @@ function buildRuntimeGraphSnapshot(input) {
|
|
|
5045
4970
|
rootDir: workspace.rootDir,
|
|
5046
4971
|
observedAt: builtAt
|
|
5047
4972
|
}));
|
|
4973
|
+
const carryForwardSeedBlocks = input.activationRoot === undefined
|
|
4974
|
+
? []
|
|
4975
|
+
: loadCarryForwardSeedBlocksFromActivation(input.activationRoot, new Set(graph.blocks.map((block) => block.id)));
|
|
4976
|
+
const graphWithCarryForwardSeed = carryForwardSeedBlocks.length === 0
|
|
4977
|
+
? graph
|
|
4978
|
+
: {
|
|
4979
|
+
...graph,
|
|
4980
|
+
blocks: [...graph.blocks, ...carryForwardSeedBlocks]
|
|
4981
|
+
};
|
|
5048
4982
|
return {
|
|
5049
|
-
graph,
|
|
5050
|
-
plasticity: summarizeRuntimeGraphPlasticity("live_loop",
|
|
4983
|
+
graph: graphWithCarryForwardSeed,
|
|
4984
|
+
plasticity: summarizeRuntimeGraphPlasticity("live_loop", graphWithCarryForwardSeed, builtAt, null, normalizedEventExport)
|
|
5051
4985
|
};
|
|
5052
4986
|
}
|
|
5053
4987
|
export function buildCandidatePack(input) {
|
|
@@ -44,6 +44,32 @@ function sanitizeToken(value) {
|
|
|
44
44
|
function slugifyIdentity(value) {
|
|
45
45
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
46
46
|
}
|
|
47
|
+
function sortSessionRecordsDeterministically(records) {
|
|
48
|
+
return [...records].sort((left, right) => {
|
|
49
|
+
const leftTimestamp = Date.parse(left.timestamp);
|
|
50
|
+
const rightTimestamp = Date.parse(right.timestamp);
|
|
51
|
+
if (leftTimestamp !== rightTimestamp) {
|
|
52
|
+
return leftTimestamp - rightTimestamp;
|
|
53
|
+
}
|
|
54
|
+
const leftId = typeof left.id === "string" ? left.id : "";
|
|
55
|
+
const rightId = typeof right.id === "string" ? right.id : "";
|
|
56
|
+
const leftParentId = typeof left.parentId === "string" ? left.parentId : "";
|
|
57
|
+
const rightParentId = typeof right.parentId === "string" ? right.parentId : "";
|
|
58
|
+
if (rightParentId === leftId && leftParentId !== rightId) {
|
|
59
|
+
return -1;
|
|
60
|
+
}
|
|
61
|
+
if (leftParentId === rightId && rightParentId !== leftId) {
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
if (leftId !== rightId) {
|
|
65
|
+
return leftId.localeCompare(rightId);
|
|
66
|
+
}
|
|
67
|
+
if (leftParentId !== rightParentId) {
|
|
68
|
+
return leftParentId.localeCompare(rightParentId);
|
|
69
|
+
}
|
|
70
|
+
return left.type.localeCompare(right.type);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
47
73
|
function deriveChannel(sessionKey, entry) {
|
|
48
74
|
const deliveryChannel = normalizeString(entry.deliveryContext?.channel);
|
|
49
75
|
if (deliveryChannel !== null) {
|
|
@@ -256,7 +282,7 @@ export function buildPassiveLearningSessionExportFromOpenClawSessionStore(input)
|
|
|
256
282
|
let droppedRuntimeNoiseCount = 0;
|
|
257
283
|
let nextSequence = sequenceStart;
|
|
258
284
|
let latestAssistantInteractionId = null;
|
|
259
|
-
for (const record of input.records) {
|
|
285
|
+
for (const record of sortSessionRecordsDeterministically(input.records)) {
|
|
260
286
|
if (record.type !== "message") {
|
|
261
287
|
continue;
|
|
262
288
|
}
|
|
@@ -451,4 +477,4 @@ export function buildPassiveLearningStoreExportFromOpenClawSessionIndex(input) {
|
|
|
451
477
|
warnings: sessions.flatMap((session) => session.warnings)
|
|
452
478
|
};
|
|
453
479
|
}
|
|
454
|
-
//# sourceMappingURL=local-session-passive-learning.js.map
|
|
480
|
+
//# sourceMappingURL=local-session-passive-learning.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { reindexCandidatePackBuildResultWithEmbedder } from "./local-learner.js";
|
|
2
|
+
|
|
3
|
+
export async function reindexMaterializationCandidateWithEmbedder(materialization, embedder) {
|
|
4
|
+
if (materialization === null || embedder === null) {
|
|
5
|
+
return materialization;
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
...materialization,
|
|
9
|
+
candidate: await reindexCandidatePackBuildResultWithEmbedder(materialization.candidate, embedder)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -2,6 +2,8 @@ import { type OpenClawBrainInstallLayout } from "./openclaw-plugin-install.js";
|
|
|
2
2
|
export type OpenClawBrainHookInstallState = "installed" | "not_installed" | "blocked_by_allowlist" | "unverified";
|
|
3
3
|
export type OpenClawBrainHookLoadability = "loadable" | "blocked" | "not_installed" | "unverified";
|
|
4
4
|
export type OpenClawBrainHookLoadProof = "status_probe_ready" | "not_ready";
|
|
5
|
+
export type OpenClawBrainHookGuardSeverity = "none" | "degraded" | "blocking";
|
|
6
|
+
export type OpenClawBrainHookGuardActionability = "none" | "pin_openclaw_home" | "repair_install";
|
|
5
7
|
export type OpenClawBrainPluginAllowlistState = "unrestricted" | "allowed" | "blocked" | "invalid" | "unverified";
|
|
6
8
|
export interface OpenClawBrainHookInspection {
|
|
7
9
|
scope: "exact_openclaw_home" | "activation_root_only";
|
|
@@ -24,6 +26,10 @@ export interface OpenClawBrainHookInspection {
|
|
|
24
26
|
}
|
|
25
27
|
export interface OpenClawBrainHookLoadSummary extends OpenClawBrainHookInspection {
|
|
26
28
|
loadProof: OpenClawBrainHookLoadProof;
|
|
29
|
+
guardSeverity: OpenClawBrainHookGuardSeverity;
|
|
30
|
+
guardActionability: OpenClawBrainHookGuardActionability;
|
|
31
|
+
guardSummary: string;
|
|
32
|
+
guardAction: string;
|
|
27
33
|
}
|
|
28
34
|
export declare function inspectOpenClawBrainPluginAllowlist(openclawHome: string): {
|
|
29
35
|
state: Exclude<OpenClawBrainPluginAllowlistState, "unverified">;
|
|
@@ -74,6 +74,32 @@ function describeAdditionalInstallDetail(additionalInstalls) {
|
|
|
74
74
|
.map((install) => `${shortenPath(install.extensionDir)} (${describeOpenClawBrainInstallLayout(install.installLayout)}, ${describeOpenClawBrainInstallIdentity(install)})`)
|
|
75
75
|
.join(", ")}`;
|
|
76
76
|
}
|
|
77
|
+
function summarizeOpenClawBrainHookGuard(inspection) {
|
|
78
|
+
if (inspection.scope === "activation_root_only") {
|
|
79
|
+
return {
|
|
80
|
+
guardSeverity: "degraded",
|
|
81
|
+
guardActionability: "pin_openclaw_home",
|
|
82
|
+
guardSummary: "current-profile hook state is not self-proven from this status scope",
|
|
83
|
+
guardAction: "Rerun status with --openclaw-home <path> to prove the current profile hook."
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (inspection.installState === "installed" && inspection.loadability === "loadable") {
|
|
87
|
+
return {
|
|
88
|
+
guardSeverity: "none",
|
|
89
|
+
guardActionability: "none",
|
|
90
|
+
guardSummary: "profile hook is installed and loadable",
|
|
91
|
+
guardAction: "none"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
guardSeverity: "blocking",
|
|
96
|
+
guardActionability: "repair_install",
|
|
97
|
+
guardSummary: inspection.installState === "not_installed"
|
|
98
|
+
? "profile hook is missing or incomplete"
|
|
99
|
+
: "profile hook is present but OpenClaw will not load it",
|
|
100
|
+
guardAction: "Run openclawbrain install --openclaw-home <path> to repair the installed hook."
|
|
101
|
+
};
|
|
102
|
+
}
|
|
77
103
|
export function inspectOpenClawBrainPluginAllowlist(openclawHome) {
|
|
78
104
|
const { path: openclawJsonPath, config } = readOpenClawJsonConfig(openclawHome);
|
|
79
105
|
const installedPlugin = findInstalledOpenClawBrainPlugin(openclawHome);
|
|
@@ -252,6 +278,7 @@ export function inspectOpenClawBrainHookStatus(openclawHome) {
|
|
|
252
278
|
export function summarizeOpenClawBrainHookLoad(inspection, statusProbeReady) {
|
|
253
279
|
return {
|
|
254
280
|
...inspection,
|
|
281
|
+
...summarizeOpenClawBrainHookGuard(inspection),
|
|
255
282
|
loadProof: inspection.loadability === "loadable" && statusProbeReady
|
|
256
283
|
? "status_probe_ready"
|
|
257
284
|
: "not_ready"
|