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.
Files changed (35) hide show
  1. package/.dependency-cruiser.json +19 -0
  2. package/openclaw.plugin.json +1 -1
  3. package/package.json +6 -3
  4. package/src/config/defaults/runtime.ts +100 -24
  5. package/src/core/correction-cue-learner.ts +23 -8
  6. package/src/core/event-log.ts +87 -20
  7. package/src/core/init.ts +2 -2
  8. package/src/core/nocturnal-candidate-scoring.ts +6 -6
  9. package/src/core/nocturnal-trinity-types.ts +94 -0
  10. package/src/core/nocturnal-trinity.ts +35 -99
  11. package/src/core/session-tracker.ts +7 -6
  12. package/src/core/system-logger.ts +104 -12
  13. package/src/core/workspace-dir-service.ts +40 -6
  14. package/src/core/workspace-dir-validation.ts +5 -37
  15. package/src/hooks/prompt.ts +3 -3
  16. package/src/hooks/trajectory-collector.ts +7 -7
  17. package/src/index.ts +8 -68
  18. package/src/service/central-sync-service.ts +3 -8
  19. package/src/service/correction-observer-workflow-manager.ts +2 -2
  20. package/src/service/evolution-worker.ts +13 -22
  21. package/src/service/keyword-optimization-service.ts +2 -2
  22. package/src/service/nocturnal-service.ts +62 -43
  23. package/src/service/subagent-workflow/correction-observer-types.ts +69 -0
  24. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +246 -0
  25. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +4 -4
  26. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +4 -4
  27. package/src/service/subagent-workflow/index.ts +13 -0
  28. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +2 -2
  29. package/src/service/subagent-workflow/types.ts +69 -3
  30. package/src/utils/shadow-fingerprint.ts +42 -0
  31. package/src/utils/workspace-resolver.ts +54 -0
  32. package/tests/core/correction-cue-learner.test.ts +345 -0
  33. package/tests/core/workspace-dir-validation.test.ts +1 -1
  34. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +3 -3
  35. 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 './evolution-worker.js';
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
- // eslint-disable-next-line @typescript-eslint/init-declarations
310
+
303
311
  let badDecision: string;
304
- // eslint-disable-next-line @typescript-eslint/init-declarations
312
+
305
313
  let betterDecision: string;
306
- // eslint-disable-next-line @typescript-eslint/init-declarations
314
+
307
315
  let rationale: string;
308
316
 
309
317
  if (hasGateBlocks && snapshot.gateBlocks.length > 0) {
310
318
  // Use actual gate block content
311
- // eslint-disable-next-line @typescript-eslint/prefer-destructuring
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
- // eslint-disable-next-line @typescript-eslint/prefer-destructuring
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
- // eslint-disable-next-line @typescript-eslint/max-params
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
- // eslint-disable-next-line @typescript-eslint/max-params
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
- // eslint-disable-next-line @typescript-eslint/max-params
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
- console.warn(`[nocturnal-service] Failed to record run start: ${String(err)}`);
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
- // eslint-disable-next-line no-useless-assignment
821
+
808
822
  let trinityArtifact: TrinityDraftArtifact | null = null;
809
823
  let trinityResult: TrinityResult | null = null;
810
824
 
811
- // eslint-disable-next-line @typescript-eslint/init-declarations
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end (arbiter failed): ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end (executability failed): ${String(err)}`);
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
- // eslint-disable-next-line @typescript-eslint/init-declarations
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
- console.warn(`[nocturnal-service] Failed to record run end (persistence failed): ${String(e)}`);
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
- console.warn(`[nocturnal-service] Failed to register sample in dataset registry: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to append behavioral artifact lineage: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end (success): ${String(err)}`);
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
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
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
- // eslint-disable-next-line @typescript-eslint/init-declarations
1241
+
1223
1242
  let selectedPrincipleId: string | undefined;
1224
1243
 
1225
- // eslint-disable-next-line @typescript-eslint/init-declarations
1244
+
1226
1245
  let selectedSessionId: string | undefined;
1227
1246
 
1228
- // eslint-disable-next-line no-useless-assignment
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
- // eslint-disable-next-line @typescript-eslint/prefer-destructuring
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
- // eslint-disable-next-line @typescript-eslint/prefer-destructuring
1327
+
1309
1328
  selectedPrincipleId = selection.selectedPrincipleId;
1310
1329
 
1311
- // eslint-disable-next-line @typescript-eslint/prefer-destructuring
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
- console.warn(`[nocturnal-service] Failed to record run start: ${String(err)}`);
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
- // eslint-disable-next-line no-useless-assignment
1365
+
1347
1366
  let trinityArtifact: TrinityDraftArtifact | null = null;
1348
1367
  let trinityResult: TrinityResult | null = null;
1349
1368
 
1350
- // eslint-disable-next-line @typescript-eslint/init-declarations
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end (arbiter failed): ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end (executability failed): ${String(err)}`);
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
- // eslint-disable-next-line @typescript-eslint/init-declarations
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
- console.warn(`[nocturnal-service] Failed to record run end (persistence failed): ${String(e)}`);
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
- console.warn(`[nocturnal-service] Failed to register sample in dataset registry: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to append behavioral artifact lineage: ${String(err)}`);
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
- console.warn(`[nocturnal-service] Failed to record run end (success): ${String(err)}`);
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
+ }