@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.
@@ -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 "./local-learner.js";
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 "./local-learner.js";
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: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.structuralSignals : null,
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.