@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
|
@@ -3670,7 +3670,11 @@ export function buildGraphLocalActionSet(nodeBlockId, neighborBlockIds, graph, v
|
|
|
3670
3670
|
// TrajectoryStepV1, TrajectoryV1 defined below in PG5 section
|
|
3671
3671
|
export const STOP_ACTION_ID = "__STOP__";
|
|
3672
3672
|
const STOP_ACTION = STOP_ACTION_ID;
|
|
3673
|
+
const STOP_ACTION_UPDATE_SEPARATOR = "::";
|
|
3673
3674
|
const DEFAULT_STOP_BIAS = 0.0;
|
|
3675
|
+
export function buildStopActionUpdateBlockId(nodeBlockId) {
|
|
3676
|
+
return `${nodeBlockId}${STOP_ACTION_UPDATE_SEPARATOR}${STOP_ACTION_ID}`;
|
|
3677
|
+
}
|
|
3674
3678
|
const DEFAULT_TAU = 1.0;
|
|
3675
3679
|
const DEFAULT_BASELINE_ALPHA = 0.05;
|
|
3676
3680
|
const MAX_TRAJECTORY_LENGTH = 20;
|
|
@@ -3741,15 +3745,12 @@ export function computeTrajectoryPolicyGradient(trajectory, adjacency, graph, ve
|
|
|
3741
3745
|
grad = (1 / tau) * (-prob);
|
|
3742
3746
|
}
|
|
3743
3747
|
}
|
|
3744
|
-
|
|
3748
|
+
const gradientKey = j === STOP_ACTION_ID ? buildStopActionUpdateBlockId(step.nodeBlockId) : j;
|
|
3749
|
+
tailGradient.set(gradientKey, (tailGradient.get(gradientKey) ?? 0) + grad);
|
|
3745
3750
|
}
|
|
3746
3751
|
}
|
|
3747
3752
|
// Weight by advantage and pgScale, accumulate into updates
|
|
3748
3753
|
for (const [blockId, grad] of tailGradient) {
|
|
3749
|
-
// Skip STOP action — it has no learnable weight
|
|
3750
|
-
if (blockId === STOP_ACTION_ID) {
|
|
3751
|
-
continue;
|
|
3752
|
-
}
|
|
3753
3754
|
const scaledDelta = advantage * grad * pgScale;
|
|
3754
3755
|
const current = updates.get(blockId);
|
|
3755
3756
|
if (current === undefined) {
|
|
@@ -4053,15 +4054,36 @@ function normalizeOutcomeMatchText(value) {
|
|
|
4053
4054
|
? value.replace(/\s+/g, " ").trim().toLowerCase()
|
|
4054
4055
|
: null;
|
|
4055
4056
|
}
|
|
4057
|
+
function normalizeObservationOptionalString(value) {
|
|
4058
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
4059
|
+
}
|
|
4060
|
+
function buildObservationSelectionDigestKey(selectionDigest, activePackGraphChecksum) {
|
|
4061
|
+
const normalizedSelectionDigest = normalizeObservationOptionalString(selectionDigest);
|
|
4062
|
+
const normalizedGraphChecksum = normalizeObservationOptionalString(activePackGraphChecksum);
|
|
4063
|
+
if (normalizedSelectionDigest === null || normalizedGraphChecksum === null) {
|
|
4064
|
+
return null;
|
|
4065
|
+
}
|
|
4066
|
+
return `${normalizedGraphChecksum}|${normalizedSelectionDigest}`;
|
|
4067
|
+
}
|
|
4056
4068
|
function parseObservationRouteMetadata(value) {
|
|
4057
4069
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
4058
4070
|
return {
|
|
4071
|
+
serveDecisionRecordId: null,
|
|
4072
|
+
selectionDigest: null,
|
|
4073
|
+
turnCompileEventId: null,
|
|
4074
|
+
activePackId: null,
|
|
4075
|
+
activePackGraphChecksum: null,
|
|
4059
4076
|
selectedNodeIds: [],
|
|
4060
4077
|
selectedPathNodeIds: []
|
|
4061
4078
|
};
|
|
4062
4079
|
}
|
|
4063
4080
|
try {
|
|
4064
4081
|
const parsed = JSON.parse(value);
|
|
4082
|
+
const serveDecisionRecordId = normalizeObservationOptionalString(parsed?.serveDecisionRecordId);
|
|
4083
|
+
const selectionDigest = normalizeObservationOptionalString(parsed?.selectionDigest);
|
|
4084
|
+
const turnCompileEventId = normalizeObservationOptionalString(parsed?.turnCompileEventId);
|
|
4085
|
+
const activePackId = normalizeObservationOptionalString(parsed?.activePackId);
|
|
4086
|
+
const activePackGraphChecksum = normalizeObservationOptionalString(parsed?.activePackGraphChecksum);
|
|
4065
4087
|
const selectedNodeIds = Array.isArray(parsed?.selectedNodeIds)
|
|
4066
4088
|
? parsed.selectedNodeIds.filter((entry) => typeof entry === "string")
|
|
4067
4089
|
: [];
|
|
@@ -4069,12 +4091,22 @@ function parseObservationRouteMetadata(value) {
|
|
|
4069
4091
|
? parsed.selectedPathNodeIds.filter((entry) => typeof entry === "string")
|
|
4070
4092
|
: [];
|
|
4071
4093
|
return {
|
|
4094
|
+
serveDecisionRecordId,
|
|
4095
|
+
selectionDigest,
|
|
4096
|
+
turnCompileEventId,
|
|
4097
|
+
activePackId,
|
|
4098
|
+
activePackGraphChecksum,
|
|
4072
4099
|
selectedNodeIds,
|
|
4073
4100
|
selectedPathNodeIds
|
|
4074
4101
|
};
|
|
4075
4102
|
}
|
|
4076
4103
|
catch {
|
|
4077
4104
|
return {
|
|
4105
|
+
serveDecisionRecordId: null,
|
|
4106
|
+
selectionDigest: null,
|
|
4107
|
+
turnCompileEventId: null,
|
|
4108
|
+
activePackId: null,
|
|
4109
|
+
activePackGraphChecksum: null,
|
|
4078
4110
|
selectedNodeIds: [],
|
|
4079
4111
|
selectedPathNodeIds: []
|
|
4080
4112
|
};
|
|
@@ -4098,8 +4130,24 @@ function loadTeacherObservationOutcomesFromActivation(activationRoot) {
|
|
|
4098
4130
|
if (table === undefined) {
|
|
4099
4131
|
return [];
|
|
4100
4132
|
}
|
|
4133
|
+
const columns = new Set(db.prepare("PRAGMA table_info(brain_observations)").all().map((column) => String(column.name ?? "")));
|
|
4134
|
+
const selectableColumns = [
|
|
4135
|
+
"id",
|
|
4136
|
+
"query_text",
|
|
4137
|
+
"route_metadata_json",
|
|
4138
|
+
"final_score",
|
|
4139
|
+
"confidence",
|
|
4140
|
+
"created_at",
|
|
4141
|
+
"updated_at",
|
|
4142
|
+
"evaluated_at",
|
|
4143
|
+
columns.has("serve_decision_record_id") ? "serve_decision_record_id" : "NULL AS serve_decision_record_id",
|
|
4144
|
+
columns.has("selection_digest") ? "selection_digest" : "NULL AS selection_digest",
|
|
4145
|
+
columns.has("turn_compile_event_id") ? "turn_compile_event_id" : "NULL AS turn_compile_event_id",
|
|
4146
|
+
columns.has("active_pack_id") ? "active_pack_id" : "NULL AS active_pack_id",
|
|
4147
|
+
columns.has("active_pack_graph_checksum") ? "active_pack_graph_checksum" : "NULL AS active_pack_graph_checksum"
|
|
4148
|
+
];
|
|
4101
4149
|
const rows = db.prepare(`
|
|
4102
|
-
SELECT
|
|
4150
|
+
SELECT ${selectableColumns.join(", ")}
|
|
4103
4151
|
FROM brain_observations
|
|
4104
4152
|
WHERE final_score IS NOT NULL
|
|
4105
4153
|
ORDER BY created_at DESC
|
|
@@ -4115,6 +4163,11 @@ function loadTeacherObservationOutcomesFromActivation(activationRoot) {
|
|
|
4115
4163
|
createdAt: Number.isFinite(Number(row.created_at)) ? Number(row.created_at) : null,
|
|
4116
4164
|
updatedAt: Number.isFinite(Number(row.updated_at)) ? Number(row.updated_at) : null,
|
|
4117
4165
|
evaluatedAt: Number.isFinite(Number(row.evaluated_at)) ? Number(row.evaluated_at) : null,
|
|
4166
|
+
serveDecisionRecordId: normalizeObservationOptionalString(row.serve_decision_record_id) ?? routeMetadata.serveDecisionRecordId,
|
|
4167
|
+
selectionDigest: normalizeObservationOptionalString(row.selection_digest) ?? routeMetadata.selectionDigest,
|
|
4168
|
+
turnCompileEventId: normalizeObservationOptionalString(row.turn_compile_event_id) ?? routeMetadata.turnCompileEventId,
|
|
4169
|
+
activePackId: normalizeObservationOptionalString(row.active_pack_id) ?? routeMetadata.activePackId,
|
|
4170
|
+
activePackGraphChecksum: normalizeObservationOptionalString(row.active_pack_graph_checksum) ?? routeMetadata.activePackGraphChecksum,
|
|
4118
4171
|
selectedNodeIds: routeMetadata.selectedNodeIds,
|
|
4119
4172
|
selectedPathNodeIds: routeMetadata.selectedPathNodeIds
|
|
4120
4173
|
};
|
|
@@ -4196,45 +4249,172 @@ function compareDecisionObservationMatch(left, right) {
|
|
|
4196
4249
|
}
|
|
4197
4250
|
return 0;
|
|
4198
4251
|
}
|
|
4252
|
+
function emptyTeacherObservationBindingStats() {
|
|
4253
|
+
return {
|
|
4254
|
+
totalObservationCount: 0,
|
|
4255
|
+
nonZeroObservationCount: 0,
|
|
4256
|
+
skippedZeroRewardCount: 0,
|
|
4257
|
+
matched: {
|
|
4258
|
+
exactDecisionId: 0,
|
|
4259
|
+
exactSelectionDigest: 0,
|
|
4260
|
+
turnCompileEventId: 0,
|
|
4261
|
+
legacyHeuristic: 0
|
|
4262
|
+
},
|
|
4263
|
+
unmatched: {
|
|
4264
|
+
exactDecisionId: 0,
|
|
4265
|
+
exactSelectionDigest: 0,
|
|
4266
|
+
turnCompileEventId: 0,
|
|
4267
|
+
legacyHeuristic: 0
|
|
4268
|
+
},
|
|
4269
|
+
ambiguous: {
|
|
4270
|
+
exactDecisionId: 0,
|
|
4271
|
+
exactSelectionDigest: 0,
|
|
4272
|
+
turnCompileEventId: 0,
|
|
4273
|
+
legacyHeuristic: 0
|
|
4274
|
+
}
|
|
4275
|
+
};
|
|
4276
|
+
}
|
|
4277
|
+
function summarizeTeacherObservationBindingStats(stats) {
|
|
4278
|
+
return [
|
|
4279
|
+
`matched(exact_decision_id=${stats.matched.exactDecisionId},exact_selection_digest=${stats.matched.exactSelectionDigest},turn_compile_event_id=${stats.matched.turnCompileEventId},legacy_heuristic=${stats.matched.legacyHeuristic})`,
|
|
4280
|
+
`unmatched(exact_decision_id=${stats.unmatched.exactDecisionId},exact_selection_digest=${stats.unmatched.exactSelectionDigest},turn_compile_event_id=${stats.unmatched.turnCompileEventId},legacy_heuristic=${stats.unmatched.legacyHeuristic})`,
|
|
4281
|
+
`ambiguous(exact_decision_id=${stats.ambiguous.exactDecisionId},exact_selection_digest=${stats.ambiguous.exactSelectionDigest},turn_compile_event_id=${stats.ambiguous.turnCompileEventId},legacy_heuristic=${stats.ambiguous.legacyHeuristic})`,
|
|
4282
|
+
`non_zero=${stats.nonZeroObservationCount}`,
|
|
4283
|
+
`skipped_zero=${stats.skippedZeroRewardCount}`
|
|
4284
|
+
].join(" ");
|
|
4285
|
+
}
|
|
4286
|
+
function buildTeacherObservationDecisionIndexes(decisions) {
|
|
4287
|
+
const byRecordId = new Map();
|
|
4288
|
+
const bySelectionDigest = new Map();
|
|
4289
|
+
const ambiguousSelectionDigests = new Set();
|
|
4290
|
+
const byTurnCompileEventId = new Map();
|
|
4291
|
+
const ambiguousTurnCompileEventIds = new Set();
|
|
4292
|
+
for (const decision of decisions) {
|
|
4293
|
+
byRecordId.set(decision.recordId, decision);
|
|
4294
|
+
const selectionDigestKey = buildObservationSelectionDigestKey(decision.selectionDigest, decision.activePackGraphChecksum);
|
|
4295
|
+
if (selectionDigestKey !== null) {
|
|
4296
|
+
if (bySelectionDigest.has(selectionDigestKey)) {
|
|
4297
|
+
bySelectionDigest.delete(selectionDigestKey);
|
|
4298
|
+
ambiguousSelectionDigests.add(selectionDigestKey);
|
|
4299
|
+
}
|
|
4300
|
+
else if (!ambiguousSelectionDigests.has(selectionDigestKey)) {
|
|
4301
|
+
bySelectionDigest.set(selectionDigestKey, decision);
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
const turnCompileEventId = normalizeObservationOptionalString(decision.turnCompileEventId);
|
|
4305
|
+
if (turnCompileEventId !== null) {
|
|
4306
|
+
if (byTurnCompileEventId.has(turnCompileEventId)) {
|
|
4307
|
+
byTurnCompileEventId.delete(turnCompileEventId);
|
|
4308
|
+
ambiguousTurnCompileEventIds.add(turnCompileEventId);
|
|
4309
|
+
}
|
|
4310
|
+
else if (!ambiguousTurnCompileEventIds.has(turnCompileEventId)) {
|
|
4311
|
+
byTurnCompileEventId.set(turnCompileEventId, decision);
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
return {
|
|
4316
|
+
byRecordId,
|
|
4317
|
+
bySelectionDigest,
|
|
4318
|
+
ambiguousSelectionDigests,
|
|
4319
|
+
byTurnCompileEventId,
|
|
4320
|
+
ambiguousTurnCompileEventIds
|
|
4321
|
+
};
|
|
4322
|
+
}
|
|
4323
|
+
function resolveLegacyTeacherObservationMatch(decisions, observation) {
|
|
4324
|
+
const matches = decisions
|
|
4325
|
+
.map((decision) => {
|
|
4326
|
+
const key = decisionObservationMatchKey(decision, observation);
|
|
4327
|
+
return key === null
|
|
4328
|
+
? null
|
|
4329
|
+
: {
|
|
4330
|
+
decision,
|
|
4331
|
+
key
|
|
4332
|
+
};
|
|
4333
|
+
})
|
|
4334
|
+
.filter((entry) => entry !== null)
|
|
4335
|
+
.sort((left, right) => compareDecisionObservationMatch(left.key, right.key));
|
|
4336
|
+
const best = matches[0] ?? null;
|
|
4337
|
+
const runnerUp = matches[1] ?? null;
|
|
4338
|
+
if (best === null) {
|
|
4339
|
+
return { kind: "unmatched", mode: "legacyHeuristic" };
|
|
4340
|
+
}
|
|
4341
|
+
if (runnerUp !== null && compareDecisionObservationMatch(best.key, runnerUp.key) === 0) {
|
|
4342
|
+
return { kind: "ambiguous", mode: "legacyHeuristic" };
|
|
4343
|
+
}
|
|
4344
|
+
return {
|
|
4345
|
+
kind: "matched",
|
|
4346
|
+
mode: "legacyHeuristic",
|
|
4347
|
+
decision: best.decision
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4350
|
+
function resolveTeacherObservationDecisionMatch(decisions, observation, indexes) {
|
|
4351
|
+
const serveDecisionRecordId = normalizeObservationOptionalString(observation.serveDecisionRecordId);
|
|
4352
|
+
if (serveDecisionRecordId !== null) {
|
|
4353
|
+
const decision = indexes.byRecordId.get(serveDecisionRecordId) ?? null;
|
|
4354
|
+
return decision === null
|
|
4355
|
+
? { kind: "unmatched", mode: "exactDecisionId" }
|
|
4356
|
+
: { kind: "matched", mode: "exactDecisionId", decision };
|
|
4357
|
+
}
|
|
4358
|
+
const selectionDigestKey = buildObservationSelectionDigestKey(observation.selectionDigest, observation.activePackGraphChecksum);
|
|
4359
|
+
if (selectionDigestKey !== null) {
|
|
4360
|
+
if (indexes.ambiguousSelectionDigests.has(selectionDigestKey)) {
|
|
4361
|
+
return { kind: "ambiguous", mode: "exactSelectionDigest" };
|
|
4362
|
+
}
|
|
4363
|
+
const decision = indexes.bySelectionDigest.get(selectionDigestKey) ?? null;
|
|
4364
|
+
return decision === null
|
|
4365
|
+
? { kind: "unmatched", mode: "exactSelectionDigest" }
|
|
4366
|
+
: { kind: "matched", mode: "exactSelectionDigest", decision };
|
|
4367
|
+
}
|
|
4368
|
+
if (normalizeObservationOptionalString(observation.selectionDigest) !== null
|
|
4369
|
+
|| normalizeObservationOptionalString(observation.activePackGraphChecksum) !== null) {
|
|
4370
|
+
return { kind: "unmatched", mode: "exactSelectionDigest" };
|
|
4371
|
+
}
|
|
4372
|
+
const turnCompileEventId = normalizeObservationOptionalString(observation.turnCompileEventId);
|
|
4373
|
+
if (turnCompileEventId !== null) {
|
|
4374
|
+
if (indexes.ambiguousTurnCompileEventIds.has(turnCompileEventId)) {
|
|
4375
|
+
return { kind: "ambiguous", mode: "turnCompileEventId" };
|
|
4376
|
+
}
|
|
4377
|
+
const decision = indexes.byTurnCompileEventId.get(turnCompileEventId) ?? null;
|
|
4378
|
+
return decision === null
|
|
4379
|
+
? { kind: "unmatched", mode: "turnCompileEventId" }
|
|
4380
|
+
: { kind: "matched", mode: "turnCompileEventId", decision };
|
|
4381
|
+
}
|
|
4382
|
+
return resolveLegacyTeacherObservationMatch(decisions, observation);
|
|
4383
|
+
}
|
|
4199
4384
|
function joinDecisionsWithTeacherObservationOutcomes(decisions, observationOutcomes) {
|
|
4200
4385
|
const outcomes = new Map();
|
|
4386
|
+
const stats = emptyTeacherObservationBindingStats();
|
|
4201
4387
|
for (const decision of decisions) {
|
|
4202
4388
|
outcomes.set(decision.recordId, 0);
|
|
4203
4389
|
}
|
|
4204
4390
|
if (!Array.isArray(observationOutcomes) || observationOutcomes.length === 0) {
|
|
4205
|
-
return outcomes;
|
|
4391
|
+
return { outcomes, stats };
|
|
4206
4392
|
}
|
|
4393
|
+
const indexes = buildTeacherObservationDecisionIndexes(decisions);
|
|
4394
|
+
stats.totalObservationCount = observationOutcomes.length;
|
|
4207
4395
|
for (const observation of observationOutcomes) {
|
|
4208
|
-
const
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
return key === null
|
|
4212
|
-
? null
|
|
4213
|
-
: {
|
|
4214
|
-
decision,
|
|
4215
|
-
key
|
|
4216
|
-
};
|
|
4217
|
-
})
|
|
4218
|
-
.filter((entry) => entry !== null)
|
|
4219
|
-
.sort((left, right) => compareDecisionObservationMatch(left.key, right.key));
|
|
4220
|
-
const best = matches[0] ?? null;
|
|
4221
|
-
const runnerUp = matches[1] ?? null;
|
|
4222
|
-
if (best === null) {
|
|
4396
|
+
const reward = clampTeacherObservationOutcome(observation.finalScore);
|
|
4397
|
+
if (reward === 0) {
|
|
4398
|
+
stats.skippedZeroRewardCount += 1;
|
|
4223
4399
|
continue;
|
|
4224
4400
|
}
|
|
4225
|
-
|
|
4401
|
+
stats.nonZeroObservationCount += 1;
|
|
4402
|
+
const match = resolveTeacherObservationDecisionMatch(decisions, observation, indexes);
|
|
4403
|
+
if (match.kind === "unmatched") {
|
|
4404
|
+
stats.unmatched[match.mode] += 1;
|
|
4226
4405
|
continue;
|
|
4227
4406
|
}
|
|
4228
|
-
|
|
4229
|
-
|
|
4407
|
+
if (match.kind === "ambiguous") {
|
|
4408
|
+
stats.ambiguous[match.mode] += 1;
|
|
4230
4409
|
continue;
|
|
4231
4410
|
}
|
|
4232
|
-
|
|
4411
|
+
stats.matched[match.mode] += 1;
|
|
4412
|
+
const current = outcomes.get(match.decision.recordId) ?? 0;
|
|
4233
4413
|
if (current === 0 || Math.abs(reward) > Math.abs(current)) {
|
|
4234
|
-
outcomes.set(
|
|
4414
|
+
outcomes.set(match.decision.recordId, reward);
|
|
4235
4415
|
}
|
|
4236
4416
|
}
|
|
4237
|
-
return outcomes;
|
|
4417
|
+
return { outcomes, stats };
|
|
4238
4418
|
}
|
|
4239
4419
|
|
|
4240
4420
|
export function joinDecisionsWithFeedback(decisions, eventExport, maxDelayMs = 300_000) {
|
|
@@ -4587,13 +4767,12 @@ function computeTrajectoryPolicyGradientV2(trajectory, adjacency, graph, vectors
|
|
|
4587
4767
|
const grad = neighborId === actionKey
|
|
4588
4768
|
? (1 / tau) * (1 - prob)
|
|
4589
4769
|
: (1 / tau) * (-prob);
|
|
4590
|
-
|
|
4770
|
+
const gradientKey = neighborId === STOP_ACTION ? buildStopActionUpdateBlockId(step.nodeBlockId) : neighborId;
|
|
4771
|
+
tailGradient.set(gradientKey, (tailGradient.get(gradientKey) ?? 0) + grad);
|
|
4591
4772
|
}
|
|
4592
4773
|
}
|
|
4593
4774
|
// Weight by advantage and pgScale
|
|
4594
4775
|
for (const [blockId, grad] of tailGradient) {
|
|
4595
|
-
if (blockId === STOP_ACTION)
|
|
4596
|
-
continue; // don't update virtual STOP
|
|
4597
4776
|
const delta = roundPolicyGradientValue(advantage * grad * pgScale);
|
|
4598
4777
|
if (delta === 0)
|
|
4599
4778
|
continue;
|
|
@@ -4627,8 +4806,8 @@ function createRouterArtifactV2(packId, builtAt, graph, vectors, eventExport, se
|
|
|
4627
4806
|
const remappedServeTimeDecisions = serveTimeDecisions.map((decision) => remapServeTimeDecisionToGraph(decision, resolveBlockId));
|
|
4628
4807
|
// 2. Join serve-time decisions with explicit feedback first, then let teacher-v2 observations override when available.
|
|
4629
4808
|
const outcomeMap = joinDecisionsWithFeedback(remappedServeTimeDecisions, eventExport);
|
|
4630
|
-
const
|
|
4631
|
-
for (const [decisionId, reward] of
|
|
4809
|
+
const teacherObservationBindings = joinDecisionsWithTeacherObservationOutcomes(remappedServeTimeDecisions, teacherObservationOutcomes);
|
|
4810
|
+
for (const [decisionId, reward] of teacherObservationBindings.outcomes.entries()) {
|
|
4632
4811
|
if (reward !== 0) {
|
|
4633
4812
|
outcomeMap.set(decisionId, reward);
|
|
4634
4813
|
}
|
|
@@ -4695,7 +4874,8 @@ function createRouterArtifactV2(packId, builtAt, graph, vectors, eventExport, se
|
|
|
4695
4874
|
if (fallback.policyUpdates.length > 0) {
|
|
4696
4875
|
return {
|
|
4697
4876
|
artifact: fallback,
|
|
4698
|
-
updatedBaseline: currentBaseline
|
|
4877
|
+
updatedBaseline: currentBaseline,
|
|
4878
|
+
observationBindingStats: teacherObservationBindings.stats
|
|
4699
4879
|
};
|
|
4700
4880
|
}
|
|
4701
4881
|
}
|
|
@@ -4717,7 +4897,7 @@ function createRouterArtifactV2(packId, builtAt, graph, vectors, eventExport, se
|
|
|
4717
4897
|
: remappedServeTimeDecisions.length === 0
|
|
4718
4898
|
? "no serve-time decisions supplied for V2 learned routing refresh"
|
|
4719
4899
|
: supervisedTrajectoryCount === 0
|
|
4720
|
-
?
|
|
4900
|
+
? `no outcomes found for serve-time decisions (${summarizeTeacherObservationBindingStats(teacherObservationBindings.stats)})`
|
|
4721
4901
|
: "trajectory updates produced no learned routing delta";
|
|
4722
4902
|
const artifact = {
|
|
4723
4903
|
routerIdentity: `${packId}:route_fn`,
|
|
@@ -4765,7 +4945,11 @@ function createRouterArtifactV2(packId, builtAt, graph, vectors, eventExport, se
|
|
|
4765
4945
|
traces,
|
|
4766
4946
|
policyUpdates
|
|
4767
4947
|
};
|
|
4768
|
-
return {
|
|
4948
|
+
return {
|
|
4949
|
+
artifact,
|
|
4950
|
+
updatedBaseline: currentBaseline,
|
|
4951
|
+
observationBindingStats: teacherObservationBindings.stats
|
|
4952
|
+
};
|
|
4769
4953
|
}
|
|
4770
4954
|
function resolveEventExport(input) {
|
|
4771
4955
|
if (input.eventExports === undefined) {
|
|
@@ -4922,6 +5106,7 @@ export function buildCandidatePack(input) {
|
|
|
4922
5106
|
const vectors = createVectorsPayload(graph);
|
|
4923
5107
|
let router = null;
|
|
4924
5108
|
let updatedBaseline = null;
|
|
5109
|
+
let observationBindingStats = null;
|
|
4925
5110
|
const teacherObservationOutcomes = input.activationRoot === undefined
|
|
4926
5111
|
? []
|
|
4927
5112
|
: loadTeacherObservationOutcomesFromActivation(input.activationRoot);
|
|
@@ -4930,6 +5115,7 @@ export function buildCandidatePack(input) {
|
|
|
4930
5115
|
const v2Result = createRouterArtifactV2(packId, builtAt, graph, vectors, eventExport, input.serveTimeDecisions, input.baselineState ?? initBaseline(), input.sparseFeedback, input.principalBacklog, teacherObservationOutcomes);
|
|
4931
5116
|
router = v2Result.artifact;
|
|
4932
5117
|
updatedBaseline = v2Result.updatedBaseline;
|
|
5118
|
+
observationBindingStats = v2Result.observationBindingStats;
|
|
4933
5119
|
}
|
|
4934
5120
|
else {
|
|
4935
5121
|
router = createRouterArtifact(packId, builtAt, graph, vectors, eventExport, input.sparseFeedback, input.principalBacklog);
|
|
@@ -5045,7 +5231,8 @@ export function buildCandidatePack(input) {
|
|
|
5045
5231
|
pgVersionUsed: input.learnedRouting ? (useV2 ? "v2" : "v1") : null,
|
|
5046
5232
|
decisionLogCount,
|
|
5047
5233
|
fallbackReason,
|
|
5048
|
-
updatedBaseline
|
|
5234
|
+
updatedBaseline,
|
|
5235
|
+
observationBindingStats
|
|
5049
5236
|
},
|
|
5050
5237
|
summary: {
|
|
5051
5238
|
packId,
|
|
@@ -19,6 +19,22 @@ function normalizeOptionalCliString(value) {
|
|
|
19
19
|
return trimmed.length > 0 ? trimmed : null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function normalizeReportedProofPath(filePath) {
|
|
23
|
+
const normalizedPath = normalizeOptionalCliString(filePath);
|
|
24
|
+
if (normalizedPath === null) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (normalizedPath === "~") {
|
|
28
|
+
return homedir();
|
|
29
|
+
}
|
|
30
|
+
if (normalizedPath.startsWith("~/")) {
|
|
31
|
+
return path.join(homedir(), normalizedPath.slice(2));
|
|
32
|
+
}
|
|
33
|
+
return path.isAbsolute(normalizedPath)
|
|
34
|
+
? normalizedPath
|
|
35
|
+
: path.resolve(normalizedPath);
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
function canonicalizeExistingProofPath(filePath) {
|
|
23
39
|
const resolvedPath = path.resolve(filePath);
|
|
24
40
|
try {
|
|
@@ -191,6 +207,12 @@ function readJsonSnapshot(filePath) {
|
|
|
191
207
|
}
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
function describeStepWarning(step) {
|
|
211
|
+
return step.captureState === "partial"
|
|
212
|
+
? `${step.stepId} ended as ${step.resultClass} with partial capture`
|
|
213
|
+
: `${step.stepId} ended as ${step.resultClass}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
194
216
|
function extractStartupBreadcrumbs(logText, bundleStartedAtIso) {
|
|
195
217
|
if (!logText) {
|
|
196
218
|
return { all: [], afterBundleStart: [] };
|
|
@@ -230,6 +252,7 @@ function extractStatusSignals(statusText) {
|
|
|
230
252
|
serveActivePack: /serve\s+state=serving_active_pack/.test(statusText),
|
|
231
253
|
routeFnAvailable: /routeFn\s+available=yes/.test(statusText),
|
|
232
254
|
proofPath: statusText.match(/proofPath=([^\s]+)/)?.[1] ?? null,
|
|
255
|
+
proofError: statusText.match(/proofError=([^\s]+)/)?.[1] ?? null,
|
|
233
256
|
};
|
|
234
257
|
}
|
|
235
258
|
|
|
@@ -238,69 +261,106 @@ function hasPackagedHookSource(pluginInspectText) {
|
|
|
238
261
|
}
|
|
239
262
|
|
|
240
263
|
function buildVerdict({ steps, gatewayStatus, pluginInspect, statusSignals, breadcrumbs, runtimeLoadProofSnapshot, openclawHome }) {
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
return {
|
|
244
|
-
verdict: "command_failed",
|
|
245
|
-
severity: "blocking",
|
|
246
|
-
why: `${failedStep.stepId} exited as ${failedStep.resultClass}`,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
264
|
+
const failedSteps = steps.filter((step) => step.resultClass !== "success" && step.skipped !== true);
|
|
265
|
+
const failedDetailedStatusStep = failedSteps.find((step) => step.stepId === "05-detailed-status");
|
|
249
266
|
const gatewayHealthy = /Runtime:\s+running/m.test(gatewayStatus) && /RPC probe:\s+ok/m.test(gatewayStatus);
|
|
250
267
|
const pluginLoaded = /Status:\s+loaded/m.test(pluginInspect);
|
|
251
268
|
const packagedHookPath = hasPackagedHookSource(pluginInspect);
|
|
252
269
|
const breadcrumbLoaded = breadcrumbs.afterBundleStart.some((entry) => entry.kind === "loaded");
|
|
253
270
|
const runtimeProofMatched = Array.isArray(runtimeLoadProofSnapshot?.value?.profiles)
|
|
254
271
|
&& runtimeLoadProofSnapshot.value.profiles.some((profile) => canonicalizeExistingProofPath(profile?.openclawHome ?? "") === canonicalizeExistingProofPath(openclawHome));
|
|
255
|
-
const
|
|
256
|
-
if (!gatewayHealthy)
|
|
257
|
-
missingProofs.push("gateway_health");
|
|
258
|
-
if (!pluginLoaded)
|
|
259
|
-
missingProofs.push("plugin_loaded");
|
|
260
|
-
if (!packagedHookPath)
|
|
261
|
-
missingProofs.push("packaged_hook_path");
|
|
272
|
+
const runtimeTruthGaps = [];
|
|
262
273
|
if (!statusSignals.statusOk)
|
|
263
|
-
|
|
274
|
+
runtimeTruthGaps.push("status_ok");
|
|
264
275
|
if (!statusSignals.loadProofReady)
|
|
265
|
-
|
|
276
|
+
runtimeTruthGaps.push("load_proof");
|
|
266
277
|
if (!statusSignals.runtimeProven)
|
|
267
|
-
|
|
278
|
+
runtimeTruthGaps.push("runtime_proven");
|
|
268
279
|
if (!statusSignals.serveActivePack)
|
|
269
|
-
|
|
280
|
+
runtimeTruthGaps.push("serve_active_pack");
|
|
270
281
|
if (!statusSignals.routeFnAvailable)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
runtimeTruthGaps.push("route_fn");
|
|
283
|
+
const warningCodes = [];
|
|
284
|
+
const warnings = [];
|
|
285
|
+
if (!gatewayHealthy) {
|
|
286
|
+
warningCodes.push("gateway_health");
|
|
287
|
+
warnings.push("gateway status did not confirm runtime running and RPC probe ok");
|
|
288
|
+
}
|
|
289
|
+
if (!pluginLoaded) {
|
|
290
|
+
warningCodes.push("plugin_loaded");
|
|
291
|
+
warnings.push("plugin inspect did not report Status: loaded");
|
|
292
|
+
}
|
|
293
|
+
if (!packagedHookPath) {
|
|
294
|
+
warningCodes.push("packaged_hook_path");
|
|
295
|
+
warnings.push("plugin inspect did not confirm the packaged hook source");
|
|
296
|
+
}
|
|
297
|
+
if (!breadcrumbLoaded) {
|
|
298
|
+
warningCodes.push("startup_breadcrumb");
|
|
299
|
+
warnings.push("startup log did not contain a post-bundle [openclawbrain] BRAIN LOADED breadcrumb");
|
|
300
|
+
}
|
|
301
|
+
if (!runtimeProofMatched) {
|
|
302
|
+
warningCodes.push("runtime_load_proof_record");
|
|
303
|
+
warnings.push(runtimeLoadProofSnapshot.error !== null
|
|
304
|
+
? `runtime-load-proof snapshot was unreadable: ${runtimeLoadProofSnapshot.error}`
|
|
305
|
+
: runtimeLoadProofSnapshot.exists
|
|
306
|
+
? "runtime-load-proof snapshot did not include the current openclaw home"
|
|
307
|
+
: "runtime-load-proof snapshot was missing");
|
|
308
|
+
}
|
|
309
|
+
if (statusSignals.proofError !== null && statusSignals.proofError !== "none") {
|
|
310
|
+
warningCodes.push(`proof_error:${statusSignals.proofError}`);
|
|
311
|
+
warnings.push(`detailed status reported proofError=${statusSignals.proofError}`);
|
|
312
|
+
}
|
|
313
|
+
for (const step of failedSteps) {
|
|
314
|
+
warningCodes.push(`step:${step.stepId}:${step.resultClass}:${step.captureState}`);
|
|
315
|
+
warnings.push(describeStepWarning(step));
|
|
316
|
+
}
|
|
317
|
+
const uniqueWarningCodes = [...new Set(warningCodes)];
|
|
318
|
+
const uniqueWarnings = [...new Set(warnings)];
|
|
319
|
+
if (runtimeTruthGaps.length === 0 && uniqueWarningCodes.length === 0) {
|
|
277
320
|
return {
|
|
278
321
|
verdict: "success_and_proven",
|
|
279
322
|
severity: "none",
|
|
280
323
|
why: "install, restart, gateway health, plugin load, startup breadcrumb, runtime-load-proof record, and detailed status all aligned",
|
|
324
|
+
missingProofs: [],
|
|
325
|
+
warnings: [],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
if (runtimeTruthGaps.length === 0) {
|
|
329
|
+
return {
|
|
330
|
+
verdict: "success_but_proof_incomplete",
|
|
331
|
+
severity: "degraded",
|
|
332
|
+
why: `status/runtime evidence stayed healthy, but proof warnings remained: ${uniqueWarningCodes.join(", ")}`,
|
|
333
|
+
missingProofs: uniqueWarningCodes,
|
|
334
|
+
warnings: uniqueWarnings,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const hasUsableStatusTruth = statusSignals.statusOk
|
|
338
|
+
|| statusSignals.loadProofReady
|
|
339
|
+
|| statusSignals.runtimeProven
|
|
340
|
+
|| statusSignals.serveActivePack
|
|
341
|
+
|| statusSignals.routeFnAvailable;
|
|
342
|
+
if (failedDetailedStatusStep && !hasUsableStatusTruth) {
|
|
343
|
+
return {
|
|
344
|
+
verdict: "command_failed",
|
|
345
|
+
severity: "blocking",
|
|
346
|
+
why: `${failedDetailedStatusStep.stepId} ended as ${failedDetailedStatusStep.resultClass} before runtime truth could be established`,
|
|
347
|
+
missingProofs: runtimeTruthGaps,
|
|
348
|
+
warnings: uniqueWarnings,
|
|
281
349
|
};
|
|
282
350
|
}
|
|
283
|
-
const blocking = missingProofs.some((item) => [
|
|
284
|
-
"gateway_health",
|
|
285
|
-
"plugin_loaded",
|
|
286
|
-
"packaged_hook_path",
|
|
287
|
-
"status_ok",
|
|
288
|
-
"load_proof",
|
|
289
|
-
"runtime_proven",
|
|
290
|
-
"serve_active_pack",
|
|
291
|
-
"route_fn",
|
|
292
|
-
].includes(item));
|
|
293
351
|
return {
|
|
294
|
-
verdict:
|
|
295
|
-
severity:
|
|
296
|
-
why: `missing or conflicting
|
|
297
|
-
missingProofs,
|
|
352
|
+
verdict: "degraded_or_failed_proof",
|
|
353
|
+
severity: "blocking",
|
|
354
|
+
why: `missing or conflicting runtime truths: ${runtimeTruthGaps.join(", ")}`,
|
|
355
|
+
missingProofs: [...new Set([...runtimeTruthGaps, ...uniqueWarningCodes])],
|
|
356
|
+
warnings: uniqueWarnings,
|
|
298
357
|
};
|
|
299
358
|
}
|
|
300
359
|
|
|
301
360
|
function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspectText, statusSignals, breadcrumbs, runtimeLoadProofSnapshot }) {
|
|
302
361
|
const passed = [];
|
|
303
362
|
const missing = [];
|
|
363
|
+
const warnings = Array.isArray(verdict.warnings) ? verdict.warnings : [];
|
|
304
364
|
if (steps.find((step) => step.stepId === "01-install")?.resultClass === "success") {
|
|
305
365
|
passed.push("install command succeeded");
|
|
306
366
|
}
|
|
@@ -350,6 +410,9 @@ function buildSummary({ options, steps, verdict, gatewayStatusText, pluginInspec
|
|
|
350
410
|
"## Missing / incomplete",
|
|
351
411
|
...(missing.length === 0 ? ["- none"] : missing.map((item) => `- ${item}`)),
|
|
352
412
|
"",
|
|
413
|
+
"## Warnings",
|
|
414
|
+
...(warnings.length === 0 ? ["- none"] : warnings.map((item) => `- ${item}`)),
|
|
415
|
+
"",
|
|
353
416
|
"## Step ledger",
|
|
354
417
|
...steps.map((step) => `- ${step.stepId}: ${step.skipped ? "skipped" : `${step.resultClass} (${step.captureState})`} - ${step.summary}`),
|
|
355
418
|
];
|
|
@@ -570,11 +633,12 @@ export function captureOperatorProofBundle(options) {
|
|
|
570
633
|
const statusCapture = addStep("05-detailed-status", "detailed status", cliInvocation.command, [...cliInvocation.args, "status", "--openclaw-home", options.openclawHome, "--detailed"]);
|
|
571
634
|
const gatewayLogPath = extractGatewayLogPath(gatewayStatusCapture.stdout);
|
|
572
635
|
const activationRoot = extractActivationRoot(statusCapture.stdout, options.activationRoot ?? null);
|
|
573
|
-
const
|
|
636
|
+
const statusSignals = extractStatusSignals(statusCapture.stdout);
|
|
637
|
+
const runtimeLoadProofPath = normalizeReportedProofPath(statusSignals.proofPath)
|
|
638
|
+
?? path.join(activationRoot, "attachment-truth", "runtime-load-proofs.json");
|
|
574
639
|
const runtimeLoadProofSnapshot = readJsonSnapshot(runtimeLoadProofPath);
|
|
575
640
|
const gatewayLogText = readTextIfExists(gatewayLogPath);
|
|
576
641
|
const breadcrumbs = extractStartupBreadcrumbs(gatewayLogText, bundleStartedAt);
|
|
577
|
-
const statusSignals = extractStatusSignals(statusCapture.stdout);
|
|
578
642
|
writeText(path.join(bundleDir, "extracted-startup-breadcrumbs.log"), breadcrumbs.all.length === 0
|
|
579
643
|
? "<no matching breadcrumbs found>\n"
|
|
580
644
|
: `${breadcrumbs.all.map((entry) => entry.line).join("\n")}\n`);
|