@openclawbrain/cli 0.4.14 → 0.4.15
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 +157 -19
- package/dist/src/index.d.ts +242 -3
- package/dist/src/index.js +1029 -38
- package/dist/src/learning-spine.js +43 -1
- package/dist/src/local-learner.d.ts +6 -0
- package/dist/src/local-learner.js +84 -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 +196 -1
- 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/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";
|
|
@@ -20,11 +20,12 @@ import { loadAttachmentPolicyDeclaration, resolveEffectiveAttachmentPolicyTruth,
|
|
|
20
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";
|
|
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";
|
|
@@ -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,7 +1082,7 @@ 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";
|
|
@@ -1344,7 +1349,7 @@ function buildCompactStatusHeader(status, report, options) {
|
|
|
1344
1349
|
const tracedLearning = options.tracedLearning ?? buildTracedLearningStatusSurface(status.host.activationRoot);
|
|
1345
1350
|
return [
|
|
1346
1351
|
`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}
|
|
1352
|
+
`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
1353
|
`attachTruth current=${attachmentTruth.currentProfileLabel} hook=${attachmentTruth.hookFiles} config=${attachmentTruth.configLoad} runtime=${attachmentTruth.runtimeLoad} watcher=${attachmentTruth.watcher} attachedSet=${formatAttachedProfileTruthCompactList(attachmentTruth.attachedProfiles)} why=${attachmentTruth.detail}`,
|
|
1349
1354
|
`passive firstExport=${yesNo(status.passiveLearning.firstExportOccurred)} backlog=${status.passiveLearning.backlogState} pending=${formatStatusNullableNumber(status.passiveLearning.pendingLive)}/${formatStatusNullableNumber(status.passiveLearning.pendingBackfill)}`,
|
|
1350
1355
|
`serving pack=${status.passiveLearning.currentServingPackId ?? "none"} lastExport=${status.passiveLearning.lastExportAt ?? "none"} lastPromotion=${status.passiveLearning.lastPromotionAt ?? "none"}`,
|
|
@@ -1353,6 +1358,7 @@ function buildCompactStatusHeader(status, report, options) {
|
|
|
1353
1358
|
`changed ${status.passiveLearning.lastObservedDelta.explanation}`,
|
|
1354
1359
|
`explain ${status.brain.summary}`,
|
|
1355
1360
|
`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)}`,
|
|
1361
|
+
`attribution ${formatOperatorLearningAttributionSummary({ status })}`,
|
|
1356
1362
|
`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
1363
|
`embedder model=${embedder.model} provisioned=${yesNo(embedder.provisioned)} live=${yesNo(embedder.live)} why=${embedder.detail}`,
|
|
1358
1364
|
`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 +1396,7 @@ function formatCurrentProfileStatusSummary(status, report, targetInspection, opt
|
|
|
1390
1396
|
})}`,
|
|
1391
1397
|
`host runtime=${status.host.runtimeOwner} activation=${status.host.activationRoot}`,
|
|
1392
1398
|
`profile selector=${status.profile.selector}${profileIdSuffix} attachment=${status.attachment.state} policy=${status.attachment.policyMode}`,
|
|
1399
|
+
`guard severity=${status.hook.guardSeverity} actionability=${status.hook.guardActionability} action=${status.hook.guardAction} summary=${status.hook.guardSummary}`,
|
|
1393
1400
|
`attachTruth current=${attachmentTruth.currentProfileLabel} hook=${attachmentTruth.hookFiles} config=${attachmentTruth.configLoad} runtime=${attachmentTruth.runtimeLoad} watcher=${attachmentTruth.watcher} detail=${attachmentTruth.detail}`,
|
|
1394
1401
|
`attachedSet ${formatAttachedProfileTruthDetailedList(attachmentTruth.attachedProfiles)} proofPath=${shortenPath(attachmentTruth.runtimeProofPath)} proofError=${attachmentTruth.runtimeProofError ?? "none"}`,
|
|
1395
1402
|
`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 +1417,7 @@ function formatCurrentProfileStatusSummary(status, report, targetInspection, opt
|
|
|
1410
1417
|
learningPath: report.learningPath,
|
|
1411
1418
|
tracedLearning
|
|
1412
1419
|
})}`,
|
|
1420
|
+
`attribution ${formatOperatorLearningAttributionSummary({ status })}`,
|
|
1413
1421
|
`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
1422
|
`traced ${formatTracedLearningSurface(tracedLearning)}`,
|
|
1415
1423
|
`teacherProof ${formatTeacherLoopSummary(report)}`,
|
|
@@ -1615,11 +1623,13 @@ function inspectInstallConvergeVerification(parsed) {
|
|
|
1615
1623
|
openclawHome: parsed.openclawHome,
|
|
1616
1624
|
installHook
|
|
1617
1625
|
}),
|
|
1618
|
-
summaryLine: `STATUS ${displayedStatus}; hook=${installHook.state}/${installHook.loadability}; runtime=${attachmentTruth.runtimeLoad}; loadProof=${normalizedStatusAndReport.status.hook.loadProof}; serve=${normalizedStatusAndReport.status.brainStatus.serveState}`,
|
|
1626
|
+
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
1627
|
facts: {
|
|
1620
1628
|
installLayout: normalizedStatusAndReport.status.hook.installLayout ?? installHook.installLayout ?? null,
|
|
1621
1629
|
installState: installHook.state,
|
|
1622
1630
|
loadability: installHook.loadability,
|
|
1631
|
+
guardSeverity: normalizedStatusAndReport.status.hook.guardSeverity,
|
|
1632
|
+
guardActionability: normalizedStatusAndReport.status.hook.guardActionability,
|
|
1623
1633
|
displayedStatus,
|
|
1624
1634
|
runtimeLoad: attachmentTruth.runtimeLoad,
|
|
1625
1635
|
loadProof: normalizedStatusAndReport.status.hook.loadProof,
|
|
@@ -3110,10 +3120,133 @@ function buildExtensionPluginManifest() {
|
|
|
3110
3120
|
name: "OpenClawBrain",
|
|
3111
3121
|
description: "Learned memory and context from OpenClawBrain",
|
|
3112
3122
|
version: packageMetadata.version,
|
|
3123
|
+
uiHints: {
|
|
3124
|
+
brainRoot: {
|
|
3125
|
+
label: "Brain Root",
|
|
3126
|
+
help: "Directory containing OpenClawBrain state.db and immutable packs"
|
|
3127
|
+
},
|
|
3128
|
+
brainEmbeddingProvider: {
|
|
3129
|
+
label: "Embedding Provider",
|
|
3130
|
+
help: "Provider used for learned retrieval embeddings"
|
|
3131
|
+
},
|
|
3132
|
+
brainEmbeddingModel: {
|
|
3133
|
+
label: "Embedding Model",
|
|
3134
|
+
help: "Embedding model used for init, retrieval, and brain_teach"
|
|
3135
|
+
},
|
|
3136
|
+
brainEmbeddingBaseUrl: {
|
|
3137
|
+
label: "Embedding Base URL",
|
|
3138
|
+
help: "Optional base URL override for the embedding provider endpoint"
|
|
3139
|
+
},
|
|
3140
|
+
brainMaxCompileMs: {
|
|
3141
|
+
label: "Brain Compile Deadline",
|
|
3142
|
+
help: "Soft wall-clock deadline in milliseconds for brain assembly phase-boundary checks"
|
|
3143
|
+
},
|
|
3144
|
+
brainBudgetFraction: {
|
|
3145
|
+
label: "Brain Budget Fraction",
|
|
3146
|
+
help: "Fraction of the available token budget reserved for retrieval before final prompt clipping"
|
|
3147
|
+
},
|
|
3148
|
+
brainMaxHops: {
|
|
3149
|
+
label: "Brain Max Hops",
|
|
3150
|
+
help: "Maximum learned-retrieval graph expansion depth per query"
|
|
3151
|
+
},
|
|
3152
|
+
brainMaxFanoutPerNode: {
|
|
3153
|
+
label: "Brain Max Fanout Per Node",
|
|
3154
|
+
help: "Maximum accepted traversals from a single source node expansion"
|
|
3155
|
+
},
|
|
3156
|
+
brainMaxFrontierSize: {
|
|
3157
|
+
label: "Brain Max Frontier Size",
|
|
3158
|
+
help: "Maximum traversal frontier size during learned retrieval"
|
|
3159
|
+
},
|
|
3160
|
+
brainMaxSeeds: {
|
|
3161
|
+
label: "Brain Max Seeds",
|
|
3162
|
+
help: "Maximum seed nodes admitted into learned retrieval before graph expansion"
|
|
3163
|
+
},
|
|
3164
|
+
brainSemanticThreshold: {
|
|
3165
|
+
label: "Brain Semantic Threshold",
|
|
3166
|
+
help: "Minimum semantic similarity required for seed admission during learned retrieval"
|
|
3167
|
+
},
|
|
3168
|
+
brainShadowMode: {
|
|
3169
|
+
label: "Brain Shadow Mode",
|
|
3170
|
+
help: "Run learned retrieval for telemetry only without injecting brain context into prompts"
|
|
3171
|
+
},
|
|
3172
|
+
brainWorkerMode: {
|
|
3173
|
+
label: "Worker Mode",
|
|
3174
|
+
help: "Run the learner in a supervised child process (default) or fall back to in-process mode"
|
|
3175
|
+
},
|
|
3176
|
+
brainWorkerHeartbeatTimeoutMs: {
|
|
3177
|
+
label: "Worker Heartbeat Timeout",
|
|
3178
|
+
help: "Milliseconds to wait before treating the supervised learner worker as stalled"
|
|
3179
|
+
},
|
|
3180
|
+
brainWorkerRestartDelayMs: {
|
|
3181
|
+
label: "Worker Restart Delay",
|
|
3182
|
+
help: "Milliseconds to wait before restarting the supervised learner worker after exit or crash"
|
|
3183
|
+
}
|
|
3184
|
+
},
|
|
3113
3185
|
configSchema: {
|
|
3114
3186
|
type: "object",
|
|
3115
3187
|
additionalProperties: false,
|
|
3116
|
-
properties: {
|
|
3188
|
+
properties: {
|
|
3189
|
+
brainEnabled: {
|
|
3190
|
+
type: "boolean"
|
|
3191
|
+
},
|
|
3192
|
+
brainRoot: {
|
|
3193
|
+
type: "string"
|
|
3194
|
+
},
|
|
3195
|
+
brainEmbeddingProvider: {
|
|
3196
|
+
type: "string"
|
|
3197
|
+
},
|
|
3198
|
+
brainEmbeddingModel: {
|
|
3199
|
+
type: "string"
|
|
3200
|
+
},
|
|
3201
|
+
brainEmbeddingBaseUrl: {
|
|
3202
|
+
type: "string"
|
|
3203
|
+
},
|
|
3204
|
+
brainMaxCompileMs: {
|
|
3205
|
+
type: "integer",
|
|
3206
|
+
minimum: 0
|
|
3207
|
+
},
|
|
3208
|
+
brainBudgetFraction: {
|
|
3209
|
+
type: "number",
|
|
3210
|
+
minimum: 0,
|
|
3211
|
+
maximum: 1
|
|
3212
|
+
},
|
|
3213
|
+
brainMaxHops: {
|
|
3214
|
+
type: "integer",
|
|
3215
|
+
minimum: 1
|
|
3216
|
+
},
|
|
3217
|
+
brainMaxFanoutPerNode: {
|
|
3218
|
+
type: "integer",
|
|
3219
|
+
minimum: 1
|
|
3220
|
+
},
|
|
3221
|
+
brainMaxFrontierSize: {
|
|
3222
|
+
type: "integer",
|
|
3223
|
+
minimum: 1
|
|
3224
|
+
},
|
|
3225
|
+
brainMaxSeeds: {
|
|
3226
|
+
type: "integer",
|
|
3227
|
+
minimum: 1
|
|
3228
|
+
},
|
|
3229
|
+
brainSemanticThreshold: {
|
|
3230
|
+
type: "number",
|
|
3231
|
+
minimum: 0,
|
|
3232
|
+
maximum: 1
|
|
3233
|
+
},
|
|
3234
|
+
brainShadowMode: {
|
|
3235
|
+
type: "boolean"
|
|
3236
|
+
},
|
|
3237
|
+
brainWorkerMode: {
|
|
3238
|
+
type: "string",
|
|
3239
|
+
enum: ["child", "in_process"]
|
|
3240
|
+
},
|
|
3241
|
+
brainWorkerHeartbeatTimeoutMs: {
|
|
3242
|
+
type: "integer",
|
|
3243
|
+
minimum: 1000
|
|
3244
|
+
},
|
|
3245
|
+
brainWorkerRestartDelayMs: {
|
|
3246
|
+
type: "integer",
|
|
3247
|
+
minimum: 0
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3117
3250
|
}
|
|
3118
3251
|
}, null, 2) + "\n";
|
|
3119
3252
|
}
|
|
@@ -4367,7 +4500,7 @@ function persistWatchTracedLearningBridgeSurface(input) {
|
|
|
4367
4500
|
}
|
|
4368
4501
|
}));
|
|
4369
4502
|
}
|
|
4370
|
-
function runLearnCommand(parsed) {
|
|
4503
|
+
async function runLearnCommand(parsed) {
|
|
4371
4504
|
const learnStatePath = path.join(parsed.activationRoot, "learn-cli-state.json");
|
|
4372
4505
|
const teacherSnapshotPath = resolveAsyncTeacherLiveLoopSnapshotPath(parsed.activationRoot);
|
|
4373
4506
|
function isLearnRuntimeStateLike(value) {
|
|
@@ -4580,6 +4713,7 @@ function runLearnCommand(parsed) {
|
|
|
4580
4713
|
return 0;
|
|
4581
4714
|
}
|
|
4582
4715
|
const learningExport = normalizedEventExport;
|
|
4716
|
+
const resolvedEmbedder = resolveCliEmbedderConfig(undefined, activationRoot);
|
|
4583
4717
|
const serveTimeLearning = resolveServeTimeLearningRuntimeInput(activationRoot);
|
|
4584
4718
|
const learnerResult = drainAlwaysOnLearningRuntime({
|
|
4585
4719
|
packLabel: "learn-cli",
|
|
@@ -4602,7 +4736,11 @@ function runLearnCommand(parsed) {
|
|
|
4602
4736
|
...(serveTimeLearning.baselineState !== undefined ? { baselineState: serveTimeLearning.baselineState } : {}),
|
|
4603
4737
|
activationRoot
|
|
4604
4738
|
});
|
|
4605
|
-
|
|
4739
|
+
let lastMaterialization = learnerResult.materializations.at(-1) ?? null;
|
|
4740
|
+
lastMaterialization = await reindexMaterializationCandidateWithEmbedder(lastMaterialization, resolvedEmbedder.embedder);
|
|
4741
|
+
if (lastMaterialization !== null && learnerResult.materializations.length > 0) {
|
|
4742
|
+
learnerResult.materializations[learnerResult.materializations.length - 1] = lastMaterialization;
|
|
4743
|
+
}
|
|
4606
4744
|
const plan = describeAlwaysOnLearningRuntimeState(learnerResult.state, lastMaterialization);
|
|
4607
4745
|
const learningPath = summarizeLearningPathFromMaterialization(lastMaterialization);
|
|
4608
4746
|
const graphEvolution = lastMaterialization?.candidate.payloads.graph.evolution;
|
|
@@ -4995,14 +5133,9 @@ async function applyWatchMaterialization(activationRoot, snapshot, lastHandledMa
|
|
|
4995
5133
|
failure: null
|
|
4996
5134
|
};
|
|
4997
5135
|
}
|
|
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
|
-
}
|
|
5136
|
+
materialization = await reindexMaterializationCandidateWithEmbedder(materialization, embedder);
|
|
5137
|
+
if (snapshot?.learner !== undefined && snapshot.learner !== null) {
|
|
5138
|
+
snapshot.learner.lastMaterialization = materialization;
|
|
5006
5139
|
}
|
|
5007
5140
|
const shortPackId = packId.length > 16 ? packId.slice(0, 16) : packId;
|
|
5008
5141
|
const observedAt = new Date().toISOString();
|
|
@@ -5183,7 +5316,7 @@ function resolveWatchTeacherLabelerConfig(input, activationRoot) {
|
|
|
5183
5316
|
warnings
|
|
5184
5317
|
};
|
|
5185
5318
|
}
|
|
5186
|
-
function
|
|
5319
|
+
function resolveCliEmbedderConfig(input, activationRoot) {
|
|
5187
5320
|
if (input !== undefined) {
|
|
5188
5321
|
return {
|
|
5189
5322
|
embedder: input,
|
|
@@ -5367,7 +5500,7 @@ export async function createWatchCommandRuntime(input) {
|
|
|
5367
5500
|
log(`Scan root: ${shortenPath(scanRoot)}`);
|
|
5368
5501
|
log(`State: cursor=${shortenPath(sessionTailCursorPath)} snapshot=${shortenPath(teacherSnapshotPath)}`);
|
|
5369
5502
|
const resolvedTeacherLabeler = resolveWatchTeacherLabelerConfig(input.teacherLabeler, activationRoot);
|
|
5370
|
-
const resolvedEmbedder =
|
|
5503
|
+
const resolvedEmbedder = resolveCliEmbedderConfig(input.embedder, activationRoot);
|
|
5371
5504
|
const teacherLabeler = resolvedTeacherLabeler.teacherLabeler;
|
|
5372
5505
|
for (const warning of resolvedTeacherLabeler.warnings) {
|
|
5373
5506
|
startupWarnings.push(`teacher_config_warning:${warning}`);
|
|
@@ -5988,7 +6121,12 @@ export function runOperatorCli(argv = process.argv.slice(2)) {
|
|
|
5988
6121
|
return runHistoryCommand(parsed);
|
|
5989
6122
|
}
|
|
5990
6123
|
if (parsed.command === "learn") {
|
|
5991
|
-
|
|
6124
|
+
runLearnCommand(parsed).then((code) => { process.exitCode = code; }, (error) => {
|
|
6125
|
+
console.error("[openclawbrain] learn failed");
|
|
6126
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
6127
|
+
process.exitCode = 1;
|
|
6128
|
+
});
|
|
6129
|
+
return 0;
|
|
5992
6130
|
}
|
|
5993
6131
|
if (parsed.command === "watch") {
|
|
5994
6132
|
// Watch is async — bridge to sync CLI entry by scheduling and returning 0.
|