principles-disciple 1.34.2 → 1.36.0
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/.dependency-cruiser.json +19 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -3
- package/src/config/defaults/runtime.ts +100 -24
- package/src/core/correction-cue-learner.ts +23 -8
- package/src/core/event-log.ts +87 -20
- package/src/core/init.ts +2 -2
- package/src/core/nocturnal-candidate-scoring.ts +6 -6
- package/src/core/nocturnal-trinity-types.ts +94 -0
- package/src/core/nocturnal-trinity.ts +35 -99
- package/src/core/session-tracker.ts +7 -6
- package/src/core/system-logger.ts +104 -12
- package/src/core/workspace-dir-service.ts +40 -6
- package/src/core/workspace-dir-validation.ts +5 -37
- package/src/hooks/prompt.ts +3 -3
- package/src/hooks/trajectory-collector.ts +7 -7
- package/src/index.ts +8 -68
- package/src/service/central-sync-service.ts +3 -8
- package/src/service/correction-observer-workflow-manager.ts +2 -2
- package/src/service/evolution-worker.ts +13 -22
- package/src/service/keyword-optimization-service.ts +2 -2
- package/src/service/nocturnal-service.ts +62 -43
- package/src/service/subagent-workflow/correction-observer-types.ts +69 -0
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +246 -0
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +4 -4
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +4 -4
- package/src/service/subagent-workflow/index.ts +13 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +2 -2
- package/src/service/subagent-workflow/types.ts +69 -3
- package/src/utils/shadow-fingerprint.ts +42 -0
- package/src/utils/workspace-resolver.ts +54 -0
- package/tests/core/correction-cue-learner.test.ts +345 -0
- package/tests/core/workspace-dir-validation.test.ts +1 -1
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +3 -3
- package/vitest.config.ts +53 -6
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
import * as fs from 'fs';
|
|
32
32
|
import * as path from 'path';
|
|
33
33
|
import { randomUUID } from 'crypto';
|
|
34
|
-
import type { RecentPainContext } from './
|
|
34
|
+
import type { RecentPainContext } from './subagent-workflow/types.js';
|
|
35
|
+
import type { PluginLogger } from '../openclaw-sdk.js';
|
|
35
36
|
import {
|
|
36
37
|
createNocturnalTrajectoryExtractor,
|
|
37
38
|
computeThinkingModelDelta,
|
|
@@ -122,6 +123,7 @@ function incrementGeneratedSampleCount(stateDir: string, principleId: string): v
|
|
|
122
123
|
state.generatedSampleCount += 1;
|
|
123
124
|
setPrincipleState(stateDir, state);
|
|
124
125
|
} catch (err) {
|
|
126
|
+
// eslint-disable-next-line no-console -- Non-critical warning in helper function
|
|
125
127
|
console.warn(`[nocturnal-service] Failed to sync generatedSampleCount for ${principleId}:`, err instanceof Error ? err.stack : err);
|
|
126
128
|
}
|
|
127
129
|
}
|
|
@@ -268,6 +270,12 @@ export interface NocturnalServiceOptions {
|
|
|
268
270
|
* When omitted, a deterministic local candidate is synthesized.
|
|
269
271
|
*/
|
|
270
272
|
artificerOutputOverride?: string;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Logger for diagnostic output.
|
|
276
|
+
* When provided, warnings are logged via logger.warn instead of console.warn.
|
|
277
|
+
*/
|
|
278
|
+
logger?: PluginLogger;
|
|
271
279
|
}
|
|
272
280
|
|
|
273
281
|
// ---------------------------------------------------------------------------
|
|
@@ -299,16 +307,16 @@ function invokeStubReflector(
|
|
|
299
307
|
const hasPain = snapshot.stats.totalPainEvents > 0;
|
|
300
308
|
const hasFailures = (snapshot.stats.failureCount ?? 0) > 0;
|
|
301
309
|
|
|
302
|
-
|
|
310
|
+
|
|
303
311
|
let badDecision: string;
|
|
304
|
-
|
|
312
|
+
|
|
305
313
|
let betterDecision: string;
|
|
306
|
-
|
|
314
|
+
|
|
307
315
|
let rationale: string;
|
|
308
316
|
|
|
309
317
|
if (hasGateBlocks && snapshot.gateBlocks.length > 0) {
|
|
310
318
|
// Use actual gate block content
|
|
311
|
-
|
|
319
|
+
|
|
312
320
|
const block = snapshot.gateBlocks[0];
|
|
313
321
|
const tool = block.toolName ?? 'a tool';
|
|
314
322
|
const file = block.filePath ? ` on ${block.filePath}` : '';
|
|
@@ -317,7 +325,7 @@ function invokeStubReflector(
|
|
|
317
325
|
rationale = `Gate blocks exist for a reason — bypassing them without understanding the underlying constraint risks unintended consequences. The block on ${tool}${file} indicates the operation exceeded allowed thresholds for the current evolution tier.`;
|
|
318
326
|
} else if (hasPain && snapshot.painEvents.length > 0) {
|
|
319
327
|
// Use actual pain event content
|
|
320
|
-
|
|
328
|
+
|
|
321
329
|
const pain = snapshot.painEvents[0];
|
|
322
330
|
const painSource = pain.source ?? 'unknown';
|
|
323
331
|
const painReason = pain.reason ? `: ${pain.reason}` : '';
|
|
@@ -413,7 +421,7 @@ function buildGateBlockRefs(snapshot: NocturnalSessionSnapshot): string[] {
|
|
|
413
421
|
}
|
|
414
422
|
|
|
415
423
|
|
|
416
|
-
|
|
424
|
+
|
|
417
425
|
function buildDefaultArtificerOutput(
|
|
418
426
|
ruleId: string,
|
|
419
427
|
artifact: NocturnalArtifact,
|
|
@@ -463,7 +471,7 @@ function buildDefaultArtificerOutput(
|
|
|
463
471
|
}
|
|
464
472
|
|
|
465
473
|
|
|
466
|
-
|
|
474
|
+
|
|
467
475
|
function persistCodeCandidate(
|
|
468
476
|
workspaceDir: string,
|
|
469
477
|
stateDir: string,
|
|
@@ -521,6 +529,7 @@ function persistCodeCandidate(
|
|
|
521
529
|
try {
|
|
522
530
|
refreshPrincipleLifecycle(workspaceDir, stateDir);
|
|
523
531
|
} catch (err) {
|
|
532
|
+
// eslint-disable-next-line no-console -- Non-critical warning in helper function
|
|
524
533
|
console.warn('[nocturnal-service] Lifecycle refresh failed after code candidate persistence:', err instanceof Error ? err.stack : err);
|
|
525
534
|
}
|
|
526
535
|
return {
|
|
@@ -560,7 +569,7 @@ function persistCodeCandidate(
|
|
|
560
569
|
}
|
|
561
570
|
|
|
562
571
|
|
|
563
|
-
|
|
572
|
+
|
|
564
573
|
function maybePersistArtificerCandidate(
|
|
565
574
|
workspaceDir: string,
|
|
566
575
|
stateDir: string,
|
|
@@ -698,6 +707,11 @@ export function executeNocturnalReflection(
|
|
|
698
707
|
stateDir: string,
|
|
699
708
|
options: NocturnalServiceOptions = {}
|
|
700
709
|
): NocturnalRunResult {
|
|
710
|
+
// Use provided logger or fallback to console
|
|
711
|
+
const logger = options.logger;
|
|
712
|
+
// eslint-disable-next-line no-console -- Intentional console fallback when no logger provided
|
|
713
|
+
const warn = logger?.warn?.bind(logger) ?? console.warn.bind(console);
|
|
714
|
+
|
|
701
715
|
const diagnostics: NocturnalRunDiagnostics = {
|
|
702
716
|
preflight: null,
|
|
703
717
|
selection: null,
|
|
@@ -797,18 +811,18 @@ export function executeNocturnalReflection(
|
|
|
797
811
|
// The async version would be used in real worker integration
|
|
798
812
|
const config = loadNocturnalConfig(stateDir);
|
|
799
813
|
void recordRunStart(stateDir, selectedPrincipleId, config.cooldown_ms).catch((err) => {
|
|
800
|
-
|
|
814
|
+
warn(`[nocturnal-service] Failed to record run start: ${String(err)}`);
|
|
801
815
|
});
|
|
802
816
|
|
|
803
817
|
// -------------------------------------------------------------------------
|
|
804
818
|
// Step 5: Artifact generation (Trinity or single-reflector)
|
|
805
819
|
// -------------------------------------------------------------------------
|
|
806
820
|
|
|
807
|
-
|
|
821
|
+
|
|
808
822
|
let trinityArtifact: TrinityDraftArtifact | null = null;
|
|
809
823
|
let trinityResult: TrinityResult | null = null;
|
|
810
824
|
|
|
811
|
-
|
|
825
|
+
|
|
812
826
|
let rawJson: string;
|
|
813
827
|
|
|
814
828
|
if (options.skipReflector) {
|
|
@@ -834,7 +848,7 @@ export function executeNocturnalReflection(
|
|
|
834
848
|
// Trinity failed — fail closed (same semantics as production)
|
|
835
849
|
const failures = trinityResult.failures.map((f) => `${f.stage}: ${f.reason}`);
|
|
836
850
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity override failed: ${failures.join('; ')}` }).catch((err) => {
|
|
837
|
-
|
|
851
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
838
852
|
});
|
|
839
853
|
// Emit threshold signals: malformed Trinity override is a strong signal
|
|
840
854
|
adjustThresholdsFromSignals(stateDir, {
|
|
@@ -857,7 +871,7 @@ export function executeNocturnalReflection(
|
|
|
857
871
|
if (!draftValidation.valid) {
|
|
858
872
|
const {failures} = draftValidation;
|
|
859
873
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity draft invalid: ${failures.join('; ')}` }).catch((err) => {
|
|
860
|
-
|
|
874
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
861
875
|
});
|
|
862
876
|
// Emit threshold signals: malformed draft content is a strong signal
|
|
863
877
|
adjustThresholdsFromSignals(stateDir, {
|
|
@@ -908,7 +922,7 @@ export function executeNocturnalReflection(
|
|
|
908
922
|
// Trinity draft invalid — fail closed
|
|
909
923
|
const {failures} = draftValidation;
|
|
910
924
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity draft invalid: ${failures.join('; ')}` }).catch((err) => {
|
|
911
|
-
|
|
925
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
912
926
|
});
|
|
913
927
|
// Emit threshold signals: malformed draft content is a strong signal
|
|
914
928
|
adjustThresholdsFromSignals(stateDir, {
|
|
@@ -935,7 +949,7 @@ export function executeNocturnalReflection(
|
|
|
935
949
|
// Phase 6 requirement: malformed Trinity stage output fails closed
|
|
936
950
|
const failures = trinityResult.failures.map((f) => `${f.stage}: ${f.reason}`);
|
|
937
951
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity chain failed: ${failures.join('; ')}` }).catch((err) => {
|
|
938
|
-
|
|
952
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
939
953
|
});
|
|
940
954
|
// Emit threshold signals: malformed Trinity is the strongest signal for tightening schema threshold
|
|
941
955
|
adjustThresholdsFromSignals(stateDir, {
|
|
@@ -979,7 +993,7 @@ export function executeNocturnalReflection(
|
|
|
979
993
|
if (!arbiterResult.passed || !arbiterResult.artifact) {
|
|
980
994
|
const failures = arbiterResult.failures.map((f) => f.reason);
|
|
981
995
|
void recordRunEnd(stateDir, 'failed', { reason: failures.join('; ') }).catch((err) => {
|
|
982
|
-
|
|
996
|
+
warn(`[nocturnal-service] Failed to record run end (arbiter failed): ${String(err)}`);
|
|
983
997
|
});
|
|
984
998
|
// Emit threshold signals: arbiter rejection indicates principle alignment issues
|
|
985
999
|
adjustThresholdsFromSignals(stateDir, {
|
|
@@ -1005,7 +1019,7 @@ export function executeNocturnalReflection(
|
|
|
1005
1019
|
if (!execResult.executable) {
|
|
1006
1020
|
const failures = execResult.failures.map((f) => f.reason);
|
|
1007
1021
|
void recordRunEnd(stateDir, 'failed', { reason: failures.join('; ') }).catch((err) => {
|
|
1008
|
-
|
|
1022
|
+
warn(`[nocturnal-service] Failed to record run end (executability failed): ${String(err)}`);
|
|
1009
1023
|
});
|
|
1010
1024
|
// Emit threshold signals: executability rejection indicates action quality issues
|
|
1011
1025
|
adjustThresholdsFromSignals(stateDir, {
|
|
@@ -1033,7 +1047,7 @@ export function executeNocturnalReflection(
|
|
|
1033
1047
|
};
|
|
1034
1048
|
|
|
1035
1049
|
|
|
1036
|
-
|
|
1050
|
+
|
|
1037
1051
|
let persistedPath: string;
|
|
1038
1052
|
try {
|
|
1039
1053
|
persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
|
|
@@ -1041,7 +1055,7 @@ export function executeNocturnalReflection(
|
|
|
1041
1055
|
diagnostics.persistedPath = persistedPath;
|
|
1042
1056
|
} catch (err) {
|
|
1043
1057
|
void recordRunEnd(stateDir, 'failed', { reason: `persistence error: ${String(err)}` }).catch((e) => {
|
|
1044
|
-
|
|
1058
|
+
warn(`[nocturnal-service] Failed to record run end (persistence failed): ${String(e)}`);
|
|
1045
1059
|
});
|
|
1046
1060
|
return {
|
|
1047
1061
|
success: false,
|
|
@@ -1066,7 +1080,7 @@ export function executeNocturnalReflection(
|
|
|
1066
1080
|
} catch (err) {
|
|
1067
1081
|
// Non-fatal: artifact is persisted, registry is secondary.
|
|
1068
1082
|
// Log but don't fail the run.
|
|
1069
|
-
|
|
1083
|
+
warn(`[nocturnal-service] Failed to register sample in dataset registry: ${String(err)}`);
|
|
1070
1084
|
}
|
|
1071
1085
|
|
|
1072
1086
|
try {
|
|
@@ -1084,7 +1098,7 @@ export function executeNocturnalReflection(
|
|
|
1084
1098
|
createdAt: arbiterResult.artifact.createdAt,
|
|
1085
1099
|
});
|
|
1086
1100
|
} catch (err) {
|
|
1087
|
-
|
|
1101
|
+
warn(`[nocturnal-service] Failed to append behavioral artifact lineage: ${String(err)}`);
|
|
1088
1102
|
}
|
|
1089
1103
|
|
|
1090
1104
|
diagnostics.artificer = maybePersistArtificerCandidate(
|
|
@@ -1101,7 +1115,7 @@ export function executeNocturnalReflection(
|
|
|
1101
1115
|
// Step 9: Record run success
|
|
1102
1116
|
// -------------------------------------------------------------------------
|
|
1103
1117
|
void recordRunEnd(stateDir, 'success', { sampleCount: 1 }).catch((err) => {
|
|
1104
|
-
|
|
1118
|
+
warn(`[nocturnal-service] Failed to record run end (success): ${String(err)}`);
|
|
1105
1119
|
});
|
|
1106
1120
|
|
|
1107
1121
|
// -------------------------------------------------------------------------
|
|
@@ -1161,7 +1175,7 @@ export async function executeNocturnalReflectionAsync(
|
|
|
1161
1175
|
// If runtime adapter is provided, use async Trinity path
|
|
1162
1176
|
if (options.runtimeAdapter) {
|
|
1163
1177
|
|
|
1164
|
-
|
|
1178
|
+
|
|
1165
1179
|
return executeNocturnalReflectionWithAdapter(workspaceDir, stateDir, options);
|
|
1166
1180
|
}
|
|
1167
1181
|
|
|
@@ -1178,6 +1192,11 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1178
1192
|
stateDir: string,
|
|
1179
1193
|
options: NocturnalServiceOptions
|
|
1180
1194
|
): Promise<NocturnalRunResult> {
|
|
1195
|
+
// Use provided logger or fallback to console
|
|
1196
|
+
const logger = options.logger;
|
|
1197
|
+
// eslint-disable-next-line no-console -- Intentional console fallback when no logger provided
|
|
1198
|
+
const warn = logger?.warn?.bind(logger) ?? console.warn.bind(console);
|
|
1199
|
+
|
|
1181
1200
|
const diagnostics: NocturnalRunDiagnostics = {
|
|
1182
1201
|
preflight: null,
|
|
1183
1202
|
selection: null,
|
|
@@ -1219,13 +1238,13 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1219
1238
|
|
|
1220
1239
|
// Step 2: Target selection (or use override to skip)
|
|
1221
1240
|
|
|
1222
|
-
|
|
1241
|
+
|
|
1223
1242
|
let selectedPrincipleId: string | undefined;
|
|
1224
1243
|
|
|
1225
|
-
|
|
1244
|
+
|
|
1226
1245
|
let selectedSessionId: string | undefined;
|
|
1227
1246
|
|
|
1228
|
-
|
|
1247
|
+
|
|
1229
1248
|
let snapshot: NocturnalSessionSnapshot | null = null;
|
|
1230
1249
|
|
|
1231
1250
|
if (options.principleIdOverride && options.snapshotOverride) {
|
|
@@ -1248,7 +1267,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1248
1267
|
// Skip Selector: use provided principleId and snapshot directly
|
|
1249
1268
|
selectedPrincipleId = options.principleIdOverride;
|
|
1250
1269
|
selectedSessionId = snapshotValidation.snapshot.sessionId;
|
|
1251
|
-
|
|
1270
|
+
|
|
1252
1271
|
snapshot = snapshotValidation.snapshot;
|
|
1253
1272
|
// Calculate violation density from snapshot stats for meaningful diagnostics
|
|
1254
1273
|
const snapStats = snapshotValidation.snapshot.stats;
|
|
@@ -1305,10 +1324,10 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1305
1324
|
}
|
|
1306
1325
|
|
|
1307
1326
|
|
|
1308
|
-
|
|
1327
|
+
|
|
1309
1328
|
selectedPrincipleId = selection.selectedPrincipleId;
|
|
1310
1329
|
|
|
1311
|
-
|
|
1330
|
+
|
|
1312
1331
|
selectedSessionId = selection.selectedSessionId;
|
|
1313
1332
|
|
|
1314
1333
|
if (!selectedPrincipleId || !selectedSessionId) {
|
|
@@ -1338,16 +1357,16 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1338
1357
|
// Step 3: Record run start
|
|
1339
1358
|
const config = loadNocturnalConfig(stateDir);
|
|
1340
1359
|
void recordRunStart(stateDir, selectedPrincipleId, config.cooldown_ms).catch((err) => {
|
|
1341
|
-
|
|
1360
|
+
warn(`[nocturnal-service] Failed to record run start: ${String(err)}`);
|
|
1342
1361
|
});
|
|
1343
1362
|
|
|
1344
1363
|
// Step 4: Trinity execution via adapter (async)
|
|
1345
1364
|
|
|
1346
|
-
|
|
1365
|
+
|
|
1347
1366
|
let trinityArtifact: TrinityDraftArtifact | null = null;
|
|
1348
1367
|
let trinityResult: TrinityResult | null = null;
|
|
1349
1368
|
|
|
1350
|
-
|
|
1369
|
+
|
|
1351
1370
|
let rawJson: string;
|
|
1352
1371
|
|
|
1353
1372
|
if (options.skipReflector) {
|
|
@@ -1370,7 +1389,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1370
1389
|
if (!trinityResult.success) {
|
|
1371
1390
|
const failures = trinityResult.failures.map((f) => `${f.stage}: ${f.reason}`);
|
|
1372
1391
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity override failed: ${failures.join('; ')}` }).catch((err) => {
|
|
1373
|
-
|
|
1392
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
1374
1393
|
});
|
|
1375
1394
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
|
|
1376
1395
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: [`Trinity override failed: ${failures.join('; ')}`], snapshot, diagnostics };
|
|
@@ -1397,7 +1416,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1397
1416
|
if (!draftValidation.valid) {
|
|
1398
1417
|
const {failures} = draftValidation;
|
|
1399
1418
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity draft invalid: ${failures.join('; ')}` }).catch((err) => {
|
|
1400
|
-
|
|
1419
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
1401
1420
|
});
|
|
1402
1421
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
|
|
1403
1422
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: failures, snapshot, diagnostics };
|
|
@@ -1408,7 +1427,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1408
1427
|
} else {
|
|
1409
1428
|
const failures = trinityResult.failures.map((f) => `${f.stage}: ${f.reason}`);
|
|
1410
1429
|
void recordRunEnd(stateDir, 'failed', { reason: `Trinity chain failed: ${failures.join('; ')}` }).catch((err) => {
|
|
1411
|
-
|
|
1430
|
+
warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
|
|
1412
1431
|
});
|
|
1413
1432
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
|
|
1414
1433
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: [`Trinity chain failed: ${failures.join('; ')}`], snapshot, diagnostics };
|
|
@@ -1433,7 +1452,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1433
1452
|
if (!arbiterResult.passed || !arbiterResult.artifact) {
|
|
1434
1453
|
const failures = arbiterResult.failures.map((f) => f.reason);
|
|
1435
1454
|
void recordRunEnd(stateDir, 'failed', { reason: failures.join('; ') }).catch((err) => {
|
|
1436
|
-
|
|
1455
|
+
warn(`[nocturnal-service] Failed to record run end (arbiter failed): ${String(err)}`);
|
|
1437
1456
|
});
|
|
1438
1457
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 0.0, arbiterRejectRate: 1.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
|
|
1439
1458
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: failures, diagnostics };
|
|
@@ -1444,7 +1463,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1444
1463
|
if (!execResult.executable) {
|
|
1445
1464
|
const failures = execResult.failures.map((f) => f.reason);
|
|
1446
1465
|
void recordRunEnd(stateDir, 'failed', { reason: failures.join('; ') }).catch((err) => {
|
|
1447
|
-
|
|
1466
|
+
warn(`[nocturnal-service] Failed to record run end (executability failed): ${String(err)}`);
|
|
1448
1467
|
});
|
|
1449
1468
|
adjustThresholdsFromSignals(stateDir, { malformedRate: 0.0, arbiterRejectRate: 0.0, executabilityRejectRate: 1.0, qualityDelta: 0.0 });
|
|
1450
1469
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: failures, diagnostics };
|
|
@@ -1454,7 +1473,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1454
1473
|
// Step 7: Persist artifact
|
|
1455
1474
|
const artifactWithBoundedAction = { ...arbiterResult.artifact, boundedAction: execResult.boundedAction };
|
|
1456
1475
|
|
|
1457
|
-
|
|
1476
|
+
|
|
1458
1477
|
let persistedPath: string;
|
|
1459
1478
|
try {
|
|
1460
1479
|
persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
|
|
@@ -1462,7 +1481,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1462
1481
|
diagnostics.persistedPath = persistedPath;
|
|
1463
1482
|
} catch (err) {
|
|
1464
1483
|
void recordRunEnd(stateDir, 'failed', { reason: `persistence error: ${String(err)}` }).catch((e) => {
|
|
1465
|
-
|
|
1484
|
+
warn(`[nocturnal-service] Failed to record run end (persistence failed): ${String(e)}`);
|
|
1466
1485
|
});
|
|
1467
1486
|
return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: [`Failed to persist artifact: ${String(err)}`], snapshot, diagnostics };
|
|
1468
1487
|
}
|
|
@@ -1474,7 +1493,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1474
1493
|
incrementGeneratedSampleCount(stateDir, arbiterResult.artifact.principleId);
|
|
1475
1494
|
}
|
|
1476
1495
|
} catch (err) {
|
|
1477
|
-
|
|
1496
|
+
warn(`[nocturnal-service] Failed to register sample in dataset registry: ${String(err)}`);
|
|
1478
1497
|
}
|
|
1479
1498
|
|
|
1480
1499
|
try {
|
|
@@ -1492,7 +1511,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1492
1511
|
createdAt: arbiterResult.artifact.createdAt,
|
|
1493
1512
|
});
|
|
1494
1513
|
} catch (err) {
|
|
1495
|
-
|
|
1514
|
+
warn(`[nocturnal-service] Failed to append behavioral artifact lineage: ${String(err)}`);
|
|
1496
1515
|
}
|
|
1497
1516
|
|
|
1498
1517
|
diagnostics.artificer = maybePersistArtificerCandidate(
|
|
@@ -1507,7 +1526,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1507
1526
|
|
|
1508
1527
|
// Step 9: Record run success
|
|
1509
1528
|
void recordRunEnd(stateDir, 'success', { sampleCount: 1 }).catch((err) => {
|
|
1510
|
-
|
|
1529
|
+
warn(`[nocturnal-service] Failed to record run end (success): ${String(err)}`);
|
|
1511
1530
|
});
|
|
1512
1531
|
|
|
1513
1532
|
// Step 10: Adaptive threshold adjustment
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correction Observer Workflow - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Types for the correction observer LLM optimization workflow.
|
|
5
|
+
* This workflow dispatches an LLM subagent to analyze keyword performance
|
|
6
|
+
* and recommend ADD/UPDATE/REMOVE actions for the correction keyword store.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { SubagentWorkflowSpec } from './types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Input passed to the correction observer subagent.
|
|
13
|
+
*/
|
|
14
|
+
export interface CorrectionObserverPayload {
|
|
15
|
+
/** Parent session that triggered the optimization */
|
|
16
|
+
parentSessionId: string;
|
|
17
|
+
/** Workspace directory */
|
|
18
|
+
workspaceDir: string;
|
|
19
|
+
/** Current keyword store summary for context */
|
|
20
|
+
keywordStoreSummary: {
|
|
21
|
+
totalKeywords: number;
|
|
22
|
+
terms: Array<{
|
|
23
|
+
term: string;
|
|
24
|
+
weight: number;
|
|
25
|
+
hitCount: number;
|
|
26
|
+
truePositiveCount: number;
|
|
27
|
+
falsePositiveCount: number;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
/** Recent user messages for pattern analysis */
|
|
31
|
+
recentMessages: string[];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Trajectory history: user turns where correctionDetected=true (D-40-08).
|
|
35
|
+
* Includes term matched, timestamp, sessionId for FPR trend analysis.
|
|
36
|
+
*/
|
|
37
|
+
trajectoryHistory: Array<{
|
|
38
|
+
sessionId: string;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
term: string;
|
|
41
|
+
userMessage: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Result from the correction observer subagent.
|
|
47
|
+
*/
|
|
48
|
+
export interface CorrectionObserverResult {
|
|
49
|
+
/** Whether any changes were made */
|
|
50
|
+
updated: boolean;
|
|
51
|
+
/** The optimization decisions returned by the LLM */
|
|
52
|
+
updates: Record<string, {
|
|
53
|
+
action: 'add' | 'update' | 'remove';
|
|
54
|
+
weight?: number;
|
|
55
|
+
falsePositiveRate?: number;
|
|
56
|
+
reasoning: string;
|
|
57
|
+
}>;
|
|
58
|
+
/** Human-readable summary */
|
|
59
|
+
summary: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Workflow spec for the correction observer optimization workflow.
|
|
64
|
+
*/
|
|
65
|
+
export interface CorrectionObserverWorkflowSpec extends SubagentWorkflowSpec<CorrectionObserverResult> {
|
|
66
|
+
workflowType: 'correction_observer';
|
|
67
|
+
payload: CorrectionObserverPayload;
|
|
68
|
+
result?: CorrectionObserverResult;
|
|
69
|
+
}
|