principles-disciple 1.18.0 → 1.19.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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/commands/nocturnal-rollout.ts +2 -0
- package/src/core/merge-gate-audit.ts +506 -0
- package/src/core/nocturnal-export.ts +106 -6
- package/src/core/nocturnal-trinity.ts +111 -28
- package/src/core/promotion-gate.ts +33 -0
- package/src/core/replay-engine.ts +25 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +2 -6
- package/tests/core/merge-gate-audit.test.ts +284 -0
- package/tests/core/nocturnal-export.test.ts +55 -0
- package/tests/core/nocturnal-trinity.test.ts +77 -4
- package/tests/core/pain-integration.test.ts +27 -0
- package/tests/core/promotion-gate.test.ts +5 -0
- package/tests/core/replay-engine.test.ts +19 -0
- package/tests/service/nocturnal-workflow-manager.test.ts +2 -0
|
@@ -307,6 +307,17 @@ If you cannot synthesize an artifact:
|
|
|
307
307
|
*/
|
|
308
308
|
|
|
309
309
|
export interface TrinityRuntimeAdapter {
|
|
310
|
+
/**
|
|
311
|
+
* Check if the runtime surface is available for Trinity stage execution.
|
|
312
|
+
* @returns true if the adapter can invoke stages
|
|
313
|
+
*/
|
|
314
|
+
isRuntimeAvailable(): boolean;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get the reason for the last runtime failure, or null if no failure.
|
|
318
|
+
*/
|
|
319
|
+
getLastFailureReason(): string | null;
|
|
320
|
+
|
|
310
321
|
/**
|
|
311
322
|
* Invoke the Dreamer stage.
|
|
312
323
|
* @param snapshot Session trajectory snapshot
|
|
@@ -369,6 +380,29 @@ export interface TrinityRuntimeAdapter {
|
|
|
369
380
|
* Uses api.runtime.agent.runEmbeddedPiAgent() which works in background contexts
|
|
370
381
|
* (unlike api.runtime.subagent.* which requires gateway request scope).
|
|
371
382
|
*/
|
|
383
|
+
export type TrinityRuntimeFailureCode =
|
|
384
|
+
| 'runtime_unavailable'
|
|
385
|
+
| 'invalid_runtime_request'
|
|
386
|
+
| 'runtime_run_failed'
|
|
387
|
+
| 'runtime_timeout'
|
|
388
|
+
| 'runtime_session_read_failed';
|
|
389
|
+
|
|
390
|
+
export class TrinityRuntimeContractError extends Error {
|
|
391
|
+
readonly code: TrinityRuntimeFailureCode;
|
|
392
|
+
readonly diagnostics?: Record<string, unknown>;
|
|
393
|
+
|
|
394
|
+
constructor(
|
|
395
|
+
code: TrinityRuntimeFailureCode,
|
|
396
|
+
message: string,
|
|
397
|
+
diagnostics?: Record<string, unknown>
|
|
398
|
+
) {
|
|
399
|
+
super(`${code}: ${message}`);
|
|
400
|
+
this.name = 'TrinityRuntimeContractError';
|
|
401
|
+
this.code = code;
|
|
402
|
+
this.diagnostics = diagnostics;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
372
406
|
export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
373
407
|
|
|
374
408
|
private readonly api: {
|
|
@@ -396,6 +430,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
396
430
|
config?: unknown;
|
|
397
431
|
logger?: { info: (msg: string) => void; warn: (msg: string) => void; error: (msg: string) => void };
|
|
398
432
|
};
|
|
433
|
+
private lastFailureReason: string | null = null;
|
|
399
434
|
|
|
400
435
|
|
|
401
436
|
private readonly stageTimeoutMs: number;
|
|
@@ -405,6 +440,13 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
405
440
|
api: OpenClawTrinityRuntimeAdapter['api'],
|
|
406
441
|
stageTimeoutMs = 300_000 // 5 min — increased from 3 min to accommodate slower LLM responses
|
|
407
442
|
) {
|
|
443
|
+
if (typeof api?.runtime?.agent?.runEmbeddedPiAgent !== 'function') {
|
|
444
|
+
throw new TrinityRuntimeContractError(
|
|
445
|
+
'runtime_unavailable',
|
|
446
|
+
'embedded runtime unavailable (missing runtime.agent.runEmbeddedPiAgent)',
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
408
450
|
this.api = api;
|
|
409
451
|
this.stageTimeoutMs = stageTimeoutMs;
|
|
410
452
|
// Cross-platform temp directory for session files
|
|
@@ -413,6 +455,14 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
413
455
|
this.cleanupStaleTempDirs();
|
|
414
456
|
}
|
|
415
457
|
|
|
458
|
+
isRuntimeAvailable(): boolean {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
getLastFailureReason(): string | null {
|
|
463
|
+
return this.lastFailureReason;
|
|
464
|
+
}
|
|
465
|
+
|
|
416
466
|
/**
|
|
417
467
|
* Clean up temp directories from previous crashed runs.
|
|
418
468
|
* Matches pattern pd-trinity-* in the OS temp directory.
|
|
@@ -509,11 +559,17 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
509
559
|
.join('\n');
|
|
510
560
|
}
|
|
511
561
|
|
|
562
|
+
private classifyRuntimeError(error: unknown): TrinityRuntimeFailureCode {
|
|
563
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
564
|
+
return /timeout/i.test(detail) ? 'runtime_timeout' : 'runtime_run_failed';
|
|
565
|
+
}
|
|
566
|
+
|
|
512
567
|
async invokeDreamer(
|
|
513
568
|
snapshot: NocturnalSessionSnapshot,
|
|
514
569
|
principleId: string,
|
|
515
570
|
maxCandidates: number
|
|
516
571
|
): Promise<DreamerOutput> {
|
|
572
|
+
this.lastFailureReason = null;
|
|
517
573
|
const runId = `dreamer-${randomUUID()}`;
|
|
518
574
|
const sessionFile = this.createSessionFile('dreamer');
|
|
519
575
|
const prompt = this.buildDreamerPrompt(snapshot, principleId, maxCandidates);
|
|
@@ -537,12 +593,10 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
537
593
|
|
|
538
594
|
const outputText = this.extractPayloadText(result);
|
|
539
595
|
if (!outputText) {
|
|
540
|
-
return
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
generatedAt: new Date().toISOString(),
|
|
545
|
-
};
|
|
596
|
+
return this.buildRuntimeFailureDreamerOutput(
|
|
597
|
+
'runtime_session_read_failed',
|
|
598
|
+
'Dreamer returned empty response',
|
|
599
|
+
);
|
|
546
600
|
}
|
|
547
601
|
|
|
548
602
|
// DEBUG: Log Dreamer's actual output
|
|
@@ -550,12 +604,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
550
604
|
|
|
551
605
|
return this.parseDreamerOutput(outputText);
|
|
552
606
|
} catch (err) {
|
|
553
|
-
return
|
|
554
|
-
valid: false,
|
|
555
|
-
candidates: [],
|
|
556
|
-
reason: `Dreamer failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
557
|
-
generatedAt: new Date().toISOString(),
|
|
558
|
-
};
|
|
607
|
+
return this.buildRuntimeFailureDreamerOutput(this.classifyRuntimeError(err), err);
|
|
559
608
|
} finally {
|
|
560
609
|
try { fs.unlinkSync(sessionFile); } catch { /* ignore */ }
|
|
561
610
|
}
|
|
@@ -566,6 +615,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
566
615
|
principleId: string,
|
|
567
616
|
snapshot: NocturnalSessionSnapshot
|
|
568
617
|
): Promise<PhilosopherOutput> {
|
|
618
|
+
this.lastFailureReason = null;
|
|
569
619
|
const runId = `philosopher-${randomUUID()}`;
|
|
570
620
|
const sessionFile = this.createSessionFile('philosopher');
|
|
571
621
|
const prompt = this.buildPhilosopherPrompt(dreamerOutput, principleId, snapshot);
|
|
@@ -587,13 +637,10 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
587
637
|
|
|
588
638
|
const outputText = this.extractPayloadText(result);
|
|
589
639
|
if (!outputText) {
|
|
590
|
-
return
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
reason: 'Philosopher returned empty response',
|
|
595
|
-
generatedAt: new Date().toISOString(),
|
|
596
|
-
};
|
|
640
|
+
return this.buildRuntimeFailurePhilosopherOutput(
|
|
641
|
+
'runtime_session_read_failed',
|
|
642
|
+
'Philosopher returned empty response',
|
|
643
|
+
);
|
|
597
644
|
}
|
|
598
645
|
|
|
599
646
|
// DEBUG: Log Philosopher's actual output
|
|
@@ -601,13 +648,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
601
648
|
|
|
602
649
|
return this.parsePhilosopherOutput(outputText);
|
|
603
650
|
} catch (err) {
|
|
604
|
-
return
|
|
605
|
-
valid: false,
|
|
606
|
-
judgments: [],
|
|
607
|
-
overallAssessment: '',
|
|
608
|
-
reason: `Philosopher failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
609
|
-
generatedAt: new Date().toISOString(),
|
|
610
|
-
};
|
|
651
|
+
return this.buildRuntimeFailurePhilosopherOutput(this.classifyRuntimeError(err), err);
|
|
611
652
|
} finally {
|
|
612
653
|
try { fs.unlinkSync(sessionFile); } catch { /* ignore */ }
|
|
613
654
|
}
|
|
@@ -623,6 +664,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
623
664
|
|
|
624
665
|
_config: TrinityConfig
|
|
625
666
|
): Promise<TrinityDraftArtifact | null> {
|
|
667
|
+
this.lastFailureReason = null;
|
|
626
668
|
const runId = `scribe-${randomUUID()}`;
|
|
627
669
|
const sessionFile = this.createSessionFile('scribe');
|
|
628
670
|
const prompt = this.buildScribePrompt(dreamerOutput, philosopherOutput, snapshot, principleId);
|
|
@@ -644,6 +686,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
644
686
|
|
|
645
687
|
const outputText = this.extractPayloadText(result);
|
|
646
688
|
if (!outputText) {
|
|
689
|
+
this.recordFailure('runtime_session_read_failed', 'Scribe returned empty response');
|
|
647
690
|
return null;
|
|
648
691
|
}
|
|
649
692
|
|
|
@@ -652,6 +695,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
652
695
|
|
|
653
696
|
return this.parseScribeOutput(outputText, snapshot, principleId, telemetry);
|
|
654
697
|
} catch (err) {
|
|
698
|
+
this.recordFailure(this.classifyRuntimeError(err), err);
|
|
655
699
|
return null;
|
|
656
700
|
} finally {
|
|
657
701
|
try { fs.unlinkSync(sessionFile); } catch { /* ignore */ }
|
|
@@ -966,6 +1010,19 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
966
1010
|
}
|
|
967
1011
|
}
|
|
968
1012
|
|
|
1013
|
+
private buildRuntimeFailureDreamerOutput(
|
|
1014
|
+
code: TrinityRuntimeFailureCode,
|
|
1015
|
+
error: unknown
|
|
1016
|
+
): DreamerOutput {
|
|
1017
|
+
const reason = this.recordFailure(code, error);
|
|
1018
|
+
return {
|
|
1019
|
+
valid: false,
|
|
1020
|
+
candidates: [],
|
|
1021
|
+
reason,
|
|
1022
|
+
generatedAt: new Date().toISOString(),
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
969
1026
|
private parsePhilosopherOutput(text: string): PhilosopherOutput {
|
|
970
1027
|
const json = this.extractJson(text);
|
|
971
1028
|
if (!json) {
|
|
@@ -1016,22 +1073,47 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
1016
1073
|
}
|
|
1017
1074
|
}
|
|
1018
1075
|
|
|
1076
|
+
private buildRuntimeFailurePhilosopherOutput(
|
|
1077
|
+
code: TrinityRuntimeFailureCode,
|
|
1078
|
+
error: unknown
|
|
1079
|
+
): PhilosopherOutput {
|
|
1080
|
+
const reason = this.recordFailure(code, error);
|
|
1081
|
+
return {
|
|
1082
|
+
valid: false,
|
|
1083
|
+
judgments: [],
|
|
1084
|
+
overallAssessment: '',
|
|
1085
|
+
reason,
|
|
1086
|
+
generatedAt: new Date().toISOString(),
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
private recordFailure(
|
|
1091
|
+
code: TrinityRuntimeFailureCode,
|
|
1092
|
+
error: unknown
|
|
1093
|
+
): string {
|
|
1094
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1095
|
+
this.lastFailureReason = `${code}: ${detail}`;
|
|
1096
|
+
return this.lastFailureReason;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1019
1099
|
|
|
1020
1100
|
private parseScribeOutput(
|
|
1021
1101
|
text: string,
|
|
1022
1102
|
snapshot: NocturnalSessionSnapshot,
|
|
1023
1103
|
principleId: string,
|
|
1024
|
-
|
|
1104
|
+
|
|
1025
1105
|
_telemetry: TrinityTelemetry
|
|
1026
1106
|
): TrinityDraftArtifact | null {
|
|
1027
1107
|
const json = this.extractJson(text);
|
|
1028
1108
|
if (!json) {
|
|
1109
|
+
this.recordFailure('runtime_run_failed', new Error('Scribe output contains no parseable JSON'));
|
|
1029
1110
|
return null;
|
|
1030
1111
|
}
|
|
1031
1112
|
|
|
1032
1113
|
try {
|
|
1033
1114
|
const parsed = JSON.parse(json);
|
|
1034
1115
|
if (typeof parsed.selectedCandidateIndex !== 'number') {
|
|
1116
|
+
this.recordFailure('runtime_run_failed', new Error(`Scribe output missing "selectedCandidateIndex" field: ${text.slice(0, 200)}`));
|
|
1035
1117
|
return null;
|
|
1036
1118
|
}
|
|
1037
1119
|
|
|
@@ -1045,7 +1127,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
1045
1127
|
sourceSnapshotRef: `snapshot-${snapshot.sessionId}-${Date.now()}`,
|
|
1046
1128
|
telemetry: {
|
|
1047
1129
|
chainMode: 'trinity',
|
|
1048
|
-
usedStubs:
|
|
1130
|
+
usedStubs: _telemetry.usedStubs,
|
|
1049
1131
|
dreamerPassed: true,
|
|
1050
1132
|
philosopherPassed: true,
|
|
1051
1133
|
scribePassed: true,
|
|
@@ -1055,6 +1137,7 @@ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
|
1055
1137
|
},
|
|
1056
1138
|
};
|
|
1057
1139
|
} catch {
|
|
1140
|
+
this.recordFailure('runtime_run_failed', new Error(`Scribe output JSON parse error: ${json.slice(0, 200)}`));
|
|
1058
1141
|
return null;
|
|
1059
1142
|
}
|
|
1060
1143
|
}
|
|
@@ -277,6 +277,12 @@ export interface PromotionGateResult {
|
|
|
277
277
|
threshold: number;
|
|
278
278
|
passed: boolean;
|
|
279
279
|
};
|
|
280
|
+
|
|
281
|
+
evidenceSummary: {
|
|
282
|
+
evidenceMode: 'shadow' | 'eval-proxy' | 'mixed';
|
|
283
|
+
shadowSampleCount: number;
|
|
284
|
+
deltaSource: 'eval';
|
|
285
|
+
};
|
|
280
286
|
}
|
|
281
287
|
|
|
282
288
|
/**
|
|
@@ -337,6 +343,11 @@ export function evaluatePromotionGate(
|
|
|
337
343
|
blockers,
|
|
338
344
|
constraintChecks: [],
|
|
339
345
|
deltaCheck: { actual: 0, threshold: minDelta, passed: false },
|
|
346
|
+
evidenceSummary: {
|
|
347
|
+
evidenceMode: 'eval-proxy',
|
|
348
|
+
shadowSampleCount: 0,
|
|
349
|
+
deltaSource: 'eval',
|
|
350
|
+
},
|
|
340
351
|
};
|
|
341
352
|
}
|
|
342
353
|
|
|
@@ -351,6 +362,11 @@ export function evaluatePromotionGate(
|
|
|
351
362
|
blockers,
|
|
352
363
|
constraintChecks: [],
|
|
353
364
|
deltaCheck: { actual: 0, threshold: minDelta, passed: false },
|
|
365
|
+
evidenceSummary: {
|
|
366
|
+
evidenceMode: 'eval-proxy',
|
|
367
|
+
shadowSampleCount: 0,
|
|
368
|
+
deltaSource: 'eval',
|
|
369
|
+
},
|
|
354
370
|
};
|
|
355
371
|
}
|
|
356
372
|
|
|
@@ -366,6 +382,11 @@ export function evaluatePromotionGate(
|
|
|
366
382
|
blockers,
|
|
367
383
|
constraintChecks: [],
|
|
368
384
|
deltaCheck: { actual: 0, threshold: minDelta, passed: false },
|
|
385
|
+
evidenceSummary: {
|
|
386
|
+
evidenceMode: 'eval-proxy',
|
|
387
|
+
shadowSampleCount: 0,
|
|
388
|
+
deltaSource: 'eval',
|
|
389
|
+
},
|
|
369
390
|
};
|
|
370
391
|
}
|
|
371
392
|
|
|
@@ -496,12 +517,24 @@ export function evaluatePromotionGate(
|
|
|
496
517
|
suggestedState = 'rejected';
|
|
497
518
|
}
|
|
498
519
|
|
|
520
|
+
const evidenceMode =
|
|
521
|
+
arbiterRejectSource === 'shadow' && executabilityRejectSource === 'shadow'
|
|
522
|
+
? 'shadow'
|
|
523
|
+
: arbiterRejectSource === 'eval-proxy' && executabilityRejectSource === 'eval-proxy'
|
|
524
|
+
? 'eval-proxy'
|
|
525
|
+
: 'mixed';
|
|
526
|
+
|
|
499
527
|
return {
|
|
500
528
|
passes: allPassed,
|
|
501
529
|
suggestedState,
|
|
502
530
|
blockers,
|
|
503
531
|
constraintChecks,
|
|
504
532
|
deltaCheck,
|
|
533
|
+
evidenceSummary: {
|
|
534
|
+
evidenceMode,
|
|
535
|
+
shadowSampleCount: shadowStats?.totalCount ?? 0,
|
|
536
|
+
deltaSource: 'eval',
|
|
537
|
+
},
|
|
505
538
|
};
|
|
506
539
|
}
|
|
507
540
|
|
|
@@ -63,6 +63,15 @@ export interface ReplayReport {
|
|
|
63
63
|
principleAnchor: ClassificationSummary;
|
|
64
64
|
};
|
|
65
65
|
blockers: string[];
|
|
66
|
+
evidenceSummary: {
|
|
67
|
+
evidenceStatus: 'observed' | 'empty';
|
|
68
|
+
totalSamples: number;
|
|
69
|
+
classifiedCounts: {
|
|
70
|
+
painNegative: number;
|
|
71
|
+
successPositive: number;
|
|
72
|
+
principleAnchor: number;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
66
75
|
generatedAt: string;
|
|
67
76
|
implementationId: string;
|
|
68
77
|
sampleFingerprints: string[];
|
|
@@ -432,6 +441,11 @@ export class ReplayEngine {
|
|
|
432
441
|
const successSummary = toSummary(successPositive);
|
|
433
442
|
const anchorSummary = toSummary(principleAnchor);
|
|
434
443
|
const blockers: string[] = [];
|
|
444
|
+
const totalSamples = results.length;
|
|
445
|
+
|
|
446
|
+
if (totalSamples === 0) {
|
|
447
|
+
blockers.push('NO REPLAY EVIDENCE: No classified replay samples were available. Report cannot justify promotion-quality conclusions.');
|
|
448
|
+
}
|
|
435
449
|
|
|
436
450
|
for (const leak of painSummary.details.filter((result) => !result.passed)) {
|
|
437
451
|
blockers.push(
|
|
@@ -459,6 +473,15 @@ export class ReplayEngine {
|
|
|
459
473
|
principleAnchor: anchorSummary,
|
|
460
474
|
},
|
|
461
475
|
blockers,
|
|
476
|
+
evidenceSummary: {
|
|
477
|
+
evidenceStatus: totalSamples > 0 ? 'observed' : 'empty',
|
|
478
|
+
totalSamples,
|
|
479
|
+
classifiedCounts: {
|
|
480
|
+
painNegative: painSummary.total,
|
|
481
|
+
successPositive: successSummary.total,
|
|
482
|
+
principleAnchor: anchorSummary.total,
|
|
483
|
+
},
|
|
484
|
+
},
|
|
462
485
|
generatedAt: new Date().toISOString(),
|
|
463
486
|
implementationId,
|
|
464
487
|
sampleFingerprints: results.map((result) => result.sampleFingerprint),
|
|
@@ -471,6 +494,7 @@ export class ReplayEngine {
|
|
|
471
494
|
success: ClassificationSummary,
|
|
472
495
|
anchor: ClassificationSummary
|
|
473
496
|
): 'pass' | 'fail' | 'needs-review' {
|
|
497
|
+
if (pain.total + success.total + anchor.total === 0) return 'needs-review';
|
|
474
498
|
if (pain.failed > 0) return 'fail';
|
|
475
499
|
if (anchor.failed > 0) return 'fail';
|
|
476
500
|
if (success.failed > 0) return 'needs-review';
|
|
@@ -526,6 +550,7 @@ export function formatReplayReport(report: ReplayReport): string {
|
|
|
526
550
|
output += `Implementation: ${report.implementationId}\n`;
|
|
527
551
|
output += `Generated At: ${report.generatedAt}\n`;
|
|
528
552
|
output += `Overall Decision: [${decisionEmoji}]\n\n`;
|
|
553
|
+
output += `Evidence Status: ${report.evidenceSummary.evidenceStatus} (samples=${report.evidenceSummary.totalSamples})\n\n`;
|
|
529
554
|
|
|
530
555
|
const formatSection = (
|
|
531
556
|
label: string,
|
|
@@ -40,7 +40,6 @@ import type { NocturnalSessionSnapshot } from '../../core/nocturnal-trajectory-e
|
|
|
40
40
|
import type { RecentPainContext } from '../evolution-worker.js';
|
|
41
41
|
import * as fs from 'fs';
|
|
42
42
|
import * as path from 'path';
|
|
43
|
-
import { isSubagentRuntimeAvailable } from '../../utils/subagent-probe.js';
|
|
44
43
|
import { validateNocturnalSnapshotIngress } from '../../core/nocturnal-snapshot-contract.js';
|
|
45
44
|
|
|
46
45
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -173,11 +172,8 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
173
172
|
metadata?: Record<string, unknown>;
|
|
174
173
|
}
|
|
175
174
|
): Promise<WorkflowHandle> {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: TrinityRuntimeAdapter interface doesn't expose api.runtime.subagent, but OpenClawTrinityRuntimeAdapter has it
|
|
179
|
-
const subagent = (this.runtimeAdapter as any).api?.runtime?.subagent;
|
|
180
|
-
if (!isSubagentRuntimeAvailable(subagent)) {
|
|
175
|
+
const runtimeAvailable = this.runtimeAdapter.isRuntimeAvailable();
|
|
176
|
+
if (!runtimeAvailable) {
|
|
181
177
|
this.logger.warn(`[PD:NocturnalWorkflow] Subagent runtime unavailable, skipping workflow`);
|
|
182
178
|
throw new Error(`NocturnalWorkflowManager: subagent runtime unavailable`);
|
|
183
179
|
}
|