@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/extension/index.js
CHANGED
|
@@ -44,8 +44,26 @@ async function reportDiagnostic(input) {
|
|
|
44
44
|
}
|
|
45
45
|
warnedDiagnostics.add(input.key);
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const formatted = formatDiagnosticMessage(input);
|
|
48
|
+
console.warn(formatted);
|
|
49
|
+
await appendLocalDiagnosticLog(formatted);
|
|
50
|
+
}
|
|
51
|
+
function formatDiagnosticMessage(input) {
|
|
52
|
+
if (input.severity === undefined ||
|
|
53
|
+
input.actionability === undefined ||
|
|
54
|
+
input.summary === undefined ||
|
|
55
|
+
input.action === undefined) {
|
|
56
|
+
return input.message;
|
|
57
|
+
}
|
|
58
|
+
const detail = input.message.replace(/^\[openclawbrain\]\s*/, "");
|
|
59
|
+
return [
|
|
60
|
+
"[openclawbrain]",
|
|
61
|
+
`severity=${input.severity}`,
|
|
62
|
+
`actionability=${input.actionability}`,
|
|
63
|
+
`summary=${JSON.stringify(input.summary)}`,
|
|
64
|
+
`action=${JSON.stringify(input.action)}`,
|
|
65
|
+
`detail=${JSON.stringify(detail)}`
|
|
66
|
+
].join(" ");
|
|
49
67
|
}
|
|
50
68
|
function announceStartupBreadcrumb() {
|
|
51
69
|
if (isActivationRootPlaceholder(ACTIVATION_ROOT)) {
|
|
@@ -78,8 +96,12 @@ export default function register(api) {
|
|
|
78
96
|
catch (error) {
|
|
79
97
|
const detail = error instanceof Error ? error.message : String(error);
|
|
80
98
|
void reportDiagnostic({
|
|
81
|
-
key:
|
|
99
|
+
key: "runtime-load-proof-failed",
|
|
82
100
|
once: true,
|
|
101
|
+
severity: "degraded",
|
|
102
|
+
actionability: "inspect_local_proof_write",
|
|
103
|
+
summary: "runtime-load proof write failed after hook registration",
|
|
104
|
+
action: "Inspect local filesystem permissions and the activation-root proof path if proof capture is expected.",
|
|
83
105
|
message: `[openclawbrain] runtime load proof failed: ${detail}`
|
|
84
106
|
});
|
|
85
107
|
}
|
|
@@ -91,8 +113,12 @@ export default function register(api) {
|
|
|
91
113
|
void reportDiagnostic({
|
|
92
114
|
key: "registration-failed",
|
|
93
115
|
once: true,
|
|
116
|
+
severity: "blocking",
|
|
117
|
+
actionability: "rerun_install",
|
|
118
|
+
summary: "extension registration threw before the runtime hook was fully attached",
|
|
119
|
+
action: "Rerun openclawbrain install --openclaw-home <path>; if it still fails, inspect the extension loader/runtime.",
|
|
94
120
|
message: `[openclawbrain] extension registration failed: ${detail}`
|
|
95
121
|
});
|
|
96
122
|
}
|
|
97
123
|
}
|
|
98
|
-
//# sourceMappingURL=index.js.map
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -22,10 +22,16 @@ export interface ExtensionCompileFailure {
|
|
|
22
22
|
}
|
|
23
23
|
export type ExtensionCompileResult = ExtensionCompileSuccess | ExtensionCompileFailure;
|
|
24
24
|
export type ExtensionCompileRuntimeContext = (input: ExtensionCompileInput) => ExtensionCompileResult;
|
|
25
|
+
export type ExtensionDiagnosticSeverity = "degraded" | "blocking";
|
|
26
|
+
export type ExtensionDiagnosticActionability = "inspect_host_event_shape" | "inspect_host_registration_api" | "inspect_local_proof_write" | "inspect_runtime_compile" | "rerun_install";
|
|
25
27
|
export interface ExtensionDiagnostic {
|
|
26
28
|
key: string;
|
|
27
29
|
message: string;
|
|
28
30
|
once?: boolean;
|
|
31
|
+
severity?: ExtensionDiagnosticSeverity;
|
|
32
|
+
actionability?: ExtensionDiagnosticActionability;
|
|
33
|
+
summary?: string;
|
|
34
|
+
action?: string;
|
|
29
35
|
}
|
|
30
36
|
export interface ExtensionRegistrationApi {
|
|
31
37
|
on(eventName: string, handler: (event: unknown, ctx: unknown) => Promise<Record<string, unknown>>, options?: {
|
|
@@ -5,12 +5,12 @@ export function validateExtensionRegistrationApi(api) {
|
|
|
5
5
|
if (!isRecord(api) || typeof api.on !== "function") {
|
|
6
6
|
return {
|
|
7
7
|
ok: false,
|
|
8
|
-
diagnostic: {
|
|
8
|
+
diagnostic: shapeDiagnostic({
|
|
9
9
|
key: "registration-api-invalid",
|
|
10
10
|
once: true,
|
|
11
11
|
message: `[openclawbrain] extension inactive: host registration API is missing api.on(event, handler, options) ` +
|
|
12
12
|
`(received=${describeValue(api)})`
|
|
13
|
-
}
|
|
13
|
+
})
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
return {
|
|
@@ -64,11 +64,11 @@ export function normalizePromptBuildEvent(event) {
|
|
|
64
64
|
export function createBeforePromptBuildHandler(input) {
|
|
65
65
|
return async (event, _ctx) => {
|
|
66
66
|
if (isActivationRootPlaceholder(input.activationRoot)) {
|
|
67
|
-
await input.reportDiagnostic({
|
|
67
|
+
await input.reportDiagnostic(shapeDiagnostic({
|
|
68
68
|
key: "activation-root-placeholder",
|
|
69
69
|
once: true,
|
|
70
70
|
message: "[openclawbrain] BRAIN NOT YET LOADED: ACTIVATION_ROOT is still a placeholder. Install @openclawbrain/cli, then run: openclawbrain install --openclaw-home <path>"
|
|
71
|
-
});
|
|
71
|
+
}));
|
|
72
72
|
return {};
|
|
73
73
|
}
|
|
74
74
|
const normalized = normalizePromptBuildEvent(event);
|
|
@@ -103,11 +103,11 @@ export function createBeforePromptBuildHandler(input) {
|
|
|
103
103
|
});
|
|
104
104
|
if (!result.ok) {
|
|
105
105
|
const mode = result.hardRequirementViolated ? "hard-fail" : "fail-open";
|
|
106
|
-
await input.reportDiagnostic({
|
|
106
|
+
await input.reportDiagnostic(shapeDiagnostic({
|
|
107
107
|
key: `compile-${mode}`,
|
|
108
108
|
message: `[openclawbrain] ${mode}: ${result.error} ` +
|
|
109
109
|
`(activationRoot=${input.activationRoot}, sessionId=${normalized.event.sessionId ?? "unknown"}, channel=${normalized.event.channel ?? "unknown"})`
|
|
110
|
-
});
|
|
110
|
+
}));
|
|
111
111
|
return {};
|
|
112
112
|
}
|
|
113
113
|
if (result.brainContext.length > 0) {
|
|
@@ -119,20 +119,20 @@ export function createBeforePromptBuildHandler(input) {
|
|
|
119
119
|
}
|
|
120
120
|
catch (error) {
|
|
121
121
|
const detail = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
122
|
-
await input.reportDiagnostic({
|
|
122
|
+
await input.reportDiagnostic(shapeDiagnostic({
|
|
123
123
|
key: "compile-threw",
|
|
124
124
|
message: `[openclawbrain] compile threw: ${detail} ` +
|
|
125
125
|
`(activationRoot=${input.activationRoot}, sessionId=${normalized.event.sessionId ?? "unknown"}, channel=${normalized.event.channel ?? "unknown"})`
|
|
126
|
-
});
|
|
126
|
+
}));
|
|
127
127
|
}
|
|
128
128
|
return {};
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
function failOpenDiagnostic(key, reason, detail) {
|
|
132
|
-
return {
|
|
132
|
+
return shapeDiagnostic({
|
|
133
133
|
key,
|
|
134
134
|
message: `[openclawbrain] fail-open: ${reason} (${detail})`
|
|
135
|
-
};
|
|
135
|
+
});
|
|
136
136
|
}
|
|
137
137
|
function normalizeOptionalScalarField(value, fieldName, warnings) {
|
|
138
138
|
if (value === undefined || value === null) {
|
|
@@ -145,11 +145,11 @@ function normalizeOptionalScalarField(value, fieldName, warnings) {
|
|
|
145
145
|
if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
|
|
146
146
|
return String(value);
|
|
147
147
|
}
|
|
148
|
-
warnings.push({
|
|
148
|
+
warnings.push(shapeDiagnostic({
|
|
149
149
|
key: `runtime-${fieldName}-ignored`,
|
|
150
150
|
message: `[openclawbrain] fail-open: ignored unsupported before_prompt_build ${fieldName} ` +
|
|
151
151
|
`(${fieldName}=${describeValue(value)})`
|
|
152
|
-
});
|
|
152
|
+
}));
|
|
153
153
|
return undefined;
|
|
154
154
|
}
|
|
155
155
|
function normalizeOptionalNonNegativeIntegerField(value, fieldName, warnings) {
|
|
@@ -174,11 +174,11 @@ function normalizeOptionalNonNegativeIntegerField(value, fieldName, warnings) {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
warnings.push({
|
|
177
|
+
warnings.push(shapeDiagnostic({
|
|
178
178
|
key: `runtime-${fieldName}-ignored`,
|
|
179
179
|
message: `[openclawbrain] fail-open: ignored unsupported before_prompt_build ${fieldName} ` +
|
|
180
180
|
`(${fieldName}=${describeValue(value)})`
|
|
181
|
-
});
|
|
181
|
+
}));
|
|
182
182
|
return undefined;
|
|
183
183
|
}
|
|
184
184
|
function extractPromptMessage(message) {
|
|
@@ -264,7 +264,63 @@ function describeValue(value) {
|
|
|
264
264
|
}
|
|
265
265
|
return `${typeof value}(${String(value)})`;
|
|
266
266
|
}
|
|
267
|
+
function shapeDiagnostic(diagnostic) {
|
|
268
|
+
if (diagnostic.severity !== undefined &&
|
|
269
|
+
diagnostic.actionability !== undefined &&
|
|
270
|
+
diagnostic.summary !== undefined &&
|
|
271
|
+
diagnostic.action !== undefined) {
|
|
272
|
+
return diagnostic;
|
|
273
|
+
}
|
|
274
|
+
if (diagnostic.key === "activation-root-placeholder") {
|
|
275
|
+
return {
|
|
276
|
+
...diagnostic,
|
|
277
|
+
severity: "blocking",
|
|
278
|
+
actionability: "rerun_install",
|
|
279
|
+
summary: "extension hook is installed but ACTIVATION_ROOT is still unpinned",
|
|
280
|
+
action: "Run openclawbrain install --openclaw-home <path> to pin the runtime hook."
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (diagnostic.key === "registration-api-invalid") {
|
|
284
|
+
return {
|
|
285
|
+
...diagnostic,
|
|
286
|
+
severity: "blocking",
|
|
287
|
+
actionability: "inspect_host_registration_api",
|
|
288
|
+
summary: "extension host registration API is missing or incompatible",
|
|
289
|
+
action: "Repair or upgrade the host extension API so api.on(event, handler, options) is available."
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
if (diagnostic.key === "compile-hard-fail") {
|
|
293
|
+
return {
|
|
294
|
+
...diagnostic,
|
|
295
|
+
severity: "blocking",
|
|
296
|
+
actionability: "inspect_runtime_compile",
|
|
297
|
+
summary: "brain context compile hit a hard requirement",
|
|
298
|
+
action: "Inspect the activation root and compile error; rerun install if the pinned hook may be stale."
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (diagnostic.key === "compile-fail-open" || diagnostic.key === "compile-threw") {
|
|
302
|
+
return {
|
|
303
|
+
...diagnostic,
|
|
304
|
+
severity: "degraded",
|
|
305
|
+
actionability: "inspect_runtime_compile",
|
|
306
|
+
summary: diagnostic.key === "compile-threw"
|
|
307
|
+
? "brain context compile threw during before_prompt_build"
|
|
308
|
+
: "brain context compile failed open during before_prompt_build",
|
|
309
|
+
action: "Inspect the activation root and compile error if brain context is unexpectedly empty."
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
if (diagnostic.key.startsWith("runtime-")) {
|
|
313
|
+
return {
|
|
314
|
+
...diagnostic,
|
|
315
|
+
severity: "degraded",
|
|
316
|
+
actionability: "inspect_host_event_shape",
|
|
317
|
+
summary: "before_prompt_build payload was partial or malformed",
|
|
318
|
+
action: "Inspect the host before_prompt_build event shape; OpenClawBrain fail-opened safely."
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return diagnostic;
|
|
322
|
+
}
|
|
267
323
|
function isRecord(value) {
|
|
268
324
|
return typeof value === "object" && value !== null;
|
|
269
325
|
}
|
|
270
|
-
//# sourceMappingURL=runtime-guard.js.map
|
|
326
|
+
//# sourceMappingURL=runtime-guard.js.map
|
package/dist/src/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { DEFAULT_OLLAMA_EMBEDDING_MODEL, createOllamaEmbedder } from "@openclawb
|
|
|
9
9
|
import { ensureManagedLearnerServiceForActivationRoot, inspectManagedLearnerService, removeManagedLearnerServiceForActivationRoot, parseDaemonArgs, runDaemonCommand } from "./daemon.js";
|
|
10
10
|
import { exportBrain, importBrain } from "./import-export.js";
|
|
11
11
|
import { buildNormalizedEventExport } from "@openclawbrain/contracts";
|
|
12
|
-
import { buildTeacherSupervisionArtifactsFromNormalizedEventExport, createAlwaysOnLearningRuntimeState, describeAlwaysOnLearningRuntimeState, drainAlwaysOnLearningRuntime, loadOrInitBaseline,
|
|
12
|
+
import { buildTeacherSupervisionArtifactsFromNormalizedEventExport, createAlwaysOnLearningRuntimeState, describeAlwaysOnLearningRuntimeState, drainAlwaysOnLearningRuntime, loadOrInitBaseline, materializeAlwaysOnLearningCandidatePack, persistBaseline } from "./local-learner.js";
|
|
13
13
|
import { inspectActivationState, loadPackFromActivation, promoteCandidatePack, readLearningSpineLogEntries, stageCandidatePack } from "@openclawbrain/pack-format";
|
|
14
14
|
import { resolveActivationRoot } from "./resolve-activation-root.js";
|
|
15
15
|
import { describeOpenClawHomeInspection, discoverOpenClawHomes, formatOpenClawHomeLayout, formatOpenClawHomeProfileSource, inspectOpenClawHome } from "./openclaw-home-layout.js";
|
|
@@ -17,14 +17,15 @@ import { inspectOpenClawBrainHookStatus, inspectOpenClawBrainPluginAllowlist } f
|
|
|
17
17
|
import { describeOpenClawBrainInstallIdentity, describeOpenClawBrainInstallLayout, findInstalledOpenClawBrainPlugin, getOpenClawBrainKnownPluginIds, normalizeOpenClawBrainPluginsConfig, pinInstalledOpenClawBrainPluginActivationRoot, resolveOpenClawBrainInstallTarget } from "./openclaw-plugin-install.js";
|
|
18
18
|
import { buildOpenClawBrainConvergeRestartPlan, classifyOpenClawBrainConvergeVerification, describeOpenClawBrainConvergeChangeReasons, diffOpenClawBrainConvergeRuntimeFingerprint, finalizeOpenClawBrainConvergeResult, planOpenClawBrainConvergePluginAction } from "./install-converge.js";
|
|
19
19
|
import { loadAttachmentPolicyDeclaration, resolveEffectiveAttachmentPolicyTruth, writeAttachmentPolicyDeclaration } from "./attachment-policy-truth.js";
|
|
20
|
-
import { DEFAULT_WATCH_POLL_INTERVAL_SECONDS, buildNormalizedEventExportFromScannedEvents, bootstrapRuntimeAttach, buildOperatorSurfaceReport, clearOpenClawProfileRuntimeLoadProof, compileRuntimeContext, createAsyncTeacherLiveLoop, createOpenClawLocalSessionTail, createRuntimeEventExportScanner, describeCurrentProfileBrainStatus, formatOperatorRollbackReport, listOpenClawProfileRuntimeLoadProofs, loadRuntimeEventExportBundle, loadWatchTeacherSnapshotState, persistWatchTeacherSnapshot, rollbackRuntimeAttach, resolveAttachmentRuntimeLoadProofsPath, resolveOperatorTeacherSnapshotPath, resolveAsyncTeacherLiveLoopSnapshotPath, resolveWatchSessionTailCursorPath, resolveWatchStateRoot, resolveWatchTeacherSnapshotPath, scanLiveEventExport, scanRecordedSession, summarizeLearningPathFromMaterialization, summarizeNormalizedEventExportLabelFlow, writeScannedEventExportBundle } from "./index.js";
|
|
20
|
+
import { DEFAULT_WATCH_POLL_INTERVAL_SECONDS, buildNormalizedEventExportFromScannedEvents, bootstrapRuntimeAttach, buildOperatorSurfaceReport, clearOpenClawProfileRuntimeLoadProof, compileRuntimeContext, createAsyncTeacherLiveLoop, createOpenClawLocalSessionTail, createRuntimeEventExportScanner, describeCurrentProfileBrainStatus, formatOperatorRollbackReport, listOpenClawProfileRuntimeLoadProofs, loadRuntimeEventExportBundle, loadWatchTeacherSnapshotState, persistWatchTeacherSnapshot, rollbackRuntimeAttach, resolveAttachmentRuntimeLoadProofsPath, resolveOperatorTeacherSnapshotPath, resolveAsyncTeacherLiveLoopSnapshotPath, resolveWatchSessionTailCursorPath, resolveWatchStateRoot, resolveWatchTeacherSnapshotPath, scanLiveEventExport, scanRecordedSession, summarizeLearningPathFromMaterialization, summarizeNormalizedEventExportLabelFlow, summarizeTeacherNoArtifactCycle, writeScannedEventExportBundle } from "./index.js";
|
|
21
21
|
import { appendLearningUpdateLogs } from "./learning-spine.js";
|
|
22
22
|
import { buildPassiveLearningSessionExportFromOpenClawSessionStore } from "./local-session-passive-learning.js";
|
|
23
|
+
import { reindexMaterializationCandidateWithEmbedder } from "./materialization-embedder.js";
|
|
23
24
|
import { summarizePackVectorEmbeddingState } from "./embedding-status.js";
|
|
24
25
|
import { buildTracedLearningBridgePayloadFromRuntime, buildTracedLearningStatusSurface, persistTracedLearningBridgeState } from "./traced-learning-bridge.js";
|
|
25
26
|
import { discoverOpenClawSessionStores, loadOpenClawSessionIndex, readOpenClawSessionFile } from "./session-store.js";
|
|
26
27
|
import { readOpenClawBrainProviderDefaults, readOpenClawBrainProviderConfig, readOpenClawBrainProviderConfigFromSources, resolveOpenClawBrainProviderDefaultsPath } from "./provider-config.js";
|
|
27
|
-
import { formatOperatorLearningPathSummary } from "./status-learning-path.js";
|
|
28
|
+
import { formatOperatorLearningAttributionSummary, formatOperatorLearningPathSummary } from "./status-learning-path.js";
|
|
28
29
|
import { buildProofCommandForOpenClawHome, buildProofCommandHelpSection, captureOperatorProofBundle, formatOperatorProofResult, parseProofCliArgs } from "./proof-command.js";
|
|
29
30
|
const OPENCLAWBRAIN_EMBEDDER_BASE_URL_ENV = "OPENCLAWBRAIN_EMBEDDER_BASE_URL";
|
|
30
31
|
const OPENCLAWBRAIN_EMBEDDER_PROVIDER_ENV = "OPENCLAWBRAIN_EMBEDDER_PROVIDER";
|
|
@@ -681,14 +682,14 @@ const LEARNING_WARNING_MESSAGES = {
|
|
|
681
682
|
passive_backfill_pending: "passive backfill remains queued",
|
|
682
683
|
teacher_queue_full: "teacher queue is full",
|
|
683
684
|
teacher_labels_stale: "teacher labels are stale",
|
|
684
|
-
teacher_no_artifacts: "
|
|
685
|
+
teacher_no_artifacts: "latest no-op cycle had teachable material but no new teacher artifact",
|
|
685
686
|
teacher_snapshot_unavailable: "teacher snapshot is unavailable"
|
|
686
687
|
};
|
|
687
688
|
const TEACHER_NO_OP_MESSAGES = {
|
|
688
689
|
none: "the latest processed export produced teacher artifacts",
|
|
689
690
|
duplicate_export: "the latest cycle was a no-op because the export was already seen",
|
|
690
691
|
queue_full: "the latest cycle was a no-op because the teacher queue was full",
|
|
691
|
-
no_teacher_artifacts: "the latest cycle
|
|
692
|
+
no_teacher_artifacts: "the latest cycle produced no new teacher artifacts",
|
|
692
693
|
empty_scan: "the latest cycle was a no-op because the scanner did not produce any events",
|
|
693
694
|
unavailable: "the latest cycle is not visible from the current operator snapshot"
|
|
694
695
|
};
|
|
@@ -707,6 +708,10 @@ function summarizeStatusHookLoad(installHook, status) {
|
|
|
707
708
|
installState: installHook.state === "unknown" ? "unverified" : installHook.state,
|
|
708
709
|
loadability: installHook.loadability,
|
|
709
710
|
loadProof: status.hook.loadProof,
|
|
711
|
+
guardSeverity: status.hook.guardSeverity,
|
|
712
|
+
guardActionability: status.hook.guardActionability,
|
|
713
|
+
guardSummary: status.hook.guardSummary,
|
|
714
|
+
guardAction: status.hook.guardAction,
|
|
710
715
|
detail: status.hook.detail
|
|
711
716
|
};
|
|
712
717
|
}
|
|
@@ -1077,14 +1082,16 @@ function summarizeStatusTeacher(report, providerConfig, localLlm) {
|
|
|
1077
1082
|
detail: `${providerConfig.teacher.model} is enabled on Ollama, but no watch teacher snapshot is visible yet`
|
|
1078
1083
|
};
|
|
1079
1084
|
}
|
|
1080
|
-
const stale = report.teacherLoop.
|
|
1085
|
+
const stale = report.teacherLoop.watchState === "stale_snapshot" || (report.teacherLoop.latestFreshness === "stale" && report.teacherLoop.lastNoOpReason !== "no_teacher_artifacts");
|
|
1081
1086
|
const idle = report.teacherLoop.running === false &&
|
|
1082
1087
|
(report.teacherLoop.queueDepth ?? 0) === 0 &&
|
|
1083
1088
|
report.teacherLoop.failureMode === "none";
|
|
1084
1089
|
const healthy = report.teacherLoop.failureMode === "none" &&
|
|
1085
1090
|
stale === false &&
|
|
1086
1091
|
report.teacherLoop.watchState !== "not_visible";
|
|
1087
|
-
const cycleDetail =
|
|
1092
|
+
const cycleDetail = report.teacherLoop.lastNoOpReason === "no_teacher_artifacts"
|
|
1093
|
+
? summarizeTeacherNoArtifactCycle(report.teacherLoop.notes).detail
|
|
1094
|
+
: TEACHER_NO_OP_MESSAGES[report.teacherLoop.lastNoOpReason] ?? "the latest teacher cycle detail is unavailable";
|
|
1088
1095
|
if (report.teacherLoop.failureMode !== "none" && report.teacherLoop.failureMode !== "unavailable") {
|
|
1089
1096
|
return {
|
|
1090
1097
|
model: providerConfig.teacher.model,
|
|
@@ -1344,7 +1351,7 @@ function buildCompactStatusHeader(status, report, options) {
|
|
|
1344
1351
|
const tracedLearning = options.tracedLearning ?? buildTracedLearningStatusSurface(status.host.activationRoot);
|
|
1345
1352
|
return [
|
|
1346
1353
|
`lifecycle attach=${status.attachment.state} learner=${yesNo(status.passiveLearning.learnerRunning)} watch=${summarizeStatusWatchState(status)} export=${status.passiveLearning.exportState} promote=${summarizeStatusPromotionState(status)} serve=${summarizeStatusServeReality(status)}`,
|
|
1347
|
-
`hook install=${hookLoad.installState} loadability=${hookLoad.loadability} loadProof=${hookLoad.loadProof} layout=${status.hook.installLayout ?? "unverified"} additional=${status.hook.additionalInstallCount ?? 0}
|
|
1354
|
+
`hook install=${hookLoad.installState} loadability=${hookLoad.loadability} loadProof=${hookLoad.loadProof} layout=${status.hook.installLayout ?? "unverified"} additional=${status.hook.additionalInstallCount ?? 0} severity=${hookLoad.guardSeverity} actionability=${hookLoad.guardActionability} summary=${hookLoad.guardSummary}`,
|
|
1348
1355
|
`attachTruth current=${attachmentTruth.currentProfileLabel} hook=${attachmentTruth.hookFiles} config=${attachmentTruth.configLoad} runtime=${attachmentTruth.runtimeLoad} watcher=${attachmentTruth.watcher} attachedSet=${formatAttachedProfileTruthCompactList(attachmentTruth.attachedProfiles)} why=${attachmentTruth.detail}`,
|
|
1349
1356
|
`passive firstExport=${yesNo(status.passiveLearning.firstExportOccurred)} backlog=${status.passiveLearning.backlogState} pending=${formatStatusNullableNumber(status.passiveLearning.pendingLive)}/${formatStatusNullableNumber(status.passiveLearning.pendingBackfill)}`,
|
|
1350
1357
|
`serving pack=${status.passiveLearning.currentServingPackId ?? "none"} lastExport=${status.passiveLearning.lastExportAt ?? "none"} lastPromotion=${status.passiveLearning.lastPromotionAt ?? "none"}`,
|
|
@@ -1353,6 +1360,7 @@ function buildCompactStatusHeader(status, report, options) {
|
|
|
1353
1360
|
`changed ${status.passiveLearning.lastObservedDelta.explanation}`,
|
|
1354
1361
|
`explain ${status.brain.summary}`,
|
|
1355
1362
|
`graph blocks=${report.graph.blockCount ?? "none"} strongest=${report.graph.strongestBlockId ?? "none"} latest=${report.graph.latestMaterialization.packId ?? "none"} latestChanged=${yesNo(report.graph.latestMaterialization.changed)} connect=${formatCompactGraphConnectDiagnostics(report.graph.latestMaterialization.connectDiagnostics ?? report.graph.connectDiagnostics)}`,
|
|
1363
|
+
`attribution ${formatOperatorLearningAttributionSummary({ status })}`,
|
|
1356
1364
|
`teacher model=${teacher.model} enabled=${yesNo(teacher.enabled)} healthy=${yesNo(teacher.healthy)} stale=${yesNo(teacher.stale)} idle=${yesNo(teacher.idle)} cycle=${teacher.latestCycle} why=${teacher.detail}`,
|
|
1357
1365
|
`embedder model=${embedder.model} provisioned=${yesNo(embedder.provisioned)} live=${yesNo(embedder.live)} why=${embedder.detail}`,
|
|
1358
1366
|
`routeFn available=${yesNo(routeFn.available)} freshness=${routeFn.freshness} trained=${routeFn.trainedAt ?? "none"} updated=${routeFn.updatedAt ?? "none"} used=${routeFn.usedAt ?? "none"} why=${routeFn.detail}`,
|
|
@@ -1390,6 +1398,7 @@ function formatCurrentProfileStatusSummary(status, report, targetInspection, opt
|
|
|
1390
1398
|
})}`,
|
|
1391
1399
|
`host runtime=${status.host.runtimeOwner} activation=${status.host.activationRoot}`,
|
|
1392
1400
|
`profile selector=${status.profile.selector}${profileIdSuffix} attachment=${status.attachment.state} policy=${status.attachment.policyMode}`,
|
|
1401
|
+
`guard severity=${status.hook.guardSeverity} actionability=${status.hook.guardActionability} action=${status.hook.guardAction} summary=${status.hook.guardSummary}`,
|
|
1393
1402
|
`attachTruth current=${attachmentTruth.currentProfileLabel} hook=${attachmentTruth.hookFiles} config=${attachmentTruth.configLoad} runtime=${attachmentTruth.runtimeLoad} watcher=${attachmentTruth.watcher} detail=${attachmentTruth.detail}`,
|
|
1394
1403
|
`attachedSet ${formatAttachedProfileTruthDetailedList(attachmentTruth.attachedProfiles)} proofPath=${shortenPath(attachmentTruth.runtimeProofPath)} proofError=${attachmentTruth.runtimeProofError ?? "none"}`,
|
|
1395
1404
|
`manyProfile surface=${report.manyProfile.operatorSurface} policy=${report.manyProfile.declaredAttachmentPolicy} intent=${report.manyProfile.sameGatewayIntent} checkedProof=${report.manyProfile.checkedInProofTopology} sameGatewayProof=${yesNo(report.manyProfile.sameGatewayProof)} sharedWriteProof=${yesNo(report.manyProfile.sharedWriteSafetyProof)}`,
|
|
@@ -1410,6 +1419,7 @@ function formatCurrentProfileStatusSummary(status, report, targetInspection, opt
|
|
|
1410
1419
|
learningPath: report.learningPath,
|
|
1411
1420
|
tracedLearning
|
|
1412
1421
|
})}`,
|
|
1422
|
+
`attribution ${formatOperatorLearningAttributionSummary({ status })}`,
|
|
1413
1423
|
`learning state=${report.learning.backlogState} bootstrapped=${yesNo(report.learning.bootstrapped)} mode=${report.learning.mode} next=${report.learning.nextPriorityLane} priority=${report.learning.nextPriorityBucket} pending=${report.learning.pendingLive ?? "none"}/${report.learning.pendingBackfill ?? "none"} buckets=${formatLearningBuckets(report)} warn=${formatLearningWarnings(report)} lastPack=${report.learning.lastMaterializedPackId ?? "none"} detail=${report.learning.detail}`,
|
|
1414
1424
|
`traced ${formatTracedLearningSurface(tracedLearning)}`,
|
|
1415
1425
|
`teacherProof ${formatTeacherLoopSummary(report)}`,
|
|
@@ -1615,11 +1625,13 @@ function inspectInstallConvergeVerification(parsed) {
|
|
|
1615
1625
|
openclawHome: parsed.openclawHome,
|
|
1616
1626
|
installHook
|
|
1617
1627
|
}),
|
|
1618
|
-
summaryLine: `STATUS ${displayedStatus}; hook=${installHook.state}/${installHook.loadability}; runtime=${attachmentTruth.runtimeLoad}; loadProof=${normalizedStatusAndReport.status.hook.loadProof}; serve=${normalizedStatusAndReport.status.brainStatus.serveState}`,
|
|
1628
|
+
summaryLine: `STATUS ${displayedStatus}; hook=${installHook.state}/${installHook.loadability}; guard=${normalizedStatusAndReport.status.hook.guardSeverity}/${normalizedStatusAndReport.status.hook.guardActionability}; runtime=${attachmentTruth.runtimeLoad}; loadProof=${normalizedStatusAndReport.status.hook.loadProof}; serve=${normalizedStatusAndReport.status.brainStatus.serveState}`,
|
|
1619
1629
|
facts: {
|
|
1620
1630
|
installLayout: normalizedStatusAndReport.status.hook.installLayout ?? installHook.installLayout ?? null,
|
|
1621
1631
|
installState: installHook.state,
|
|
1622
1632
|
loadability: installHook.loadability,
|
|
1633
|
+
guardSeverity: normalizedStatusAndReport.status.hook.guardSeverity,
|
|
1634
|
+
guardActionability: normalizedStatusAndReport.status.hook.guardActionability,
|
|
1623
1635
|
displayedStatus,
|
|
1624
1636
|
runtimeLoad: attachmentTruth.runtimeLoad,
|
|
1625
1637
|
loadProof: normalizedStatusAndReport.status.hook.loadProof,
|
|
@@ -3110,10 +3122,133 @@ function buildExtensionPluginManifest() {
|
|
|
3110
3122
|
name: "OpenClawBrain",
|
|
3111
3123
|
description: "Learned memory and context from OpenClawBrain",
|
|
3112
3124
|
version: packageMetadata.version,
|
|
3125
|
+
uiHints: {
|
|
3126
|
+
brainRoot: {
|
|
3127
|
+
label: "Brain Root",
|
|
3128
|
+
help: "Directory containing OpenClawBrain state.db and immutable packs"
|
|
3129
|
+
},
|
|
3130
|
+
brainEmbeddingProvider: {
|
|
3131
|
+
label: "Embedding Provider",
|
|
3132
|
+
help: "Provider used for learned retrieval embeddings"
|
|
3133
|
+
},
|
|
3134
|
+
brainEmbeddingModel: {
|
|
3135
|
+
label: "Embedding Model",
|
|
3136
|
+
help: "Embedding model used for init, retrieval, and brain_teach"
|
|
3137
|
+
},
|
|
3138
|
+
brainEmbeddingBaseUrl: {
|
|
3139
|
+
label: "Embedding Base URL",
|
|
3140
|
+
help: "Optional base URL override for the embedding provider endpoint"
|
|
3141
|
+
},
|
|
3142
|
+
brainMaxCompileMs: {
|
|
3143
|
+
label: "Brain Compile Deadline",
|
|
3144
|
+
help: "Soft wall-clock deadline in milliseconds for brain assembly phase-boundary checks"
|
|
3145
|
+
},
|
|
3146
|
+
brainBudgetFraction: {
|
|
3147
|
+
label: "Brain Budget Fraction",
|
|
3148
|
+
help: "Fraction of the available token budget reserved for retrieval before final prompt clipping"
|
|
3149
|
+
},
|
|
3150
|
+
brainMaxHops: {
|
|
3151
|
+
label: "Brain Max Hops",
|
|
3152
|
+
help: "Maximum learned-retrieval graph expansion depth per query"
|
|
3153
|
+
},
|
|
3154
|
+
brainMaxFanoutPerNode: {
|
|
3155
|
+
label: "Brain Max Fanout Per Node",
|
|
3156
|
+
help: "Maximum accepted traversals from a single source node expansion"
|
|
3157
|
+
},
|
|
3158
|
+
brainMaxFrontierSize: {
|
|
3159
|
+
label: "Brain Max Frontier Size",
|
|
3160
|
+
help: "Maximum traversal frontier size during learned retrieval"
|
|
3161
|
+
},
|
|
3162
|
+
brainMaxSeeds: {
|
|
3163
|
+
label: "Brain Max Seeds",
|
|
3164
|
+
help: "Maximum seed nodes admitted into learned retrieval before graph expansion"
|
|
3165
|
+
},
|
|
3166
|
+
brainSemanticThreshold: {
|
|
3167
|
+
label: "Brain Semantic Threshold",
|
|
3168
|
+
help: "Minimum semantic similarity required for seed admission during learned retrieval"
|
|
3169
|
+
},
|
|
3170
|
+
brainShadowMode: {
|
|
3171
|
+
label: "Brain Shadow Mode",
|
|
3172
|
+
help: "Run learned retrieval for telemetry only without injecting brain context into prompts"
|
|
3173
|
+
},
|
|
3174
|
+
brainWorkerMode: {
|
|
3175
|
+
label: "Worker Mode",
|
|
3176
|
+
help: "Run the learner in a supervised child process (default) or fall back to in-process mode"
|
|
3177
|
+
},
|
|
3178
|
+
brainWorkerHeartbeatTimeoutMs: {
|
|
3179
|
+
label: "Worker Heartbeat Timeout",
|
|
3180
|
+
help: "Milliseconds to wait before treating the supervised learner worker as stalled"
|
|
3181
|
+
},
|
|
3182
|
+
brainWorkerRestartDelayMs: {
|
|
3183
|
+
label: "Worker Restart Delay",
|
|
3184
|
+
help: "Milliseconds to wait before restarting the supervised learner worker after exit or crash"
|
|
3185
|
+
}
|
|
3186
|
+
},
|
|
3113
3187
|
configSchema: {
|
|
3114
3188
|
type: "object",
|
|
3115
3189
|
additionalProperties: false,
|
|
3116
|
-
properties: {
|
|
3190
|
+
properties: {
|
|
3191
|
+
brainEnabled: {
|
|
3192
|
+
type: "boolean"
|
|
3193
|
+
},
|
|
3194
|
+
brainRoot: {
|
|
3195
|
+
type: "string"
|
|
3196
|
+
},
|
|
3197
|
+
brainEmbeddingProvider: {
|
|
3198
|
+
type: "string"
|
|
3199
|
+
},
|
|
3200
|
+
brainEmbeddingModel: {
|
|
3201
|
+
type: "string"
|
|
3202
|
+
},
|
|
3203
|
+
brainEmbeddingBaseUrl: {
|
|
3204
|
+
type: "string"
|
|
3205
|
+
},
|
|
3206
|
+
brainMaxCompileMs: {
|
|
3207
|
+
type: "integer",
|
|
3208
|
+
minimum: 0
|
|
3209
|
+
},
|
|
3210
|
+
brainBudgetFraction: {
|
|
3211
|
+
type: "number",
|
|
3212
|
+
minimum: 0,
|
|
3213
|
+
maximum: 1
|
|
3214
|
+
},
|
|
3215
|
+
brainMaxHops: {
|
|
3216
|
+
type: "integer",
|
|
3217
|
+
minimum: 1
|
|
3218
|
+
},
|
|
3219
|
+
brainMaxFanoutPerNode: {
|
|
3220
|
+
type: "integer",
|
|
3221
|
+
minimum: 1
|
|
3222
|
+
},
|
|
3223
|
+
brainMaxFrontierSize: {
|
|
3224
|
+
type: "integer",
|
|
3225
|
+
minimum: 1
|
|
3226
|
+
},
|
|
3227
|
+
brainMaxSeeds: {
|
|
3228
|
+
type: "integer",
|
|
3229
|
+
minimum: 1
|
|
3230
|
+
},
|
|
3231
|
+
brainSemanticThreshold: {
|
|
3232
|
+
type: "number",
|
|
3233
|
+
minimum: 0,
|
|
3234
|
+
maximum: 1
|
|
3235
|
+
},
|
|
3236
|
+
brainShadowMode: {
|
|
3237
|
+
type: "boolean"
|
|
3238
|
+
},
|
|
3239
|
+
brainWorkerMode: {
|
|
3240
|
+
type: "string",
|
|
3241
|
+
enum: ["child", "in_process"]
|
|
3242
|
+
},
|
|
3243
|
+
brainWorkerHeartbeatTimeoutMs: {
|
|
3244
|
+
type: "integer",
|
|
3245
|
+
minimum: 1000
|
|
3246
|
+
},
|
|
3247
|
+
brainWorkerRestartDelayMs: {
|
|
3248
|
+
type: "integer",
|
|
3249
|
+
minimum: 0
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3117
3252
|
}
|
|
3118
3253
|
}, null, 2) + "\n";
|
|
3119
3254
|
}
|
|
@@ -4367,7 +4502,7 @@ function persistWatchTracedLearningBridgeSurface(input) {
|
|
|
4367
4502
|
}
|
|
4368
4503
|
}));
|
|
4369
4504
|
}
|
|
4370
|
-
function runLearnCommand(parsed) {
|
|
4505
|
+
async function runLearnCommand(parsed) {
|
|
4371
4506
|
const learnStatePath = path.join(parsed.activationRoot, "learn-cli-state.json");
|
|
4372
4507
|
const teacherSnapshotPath = resolveAsyncTeacherLiveLoopSnapshotPath(parsed.activationRoot);
|
|
4373
4508
|
function isLearnRuntimeStateLike(value) {
|
|
@@ -4580,6 +4715,7 @@ function runLearnCommand(parsed) {
|
|
|
4580
4715
|
return 0;
|
|
4581
4716
|
}
|
|
4582
4717
|
const learningExport = normalizedEventExport;
|
|
4718
|
+
const resolvedEmbedder = resolveCliEmbedderConfig(undefined, activationRoot);
|
|
4583
4719
|
const serveTimeLearning = resolveServeTimeLearningRuntimeInput(activationRoot);
|
|
4584
4720
|
const learnerResult = drainAlwaysOnLearningRuntime({
|
|
4585
4721
|
packLabel: "learn-cli",
|
|
@@ -4602,7 +4738,11 @@ function runLearnCommand(parsed) {
|
|
|
4602
4738
|
...(serveTimeLearning.baselineState !== undefined ? { baselineState: serveTimeLearning.baselineState } : {}),
|
|
4603
4739
|
activationRoot
|
|
4604
4740
|
});
|
|
4605
|
-
|
|
4741
|
+
let lastMaterialization = learnerResult.materializations.at(-1) ?? null;
|
|
4742
|
+
lastMaterialization = await reindexMaterializationCandidateWithEmbedder(lastMaterialization, resolvedEmbedder.embedder);
|
|
4743
|
+
if (lastMaterialization !== null && learnerResult.materializations.length > 0) {
|
|
4744
|
+
learnerResult.materializations[learnerResult.materializations.length - 1] = lastMaterialization;
|
|
4745
|
+
}
|
|
4606
4746
|
const plan = describeAlwaysOnLearningRuntimeState(learnerResult.state, lastMaterialization);
|
|
4607
4747
|
const learningPath = summarizeLearningPathFromMaterialization(lastMaterialization);
|
|
4608
4748
|
const graphEvolution = lastMaterialization?.candidate.payloads.graph.evolution;
|
|
@@ -4995,14 +5135,9 @@ async function applyWatchMaterialization(activationRoot, snapshot, lastHandledMa
|
|
|
4995
5135
|
failure: null
|
|
4996
5136
|
};
|
|
4997
5137
|
}
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
candidate: await reindexCandidatePackBuildResultWithEmbedder(materialization.candidate, embedder)
|
|
5002
|
-
};
|
|
5003
|
-
if (snapshot?.learner !== undefined && snapshot.learner !== null) {
|
|
5004
|
-
snapshot.learner.lastMaterialization = materialization;
|
|
5005
|
-
}
|
|
5138
|
+
materialization = await reindexMaterializationCandidateWithEmbedder(materialization, embedder);
|
|
5139
|
+
if (snapshot?.learner !== undefined && snapshot.learner !== null) {
|
|
5140
|
+
snapshot.learner.lastMaterialization = materialization;
|
|
5006
5141
|
}
|
|
5007
5142
|
const shortPackId = packId.length > 16 ? packId.slice(0, 16) : packId;
|
|
5008
5143
|
const observedAt = new Date().toISOString();
|
|
@@ -5183,7 +5318,7 @@ function resolveWatchTeacherLabelerConfig(input, activationRoot) {
|
|
|
5183
5318
|
warnings
|
|
5184
5319
|
};
|
|
5185
5320
|
}
|
|
5186
|
-
function
|
|
5321
|
+
function resolveCliEmbedderConfig(input, activationRoot) {
|
|
5187
5322
|
if (input !== undefined) {
|
|
5188
5323
|
return {
|
|
5189
5324
|
embedder: input,
|
|
@@ -5367,7 +5502,7 @@ export async function createWatchCommandRuntime(input) {
|
|
|
5367
5502
|
log(`Scan root: ${shortenPath(scanRoot)}`);
|
|
5368
5503
|
log(`State: cursor=${shortenPath(sessionTailCursorPath)} snapshot=${shortenPath(teacherSnapshotPath)}`);
|
|
5369
5504
|
const resolvedTeacherLabeler = resolveWatchTeacherLabelerConfig(input.teacherLabeler, activationRoot);
|
|
5370
|
-
const resolvedEmbedder =
|
|
5505
|
+
const resolvedEmbedder = resolveCliEmbedderConfig(input.embedder, activationRoot);
|
|
5371
5506
|
const teacherLabeler = resolvedTeacherLabeler.teacherLabeler;
|
|
5372
5507
|
for (const warning of resolvedTeacherLabeler.warnings) {
|
|
5373
5508
|
startupWarnings.push(`teacher_config_warning:${warning}`);
|
|
@@ -5988,7 +6123,12 @@ export function runOperatorCli(argv = process.argv.slice(2)) {
|
|
|
5988
6123
|
return runHistoryCommand(parsed);
|
|
5989
6124
|
}
|
|
5990
6125
|
if (parsed.command === "learn") {
|
|
5991
|
-
|
|
6126
|
+
runLearnCommand(parsed).then((code) => { process.exitCode = code; }, (error) => {
|
|
6127
|
+
console.error("[openclawbrain] learn failed");
|
|
6128
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
6129
|
+
process.exitCode = 1;
|
|
6130
|
+
});
|
|
6131
|
+
return 0;
|
|
5992
6132
|
}
|
|
5993
6133
|
if (parsed.command === "watch") {
|
|
5994
6134
|
// Watch is async — bridge to sync CLI entry by scheduling and returning 0.
|