@openclawbrain/cli 0.4.13 → 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/README.md +17 -11
- package/dist/extension/index.js +29 -3
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/runtime-guard.d.ts +8 -0
- package/dist/extension/runtime-guard.js +100 -12
- 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 +595 -113
- package/dist/src/index.d.ts +242 -3
- package/dist/src/index.js +1029 -38
- package/dist/src/install-converge.js +217 -0
- package/dist/src/learning-spine.d.ts +2 -1
- package/dist/src/learning-spine.js +49 -19
- package/dist/src/local-learner.d.ts +30 -0
- package/dist/src/local-learner.js +298 -179
- 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 +301 -42
- package/dist/src/runtime-core.js +658 -0
- package/dist/src/status-learning-path.js +32 -2
- package/dist/src/teacher-decision-match.js +277 -0
- package/dist/src/teacher-labeler.js +4 -30
- 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 +4 -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 [];
|
|
@@ -22,6 +23,48 @@ function softmax(values) {
|
|
|
22
23
|
}
|
|
23
24
|
return numerators.map((value) => roundNumber(value / denominator));
|
|
24
25
|
}
|
|
26
|
+
function isPlainObject(value) {
|
|
27
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const prototype = Object.getPrototypeOf(value);
|
|
31
|
+
return prototype === Object.prototype || prototype === null;
|
|
32
|
+
}
|
|
33
|
+
function compactStructuralSignalValue(value, depth = 0) {
|
|
34
|
+
if (value === null || typeof value === "boolean") {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "number") {
|
|
38
|
+
return Number.isFinite(value) ? roundNumber(value) : undefined;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "string") {
|
|
41
|
+
return value.length <= 256 ? value : `${value.slice(0, 256)}...`;
|
|
42
|
+
}
|
|
43
|
+
if (depth >= 3) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
const compacted = value
|
|
48
|
+
.slice(0, 32)
|
|
49
|
+
.map((entry) => compactStructuralSignalValue(entry, depth + 1))
|
|
50
|
+
.filter((entry) => entry !== undefined);
|
|
51
|
+
return compacted;
|
|
52
|
+
}
|
|
53
|
+
if (!isPlainObject(value)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const compactedEntries = Object.entries(value)
|
|
57
|
+
.slice(0, 32)
|
|
58
|
+
.flatMap(([key, entry]) => {
|
|
59
|
+
const compacted = compactStructuralSignalValue(entry, depth + 1);
|
|
60
|
+
return compacted === undefined ? [] : [[key, compacted]];
|
|
61
|
+
});
|
|
62
|
+
return compactedEntries.length > 0 ? Object.fromEntries(compactedEntries) : undefined;
|
|
63
|
+
}
|
|
64
|
+
function compactStructuralSignals(value) {
|
|
65
|
+
const compacted = compactStructuralSignalValue(value);
|
|
66
|
+
return compacted !== undefined && isPlainObject(compacted) ? compacted : null;
|
|
67
|
+
}
|
|
25
68
|
function isStableKernelContextBlock(block) {
|
|
26
69
|
if (block.id.includes(":event:") || block.id.includes(":teacher:")) {
|
|
27
70
|
return false;
|
|
@@ -247,7 +290,7 @@ export function appendServeTimeRouteDecisionLog(input) {
|
|
|
247
290
|
requestedBudget: {
|
|
248
291
|
modeRequested: input.turn.mode ?? "heuristic",
|
|
249
292
|
maxContextBlocks: effectiveMaxContextBlocks,
|
|
250
|
-
maxContextChars: null
|
|
293
|
+
maxContextChars: input.turn.maxContextChars ?? null
|
|
251
294
|
},
|
|
252
295
|
actualBudget: {
|
|
253
296
|
modeEffective: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.modeEffective : null,
|
|
@@ -255,28 +298,15 @@ export function appendServeTimeRouteDecisionLog(input) {
|
|
|
255
298
|
selectedCharCount: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.selectedCharCount : 0,
|
|
256
299
|
selectedTokenCount: input.compileResult.ok ? input.compileResult.compileResponse.diagnostics.selectedTokenCount : 0
|
|
257
300
|
},
|
|
258
|
-
candidateSetIds: ranked.map((entry) => entry.blockId),
|
|
301
|
+
candidateSetIds: ranked.slice(0, MAX_PERSISTED_ROUTE_DECISION_CANDIDATE_SET_IDS).map((entry) => entry.blockId),
|
|
259
302
|
chosenContextIds: input.compileResult.ok ? input.compileResult.compileResponse.selectedContext.map((block) => block.id) : [],
|
|
260
303
|
candidateScores: ranked.map((entry, index) => ({
|
|
261
304
|
blockId: entry.blockId,
|
|
262
|
-
source: entry.source,
|
|
263
305
|
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
306
|
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
|
|
307
|
+
actionProbability: probabilities[index] ?? 0
|
|
278
308
|
})),
|
|
279
|
-
structuralSignals: input.compileResult.ok ? input.compileResult.compileResponse.
|
|
309
|
+
structuralSignals: compactStructuralSignals(input.compileResult.ok ? input.compileResult.compileResponse.structuralSignals : null),
|
|
280
310
|
fallbackReason: serveFallbackReason(input.compileResult),
|
|
281
311
|
hotPathTiming: input.compileResult.timing,
|
|
282
312
|
kernelContextCount: selectedKernelContextIds.length,
|
|
@@ -458,4 +488,4 @@ export function appendLearningUpdateLogs(input) {
|
|
|
458
488
|
pgRouteUpdate
|
|
459
489
|
};
|
|
460
490
|
}
|
|
461
|
-
//# sourceMappingURL=learning-spine.js.map
|
|
491
|
+
//# sourceMappingURL=learning-spine.js.map
|
|
@@ -81,6 +81,35 @@ 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
|
+
accounting: {
|
|
89
|
+
exact: number;
|
|
90
|
+
heuristic: number;
|
|
91
|
+
unmatched: number;
|
|
92
|
+
ambiguous: number;
|
|
93
|
+
};
|
|
94
|
+
matched: {
|
|
95
|
+
exactDecisionId: number;
|
|
96
|
+
exactSelectionDigest: number;
|
|
97
|
+
turnCompileEventId: number;
|
|
98
|
+
legacyHeuristic: number;
|
|
99
|
+
};
|
|
100
|
+
unmatched: {
|
|
101
|
+
exactDecisionId: number;
|
|
102
|
+
exactSelectionDigest: number;
|
|
103
|
+
turnCompileEventId: number;
|
|
104
|
+
legacyHeuristic: number;
|
|
105
|
+
};
|
|
106
|
+
ambiguous: {
|
|
107
|
+
exactDecisionId: number;
|
|
108
|
+
exactSelectionDigest: number;
|
|
109
|
+
turnCompileEventId: number;
|
|
110
|
+
legacyHeuristic: number;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
84
113
|
export interface CandidatePackBuildResult {
|
|
85
114
|
manifest: ArtifactManifestV1;
|
|
86
115
|
payloads: CandidatePackPayloads;
|
|
@@ -91,6 +120,7 @@ export interface CandidatePackBuildResult {
|
|
|
91
120
|
decisionLogCount: number;
|
|
92
121
|
fallbackReason: string | null;
|
|
93
122
|
updatedBaseline: BaselineStateV1 | null;
|
|
123
|
+
observationBindingStats: TeacherObservationBindingStatsV1 | null;
|
|
94
124
|
};
|
|
95
125
|
summary: {
|
|
96
126
|
packId: string;
|