@openclawbrain/cli 0.4.12 → 0.4.14
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/README.md +17 -11
- package/dist/extension/index.js +1 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/runtime-guard.d.ts +2 -0
- package/dist/extension/runtime-guard.js +33 -1
- package/dist/extension/runtime-guard.js.map +1 -1
- package/dist/src/attachment-truth.d.ts +32 -22
- package/dist/src/attachment-truth.js +338 -186
- package/dist/src/cli.d.ts +13 -1
- package/dist/src/cli.js +439 -95
- package/dist/src/install-converge.js +217 -0
- package/dist/src/learning-spine.d.ts +2 -1
- package/dist/src/learning-spine.js +7 -19
- package/dist/src/local-learner.d.ts +25 -0
- package/dist/src/local-learner.js +224 -37
- package/dist/src/proof-command.js +105 -41
- package/dist/src/runtime-core.js +578 -0
- package/dist/src/teacher-decision-match.js +240 -0
- package/dist/src/teacher-labeler.js +4 -30
- package/extension/runtime-guard.ts +45 -0
- package/package.json +5 -3
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
const CHANGE_REASON_LABELS = {
|
|
2
|
+
install_identity: "plugin install identity changed",
|
|
3
|
+
install_layout: "authoritative install layout changed",
|
|
4
|
+
hook_path: "hook path changed",
|
|
5
|
+
hook_state: "hook install state changed",
|
|
6
|
+
loadability: "hook loadability changed",
|
|
7
|
+
activation_root: "pinned activation root changed",
|
|
8
|
+
loader_source: "loader source changed",
|
|
9
|
+
runtime_guard_source: "runtime-guard source changed",
|
|
10
|
+
plugins_config: "OpenClaw plugins config changed",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function sameValue(left, right) {
|
|
14
|
+
return left === right;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function installIdentityOf(fingerprint) {
|
|
18
|
+
if (fingerprint?.selectedInstall === null || fingerprint?.selectedInstall === undefined) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return JSON.stringify({
|
|
22
|
+
extensionDir: fingerprint.selectedInstall.extensionDir ?? null,
|
|
23
|
+
manifestId: fingerprint.selectedInstall.manifestId ?? null,
|
|
24
|
+
installId: fingerprint.selectedInstall.installId ?? null,
|
|
25
|
+
packageName: fingerprint.selectedInstall.packageName ?? null,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function planOpenClawBrainConvergePluginAction(fingerprint) {
|
|
30
|
+
const selectedInstall = fingerprint?.selectedInstall ?? null;
|
|
31
|
+
if (selectedInstall === null) {
|
|
32
|
+
return {
|
|
33
|
+
action: "install",
|
|
34
|
+
packageSpec: "@openclawbrain/openclaw",
|
|
35
|
+
pluginId: "openclawbrain",
|
|
36
|
+
reason: "no authoritative OpenClawBrain plugin install was discovered for this OpenClaw home",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (selectedInstall.installLayout !== "native_package_plugin") {
|
|
40
|
+
return {
|
|
41
|
+
action: "install",
|
|
42
|
+
packageSpec: "@openclawbrain/openclaw",
|
|
43
|
+
pluginId: "openclawbrain",
|
|
44
|
+
reason: "the authoritative install is still a generated shadow extension, so converge must install the split-package plugin lane",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
action: "update",
|
|
49
|
+
packageSpec: "@openclawbrain/openclaw",
|
|
50
|
+
pluginId: "openclawbrain",
|
|
51
|
+
reason: "refresh the existing split-package plugin install so install/upgrade/repair stays on one converge path",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function diffOpenClawBrainConvergeRuntimeFingerprint(before, after) {
|
|
56
|
+
const reasons = [];
|
|
57
|
+
if (!sameValue(installIdentityOf(before), installIdentityOf(after))) {
|
|
58
|
+
reasons.push("install_identity");
|
|
59
|
+
}
|
|
60
|
+
if (!sameValue(before?.installLayout ?? null, after?.installLayout ?? null)) {
|
|
61
|
+
reasons.push("install_layout");
|
|
62
|
+
}
|
|
63
|
+
if (!sameValue(before?.hookPath ?? null, after?.hookPath ?? null)) {
|
|
64
|
+
reasons.push("hook_path");
|
|
65
|
+
}
|
|
66
|
+
if (!sameValue(before?.hookState ?? null, after?.hookState ?? null)) {
|
|
67
|
+
reasons.push("hook_state");
|
|
68
|
+
}
|
|
69
|
+
if (!sameValue(before?.loadability ?? null, after?.loadability ?? null)) {
|
|
70
|
+
reasons.push("loadability");
|
|
71
|
+
}
|
|
72
|
+
if (!sameValue(before?.activationRoot ?? null, after?.activationRoot ?? null)) {
|
|
73
|
+
reasons.push("activation_root");
|
|
74
|
+
}
|
|
75
|
+
if (!sameValue(before?.loaderSource ?? null, after?.loaderSource ?? null)) {
|
|
76
|
+
reasons.push("loader_source");
|
|
77
|
+
}
|
|
78
|
+
if (!sameValue(before?.runtimeGuardSource ?? null, after?.runtimeGuardSource ?? null)) {
|
|
79
|
+
reasons.push("runtime_guard_source");
|
|
80
|
+
}
|
|
81
|
+
if (!sameValue(before?.pluginsConfig ?? null, after?.pluginsConfig ?? null)) {
|
|
82
|
+
reasons.push("plugins_config");
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
changed: reasons.length > 0,
|
|
86
|
+
reasons,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function describeOpenClawBrainConvergeChangeReasons(reasons) {
|
|
91
|
+
const normalized = reasons
|
|
92
|
+
.map((reason) => CHANGE_REASON_LABELS[reason] ?? reason)
|
|
93
|
+
.filter((reason, index, values) => values.indexOf(reason) === index);
|
|
94
|
+
return normalized.length === 0 ? "no runtime-affecting install changes detected" : normalized.join("; ");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function buildOpenClawBrainConvergeRestartPlan(input) {
|
|
98
|
+
const changeReasons = input.changeReasons ?? [];
|
|
99
|
+
if (changeReasons.length === 0) {
|
|
100
|
+
return {
|
|
101
|
+
required: false,
|
|
102
|
+
automatic: false,
|
|
103
|
+
reason: "no_runtime_affecting_changes",
|
|
104
|
+
detail: "Skipped gateway restart because converge did not change plugin files, hook wiring, or the pinned activation root.",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (input.profileName === null) {
|
|
108
|
+
return {
|
|
109
|
+
required: true,
|
|
110
|
+
automatic: false,
|
|
111
|
+
reason: "exact_profile_unresolved",
|
|
112
|
+
detail: "Restart is still required because converge changed runtime-affecting install state, but install could not infer an exact OpenClaw profile token for automatic restart.",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
required: true,
|
|
117
|
+
automatic: true,
|
|
118
|
+
reason: "runtime_affecting_changes_detected",
|
|
119
|
+
detail: `Restart is required because converge changed runtime-affecting install state for profile ${input.profileName}.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function classifyOpenClawBrainConvergeVerification(input) {
|
|
124
|
+
const warnings = [];
|
|
125
|
+
const blockingReasons = [];
|
|
126
|
+
const installLayout = input.installLayout ?? null;
|
|
127
|
+
const installState = input.installState ?? "unknown";
|
|
128
|
+
const loadability = input.loadability ?? "unverified";
|
|
129
|
+
const displayedStatus = input.displayedStatus ?? "unknown";
|
|
130
|
+
const runtimeLoad = input.runtimeLoad ?? "unverified";
|
|
131
|
+
const loadProof = input.loadProof ?? "unverified";
|
|
132
|
+
const serveState = input.serveState ?? "unknown";
|
|
133
|
+
if (installLayout !== "native_package_plugin") {
|
|
134
|
+
blockingReasons.push("split-package native package plugin is not authoritative after converge");
|
|
135
|
+
}
|
|
136
|
+
if (installState !== "installed") {
|
|
137
|
+
blockingReasons.push(`hook install state is ${installState}`);
|
|
138
|
+
}
|
|
139
|
+
if (loadability !== "loadable") {
|
|
140
|
+
blockingReasons.push(`hook loadability is ${loadability}`);
|
|
141
|
+
}
|
|
142
|
+
if (displayedStatus === "fail") {
|
|
143
|
+
blockingReasons.push("status still reports fail");
|
|
144
|
+
}
|
|
145
|
+
const runtimeTruthAlreadyProven = displayedStatus === "ok"
|
|
146
|
+
&& runtimeLoad === "proven"
|
|
147
|
+
&& loadProof === "status_probe_ready";
|
|
148
|
+
if (input.restartRequired === true && input.restartPerformed !== true && !runtimeTruthAlreadyProven) {
|
|
149
|
+
blockingReasons.push("restart is still required before runtime load can be trusted");
|
|
150
|
+
}
|
|
151
|
+
if (blockingReasons.length > 0) {
|
|
152
|
+
return {
|
|
153
|
+
state: "failed",
|
|
154
|
+
manualActionRequired: true,
|
|
155
|
+
blockingReasons,
|
|
156
|
+
warnings,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (input.restartRequired === true && input.restartPerformed !== true && runtimeTruthAlreadyProven) {
|
|
160
|
+
warnings.push("automatic restart was not performed because install could not infer an exact OpenClaw profile token, but current status already proves runtime load");
|
|
161
|
+
}
|
|
162
|
+
if (displayedStatus !== "ok") {
|
|
163
|
+
warnings.push(`status is ${displayedStatus}`);
|
|
164
|
+
}
|
|
165
|
+
if (runtimeLoad !== "proven") {
|
|
166
|
+
warnings.push(`runtime load is ${runtimeLoad}`);
|
|
167
|
+
}
|
|
168
|
+
if (loadProof !== "status_probe_ready") {
|
|
169
|
+
warnings.push(`loadProof is ${loadProof}`);
|
|
170
|
+
}
|
|
171
|
+
if (serveState !== "serving_active_pack") {
|
|
172
|
+
warnings.push(`serve state is ${serveState}`);
|
|
173
|
+
}
|
|
174
|
+
if (input.routeFnAvailable !== true) {
|
|
175
|
+
warnings.push("route_fn availability is not yet proven");
|
|
176
|
+
}
|
|
177
|
+
if (input.awaitingFirstExport === true) {
|
|
178
|
+
warnings.push("the attached profile has not emitted its first export yet");
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
state: warnings.length > 0 ? "warning" : "healthy",
|
|
182
|
+
manualActionRequired: false,
|
|
183
|
+
blockingReasons,
|
|
184
|
+
warnings,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function finalizeOpenClawBrainConvergeResult(input) {
|
|
189
|
+
const warnings = [...(input.warnings ?? []), ...(input.verification?.warnings ?? [])];
|
|
190
|
+
const uniqueWarnings = warnings.filter((warning, index, values) => values.indexOf(warning) === index);
|
|
191
|
+
if (input.stepFailure !== null && input.stepFailure !== undefined) {
|
|
192
|
+
return {
|
|
193
|
+
verdict: "failed",
|
|
194
|
+
why: input.stepFailure,
|
|
195
|
+
warnings: uniqueWarnings,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (input.verification?.state === "failed") {
|
|
199
|
+
return {
|
|
200
|
+
verdict: input.verification.manualActionRequired ? "manual_action_required" : "failed",
|
|
201
|
+
why: input.verification.blockingReasons.join("; "),
|
|
202
|
+
warnings: uniqueWarnings,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (input.verification?.state === "warning") {
|
|
206
|
+
return {
|
|
207
|
+
verdict: "converged_with_warnings",
|
|
208
|
+
why: input.verification.warnings.join("; "),
|
|
209
|
+
warnings: uniqueWarnings,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
verdict: "converged",
|
|
214
|
+
why: "plugin install, hook wiring, restart behavior, and status verification converged on the expected split-package state",
|
|
215
|
+
warnings: uniqueWarnings,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AlwaysOnLearningMaterializationJobV1 } from "
|
|
1
|
+
import { type AlwaysOnLearningMaterializationJobV1 } from "@openclawbrain/learner";
|
|
2
2
|
import { type BrainServeHotPathTimingV1, type NormalizedEventExportV1, type RouteMode, type RuntimeCompileResponseV1 } from "@openclawbrain/contracts";
|
|
3
3
|
import { type LearningSpinePgRouteUpdateLogEntryV1, type LearningSpineServeRouteBreadcrumbsV1, type LearningSpineServeRouteDecisionLogEntryV1, type PackDescriptor } from "@openclawbrain/pack-format";
|
|
4
4
|
type CompileFailureLike = {
|
|
@@ -22,6 +22,7 @@ interface RuntimeTurnLike {
|
|
|
22
22
|
createdAt?: string | null;
|
|
23
23
|
sequenceStart?: number | null;
|
|
24
24
|
maxContextBlocks?: number;
|
|
25
|
+
maxContextChars?: number;
|
|
25
26
|
budgetStrategy?: "fixed_v1" | "empirical_v1";
|
|
26
27
|
mode?: RouteMode;
|
|
27
28
|
runtimeHints?: readonly string[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { rankContextBlocks } from "@openclawbrain/compiler";
|
|
3
|
-
import { describeSparseFeedbackEventDispositions } from "
|
|
3
|
+
import { describeSparseFeedbackEventDispositions } from "@openclawbrain/learner";
|
|
4
4
|
import { CONTRACT_IDS } from "@openclawbrain/contracts";
|
|
5
5
|
import { appendLearningSpineLogEntry, buildLearningSpineLogId, loadActivationPointers, loadPack, loadPackFromActivation, readLearningSpineLogEntries } from "@openclawbrain/pack-format";
|
|
6
6
|
function noteValue(notes, prefix) {
|
|
@@ -10,6 +10,7 @@ function noteValue(notes, prefix) {
|
|
|
10
10
|
function roundNumber(value) {
|
|
11
11
|
return Math.round(value * 10_000) / 10_000;
|
|
12
12
|
}
|
|
13
|
+
const MAX_PERSISTED_ROUTE_DECISION_CANDIDATE_SET_IDS = 256;
|
|
13
14
|
function softmax(values) {
|
|
14
15
|
if (values.length === 0) {
|
|
15
16
|
return [];
|
|
@@ -247,7 +248,7 @@ export function appendServeTimeRouteDecisionLog(input) {
|
|
|
247
248
|
requestedBudget: {
|
|
248
249
|
modeRequested: input.turn.mode ?? "heuristic",
|
|
249
250
|
maxContextBlocks: effectiveMaxContextBlocks,
|
|
250
|
-
maxContextChars: null
|
|
251
|
+
maxContextChars: input.turn.maxContextChars ?? null
|
|
251
252
|
},
|
|
252
253
|
actualBudget: {
|
|
253
254
|
modeEffective: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.modeEffective : null,
|
|
@@ -255,28 +256,15 @@ export function appendServeTimeRouteDecisionLog(input) {
|
|
|
255
256
|
selectedCharCount: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.selectedCharCount : 0,
|
|
256
257
|
selectedTokenCount: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.selectedTokenCount : 0
|
|
257
258
|
},
|
|
258
|
-
candidateSetIds: ranked.map((entry) => entry.blockId),
|
|
259
|
+
candidateSetIds: ranked.slice(0, MAX_PERSISTED_ROUTE_DECISION_CANDIDATE_SET_IDS).map((entry) => entry.blockId),
|
|
259
260
|
chosenContextIds: input.compileResult.ok ? input.compileResult.compileResponse.selectedContext.map((block) => block.id) : [],
|
|
260
261
|
candidateScores: ranked.map((entry, index) => ({
|
|
261
262
|
blockId: entry.blockId,
|
|
262
|
-
source: entry.source,
|
|
263
263
|
selected: selectedIds.has(entry.blockId),
|
|
264
|
-
compactedFrom: entry.compactedFrom ?? [],
|
|
265
|
-
matchedTokens: [...entry.matchedTokens],
|
|
266
|
-
routingChannels: [...entry.routingChannels],
|
|
267
|
-
channelScores: {
|
|
268
|
-
graph: roundNumber(entry.channelScores.graph),
|
|
269
|
-
shortTerm: roundNumber(entry.channelScores.shortTerm),
|
|
270
|
-
vector: roundNumber(entry.channelScores.vector)
|
|
271
|
-
},
|
|
272
|
-
routeFnScore: roundNumber(entry.score),
|
|
273
264
|
actionScore: roundNumber(entry.score),
|
|
274
|
-
actionProbability: probabilities[index] ?? 0
|
|
275
|
-
actionLogProbability: probabilities[index] === undefined || probabilities[index] <= 0 ? null : roundNumber(Math.log(probabilities[index])),
|
|
276
|
-
traversalScore: roundNumber(entry.traversalScore ?? 0),
|
|
277
|
-
priority: entry.priority
|
|
265
|
+
actionProbability: probabilities[index] ?? 0
|
|
278
266
|
})),
|
|
279
|
-
structuralSignals:
|
|
267
|
+
structuralSignals: null,
|
|
280
268
|
fallbackReason: serveFallbackReason(input.compileResult),
|
|
281
269
|
hotPathTiming: input.compileResult.timing,
|
|
282
270
|
kernelContextCount: selectedKernelContextIds.length,
|
|
@@ -458,4 +446,4 @@ export function appendLearningUpdateLogs(input) {
|
|
|
458
446
|
pgRouteUpdate
|
|
459
447
|
};
|
|
460
448
|
}
|
|
461
|
-
//# sourceMappingURL=learning-spine.js.map
|
|
449
|
+
//# sourceMappingURL=learning-spine.js.map
|
|
@@ -81,6 +81,29 @@ export interface CandidatePackPayloads {
|
|
|
81
81
|
vectors: PackVectorsPayloadV1;
|
|
82
82
|
router: RouterArtifactV1 | null;
|
|
83
83
|
}
|
|
84
|
+
export interface TeacherObservationBindingStatsV1 {
|
|
85
|
+
totalObservationCount: number;
|
|
86
|
+
nonZeroObservationCount: number;
|
|
87
|
+
skippedZeroRewardCount: number;
|
|
88
|
+
matched: {
|
|
89
|
+
exactDecisionId: number;
|
|
90
|
+
exactSelectionDigest: number;
|
|
91
|
+
turnCompileEventId: number;
|
|
92
|
+
legacyHeuristic: number;
|
|
93
|
+
};
|
|
94
|
+
unmatched: {
|
|
95
|
+
exactDecisionId: number;
|
|
96
|
+
exactSelectionDigest: number;
|
|
97
|
+
turnCompileEventId: number;
|
|
98
|
+
legacyHeuristic: number;
|
|
99
|
+
};
|
|
100
|
+
ambiguous: {
|
|
101
|
+
exactDecisionId: number;
|
|
102
|
+
exactSelectionDigest: number;
|
|
103
|
+
turnCompileEventId: number;
|
|
104
|
+
legacyHeuristic: number;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
84
107
|
export interface CandidatePackBuildResult {
|
|
85
108
|
manifest: ArtifactManifestV1;
|
|
86
109
|
payloads: CandidatePackPayloads;
|
|
@@ -91,6 +114,7 @@ export interface CandidatePackBuildResult {
|
|
|
91
114
|
decisionLogCount: number;
|
|
92
115
|
fallbackReason: string | null;
|
|
93
116
|
updatedBaseline: BaselineStateV1 | null;
|
|
117
|
+
observationBindingStats: TeacherObservationBindingStatsV1 | null;
|
|
94
118
|
};
|
|
95
119
|
summary: {
|
|
96
120
|
packId: string;
|
|
@@ -403,6 +427,7 @@ export declare function buildGraphLocalActionSet(nodeBlockId: string, neighborBl
|
|
|
403
427
|
queryVector: Record<string, number>;
|
|
404
428
|
}, tau: number, stopBias?: number): GraphLocalActionSet;
|
|
405
429
|
export declare const STOP_ACTION_ID = "__STOP__";
|
|
430
|
+
export declare function buildStopActionUpdateBlockId(nodeBlockId: string): string;
|
|
406
431
|
export declare function createDefaultBaselineState(alpha?: number): BaselineStateV1;
|
|
407
432
|
/**
|
|
408
433
|
* EMA update: on first observation use the raw outcome; thereafter blend.
|