principles-disciple 1.28.2 → 1.29.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/scripts/acceptance-test.mjs +314 -0
- package/scripts/seed-nocturnal-scenarios.mjs +16 -9
- package/scripts/validate-live-path.ts +141 -17
- package/src/commands/archive-impl.ts +3 -0
- package/src/commands/context.ts +4 -1
- package/src/commands/disable-impl.ts +2 -2
- package/src/commands/evolution-status.ts +2 -2
- package/src/commands/focus.ts +4 -2
- package/src/commands/nocturnal-train.ts +9 -1
- package/src/commands/pain.ts +3 -1
- package/src/commands/pd-reflect.ts +2 -0
- package/src/commands/rollback-impl.ts +5 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +1 -0
- package/src/commands/workflow-debug.ts +1 -0
- package/src/core/adaptive-thresholds.ts +2 -1
- package/src/core/code-implementation-storage.ts +2 -0
- package/src/core/config.ts +1 -0
- package/src/core/diagnostician-task-store.ts +2 -0
- package/src/core/empathy-keyword-matcher.ts +4 -1
- package/src/core/event-log.ts +6 -3
- package/src/core/evolution-engine.ts +4 -1
- package/src/core/evolution-logger.ts +1 -0
- package/src/core/external-training-contract.ts +2 -1
- package/src/core/focus-history.ts +15 -3
- package/src/core/init.ts +3 -1
- package/src/core/merge-gate-audit.ts +3 -0
- package/src/core/model-deployment-registry.ts +1 -0
- package/src/core/model-training-registry.ts +1 -0
- package/src/core/nocturnal-arbiter.ts +4 -3
- package/src/core/nocturnal-candidate-scoring.ts +5 -0
- package/src/core/nocturnal-compliance.ts +22 -1
- package/src/core/nocturnal-dataset.ts +3 -1
- package/src/core/nocturnal-export.ts +5 -0
- package/src/core/nocturnal-reasoning-deriver.ts +6 -1
- package/src/core/nocturnal-snapshot-contract.ts +1 -0
- package/src/core/nocturnal-trinity.ts +24 -3
- package/src/core/pain-context-extractor.ts +3 -1
- package/src/core/pain.ts +3 -1
- package/src/core/path-resolver.ts +10 -4
- package/src/core/pd-task-reconciler.ts +3 -1
- package/src/core/pd-task-store.ts +1 -0
- package/src/core/principle-internalization/deprecated-readiness.ts +2 -1
- package/src/core/principle-training-state.ts +2 -0
- package/src/core/principle-tree-ledger.ts +4 -0
- package/src/core/principle-tree-migration.ts +2 -1
- package/src/core/promotion-gate.ts +7 -1
- package/src/core/replay-engine.ts +10 -4
- package/src/core/risk-calculator.ts +2 -1
- package/src/core/rule-host.ts +3 -2
- package/src/core/session-tracker.ts +5 -2
- package/src/core/shadow-observation-registry.ts +1 -0
- package/src/core/thinking-os-parser.ts +1 -0
- package/src/core/trajectory.ts +9 -5
- package/src/hooks/bash-risk.ts +2 -0
- package/src/hooks/edit-verification.ts +3 -0
- package/src/hooks/gate-block-helper.ts +3 -0
- package/src/hooks/gate.ts +8 -0
- package/src/hooks/gfi-gate.ts +2 -0
- package/src/hooks/lifecycle.ts +1 -0
- package/src/hooks/llm.ts +1 -0
- package/src/hooks/pain.ts +3 -1
- package/src/hooks/progressive-trust-gate.ts +3 -0
- package/src/hooks/prompt.ts +5 -2
- package/src/hooks/subagent.ts +1 -0
- package/src/hooks/thinking-checkpoint.ts +1 -0
- package/src/hooks/trajectory-collector.ts +2 -1
- package/src/http/principles-console-route.ts +5 -2
- package/src/index.ts +7 -0
- package/src/service/central-health-service.ts +1 -0
- package/src/service/central-overview-service.ts +2 -0
- package/src/service/evolution-query-service.ts +1 -0
- package/src/service/evolution-worker.ts +31 -1
- package/src/service/health-query-service.ts +6 -6
- package/src/service/monitoring-query-service.ts +4 -0
- package/src/service/nocturnal-runtime.ts +7 -5
- package/src/service/nocturnal-service.ts +21 -0
- package/src/service/nocturnal-target-selector.ts +2 -0
- package/src/service/runtime-summary-service.ts +6 -5
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +2 -1
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +2 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +3 -2
- package/src/service/subagent-workflow/workflow-manager-base.ts +6 -1
- package/src/service/subagent-workflow/workflow-store.ts +2 -0
- package/src/tools/deep-reflect.ts +9 -0
- package/src/tools/model-index.ts +1 -0
- package/src/tools/write-pain-flag.ts +1 -0
- package/src/utils/file-lock.ts +1 -0
- package/src/utils/io.ts +2 -1
- package/tests/core/nocturnal-e2e.test.ts +10 -0
- package/tests/tools/write-pain-flag.test.ts +29 -13
|
@@ -155,6 +155,7 @@ export class EvolutionQueryService {
|
|
|
155
155
|
* 注意:不关闭 trajectory,因为它是单例由 TrajectoryRegistry 管理
|
|
156
156
|
*/
|
|
157
157
|
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
158
159
|
dispose(): void {
|
|
159
160
|
// EvolutionQueryService 不拥有 trajectory,所以不关闭它
|
|
160
161
|
// trajectory 是由 TrajectoryRegistry 管理的单例
|
|
@@ -427,6 +427,7 @@ export const LOCK_RETRY_DELAY_MS = 50;
|
|
|
427
427
|
export const LOCK_STALE_MS = 30_000;
|
|
428
428
|
|
|
429
429
|
|
|
430
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
430
431
|
export function createEvolutionTaskId(
|
|
431
432
|
source: string,
|
|
432
433
|
score: number,
|
|
@@ -460,6 +461,7 @@ export async function acquireQueueLock(resourcePath: string, logger: PluginLogge
|
|
|
460
461
|
}
|
|
461
462
|
|
|
462
463
|
|
|
464
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
463
465
|
async function requireQueueLock(resourcePath: string, logger: PluginLogger | { warn?: (message: string) => void; info?: (message: string) => void } | undefined, scope: string, lockSuffix: string = EVOLUTION_QUEUE_LOCK_SUFFIX): Promise<() => void> {
|
|
464
466
|
try {
|
|
465
467
|
return await acquireQueueLock(resourcePath, logger, lockSuffix);
|
|
@@ -475,6 +477,7 @@ export function extractEvolutionTaskId(task: string): string | null {
|
|
|
475
477
|
}
|
|
476
478
|
|
|
477
479
|
|
|
480
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
478
481
|
function findRecentDuplicateTask(
|
|
479
482
|
queue: EvolutionQueueItem[],
|
|
480
483
|
source: string,
|
|
@@ -483,12 +486,14 @@ function findRecentDuplicateTask(
|
|
|
483
486
|
reason?: string
|
|
484
487
|
): EvolutionQueueItem | undefined {
|
|
485
488
|
|
|
489
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
486
490
|
const key = normalizePainDedupKey(source, preview, reason);
|
|
487
491
|
return queue.find((task) => {
|
|
488
492
|
if (task.status === 'completed') return false;
|
|
489
493
|
const taskTime = new Date(task.enqueued_at || task.timestamp).getTime();
|
|
490
494
|
if (!Number.isFinite(taskTime) || (now - taskTime) > PAIN_QUEUE_DEDUP_WINDOW_MS) return false;
|
|
491
495
|
|
|
496
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
492
497
|
return normalizePainDedupKey(task.source, task.trigger_text_preview || '', task.reason) === key;
|
|
493
498
|
});
|
|
494
499
|
}
|
|
@@ -542,6 +547,7 @@ function normalizePainDedupKey(source: string, preview: string, reason?: string)
|
|
|
542
547
|
}
|
|
543
548
|
|
|
544
549
|
|
|
550
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
545
551
|
export function hasRecentDuplicateTask(queue: EvolutionQueueItem[], source: string, preview: string, now: number, reason?: string): boolean {
|
|
546
552
|
return !!findRecentDuplicateTask(queue, source, preview, now, reason);
|
|
547
553
|
}
|
|
@@ -657,6 +663,7 @@ function shouldSkipForDedup(
|
|
|
657
663
|
const recentSimilarReflection = hasRecentSimilarReflection(queue, painSourceKey, now);
|
|
658
664
|
|
|
659
665
|
if (recentSimilarReflection) {
|
|
666
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
660
667
|
const completedTime = new Date(recentSimilarReflection.completed_at!).getTime();
|
|
661
668
|
logger?.debug?.(`[PD:EvolutionWorker] Skipping sleep_reflection — similar reflection completed ${Math.round((now - completedTime) / 60000)}min ago (same pain pattern: ${painSourceKey})`);
|
|
662
669
|
return true;
|
|
@@ -668,6 +675,7 @@ function shouldSkipForDedup(
|
|
|
668
675
|
* Load and migrate the evolution queue. Returns empty array if file doesn't exist.
|
|
669
676
|
*/
|
|
670
677
|
function loadEvolutionQueue(queuePath: string): EvolutionQueueItem[] {
|
|
678
|
+
// eslint-disable-next-line no-useless-assignment
|
|
671
679
|
let rawQueue: RawQueueItem[] = [];
|
|
672
680
|
try {
|
|
673
681
|
rawQueue = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
|
|
@@ -681,6 +689,7 @@ function loadEvolutionQueue(queuePath: string): EvolutionQueueItem[] {
|
|
|
681
689
|
/**
|
|
682
690
|
* Build and persist a new sleep_reflection task.
|
|
683
691
|
*/
|
|
692
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
684
693
|
function enqueueNewSleepReflectionTask(
|
|
685
694
|
queue: EvolutionQueueItem[],
|
|
686
695
|
recentPainContext: ReturnType<typeof readRecentPainContext>,
|
|
@@ -752,6 +761,7 @@ interface ParsedPainValues {
|
|
|
752
761
|
}
|
|
753
762
|
|
|
754
763
|
|
|
764
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
755
765
|
async function doEnqueuePainTask(
|
|
756
766
|
wctx: WorkspaceContext, logger: PluginLogger, painFlagPath: string,
|
|
757
767
|
result: WorkerStatusReport['pain_flag'], v: ParsedPainValues,
|
|
@@ -998,6 +1008,7 @@ async function checkPainFlag(wctx: WorkspaceContext, logger: PluginLogger): Prom
|
|
|
998
1008
|
}
|
|
999
1009
|
|
|
1000
1010
|
|
|
1011
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
1001
1012
|
async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogger, eventLog: EventLog, api?: OpenClawPluginApi) {
|
|
1002
1013
|
const queuePath = wctx.resolve('EVOLUTION_QUEUE');
|
|
1003
1014
|
if (!fs.existsSync(queuePath)) {
|
|
@@ -1595,10 +1606,13 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1595
1606
|
logger?.info?.(`[PD:EvolutionWorker] Processing sleep_reflection task ${sleepTask.id}`);
|
|
1596
1607
|
}
|
|
1597
1608
|
|
|
1609
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1598
1610
|
let workflowId: string | undefined;
|
|
1599
1611
|
|
|
1612
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1600
1613
|
let nocturnalManager: NocturnalWorkflowManager;
|
|
1601
1614
|
|
|
1615
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1602
1616
|
let snapshotData: NocturnalSessionSnapshot | undefined;
|
|
1603
1617
|
|
|
1604
1618
|
if (isPollingTask) {
|
|
@@ -1636,11 +1650,13 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1636
1650
|
s => s.failureCount > 0 || s.painEventCount > 0 || s.gateBlockCount > 0
|
|
1637
1651
|
);
|
|
1638
1652
|
if (sessionsWithViolations.length > 0) {
|
|
1653
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1639
1654
|
const targetSession = sessionsWithViolations[0];
|
|
1640
1655
|
logger?.info?.(`[PD:EvolutionWorker] Task ${sleepTask.id} using session with violations: ${targetSession.sessionId} (failed=${targetSession.failureCount}, pain=${targetSession.painEventCount}, gates=${targetSession.gateBlockCount})`);
|
|
1641
1656
|
fullSnapshot = extractor.getNocturnalSessionSnapshot(targetSession.sessionId);
|
|
1642
1657
|
} else if (recentSessions.length > 0) {
|
|
1643
1658
|
// No sessions with violations, use most recent as last resort
|
|
1659
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1644
1660
|
const latestSession = recentSessions[0];
|
|
1645
1661
|
logger?.warn?.(`[PD:EvolutionWorker] Task ${sleepTask.id} no sessions with violations found, using most recent: ${latestSession.sessionId} (failed=${latestSession.failureCount}, pain=${latestSession.painEventCount}, gates=${latestSession.gateBlockCount})`);
|
|
1646
1662
|
fullSnapshot = extractor.getNocturnalSessionSnapshot(latestSession.sessionId);
|
|
@@ -1707,6 +1723,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1707
1723
|
},
|
|
1708
1724
|
});
|
|
1709
1725
|
sleepTask.resultRef = workflowHandle.workflowId;
|
|
1726
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1710
1727
|
workflowId = workflowHandle.workflowId;
|
|
1711
1728
|
}
|
|
1712
1729
|
|
|
@@ -1739,13 +1756,20 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1739
1756
|
const errorReason = lastEvent?.reason ?? 'unknown';
|
|
1740
1757
|
// #219: Include payload details for better diagnostics
|
|
1741
1758
|
let detailedError = `Workflow terminal_error: ${errorReason}`;
|
|
1759
|
+
|
|
1742
1760
|
let payload: unknown = {};
|
|
1761
|
+
|
|
1743
1762
|
try {
|
|
1744
1763
|
payload = lastEvent?.payload ?? {};
|
|
1764
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1745
1765
|
if ((payload as any).skipReason) {
|
|
1766
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1746
1767
|
detailedError += ` (skipReason: ${(payload as any).skipReason})`;
|
|
1768
|
+
|
|
1747
1769
|
}
|
|
1770
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1748
1771
|
if ((payload as any).failures && Array.isArray((payload as any).failures) && (payload as any).failures.length > 0) {
|
|
1772
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1749
1773
|
detailedError += ` | failures: ${((payload as any).failures as string[]).slice(0, 3).join(', ')}`;
|
|
1750
1774
|
}
|
|
1751
1775
|
} catch { /* ignore parse errors */ }
|
|
@@ -1755,9 +1779,12 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1755
1779
|
if (isExpectedSubagentError(errorReason)) {
|
|
1756
1780
|
// #237: Expected unavailability → stub fallback, not hard failure
|
|
1757
1781
|
sleepTask.status = 'completed';
|
|
1782
|
+
|
|
1758
1783
|
sleepTask.completed_at = new Date().toISOString();
|
|
1759
1784
|
sleepTask.resolution = 'stub_fallback';
|
|
1785
|
+
|
|
1760
1786
|
logger?.warn?.(`[PD:EvolutionWorker] sleep_reflection task ${sleepTask.id} background runtime unavailable, using stub fallback: ${errorReason}`);
|
|
1787
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1761
1788
|
} else if ((payload as any).skipReason === 'no_violating_sessions') {
|
|
1762
1789
|
// #244: No meaningful violations found (thin filter) → skip without failure
|
|
1763
1790
|
sleepTask.status = 'completed';
|
|
@@ -1873,7 +1900,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1873
1900
|
}
|
|
1874
1901
|
}
|
|
1875
1902
|
|
|
1876
|
-
|
|
1903
|
+
|
|
1877
1904
|
async function processDetectionQueue(wctx: WorkspaceContext, api: OpenClawPluginApi, eventLog: EventLog) {
|
|
1878
1905
|
const {logger} = api;
|
|
1879
1906
|
try {
|
|
@@ -1929,6 +1956,7 @@ async function processDetectionQueue(wctx: WorkspaceContext, api: OpenClawPlugin
|
|
|
1929
1956
|
// Evolution queue is now the single active pain→principle path
|
|
1930
1957
|
|
|
1931
1958
|
|
|
1959
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
1932
1960
|
export async function registerEvolutionTaskSession(
|
|
1933
1961
|
workspaceResolve: (key: string) => string,
|
|
1934
1962
|
taskId: string,
|
|
@@ -1942,6 +1970,7 @@ export async function registerEvolutionTaskSession(
|
|
|
1942
1970
|
|
|
1943
1971
|
try {
|
|
1944
1972
|
|
|
1973
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1945
1974
|
let rawQueue: RawQueueItem[];
|
|
1946
1975
|
try {
|
|
1947
1976
|
rawQueue = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
|
|
@@ -2009,6 +2038,7 @@ function writeWorkerStatus(stateDir: string, report: WorkerStatusReport): void {
|
|
|
2009
2038
|
}
|
|
2010
2039
|
|
|
2011
2040
|
|
|
2041
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
2012
2042
|
async function processEvolutionQueueWithResult(
|
|
2013
2043
|
wctx: WorkspaceContext,
|
|
2014
2044
|
logger: PluginLogger,
|
|
@@ -337,7 +337,7 @@ export class HealthQueryService {
|
|
|
337
337
|
}));
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
|
|
341
341
|
getGateStats(): {
|
|
342
342
|
today: {
|
|
343
343
|
gfiBlocks: number;
|
|
@@ -659,7 +659,7 @@ export class HealthQueryService {
|
|
|
659
659
|
return null;
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
-
|
|
662
|
+
|
|
663
663
|
private readNocturnalTraining(): {
|
|
664
664
|
queue: { pending: number; inProgress: number; completed: number };
|
|
665
665
|
trinityRecords: { artifactId: string; status: string; createdAt: string }[];
|
|
@@ -787,8 +787,8 @@ export class HealthQueryService {
|
|
|
787
787
|
}
|
|
788
788
|
|
|
789
789
|
|
|
790
|
-
|
|
791
|
-
// eslint-disable-next-line
|
|
790
|
+
|
|
791
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this -- complexity 15, refactor candidate
|
|
792
792
|
private getEventDedupKey(entry: EventLogEntry): string {
|
|
793
793
|
const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
|
|
794
794
|
if (eventId) {
|
|
@@ -859,7 +859,7 @@ export class HealthQueryService {
|
|
|
859
859
|
}
|
|
860
860
|
|
|
861
861
|
|
|
862
|
-
|
|
862
|
+
|
|
863
863
|
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
864
864
|
private resolveGateType(row: GateBlockRow): string {
|
|
865
865
|
if (typeof row.gate_type === 'string' && row.gate_type.trim().length > 0) {
|
|
@@ -994,7 +994,7 @@ export class HealthQueryService {
|
|
|
994
994
|
* Read the most recent session JSON file from disk.
|
|
995
995
|
* Used to sync GFI from session-tracker's persistence into SQLite.
|
|
996
996
|
*/
|
|
997
|
-
|
|
997
|
+
|
|
998
998
|
private readLatestSessionFromFile(): SessionState | null {
|
|
999
999
|
const sessionsDir = path.join(this.stateDir, 'sessions');
|
|
1000
1000
|
if (!fs.existsSync(sessionsDir)) {
|
|
@@ -36,6 +36,7 @@ export class MonitoringQueryService {
|
|
|
36
36
|
const now = Date.now();
|
|
37
37
|
const workflowsWithStuckDetection = workflows.map(wf => {
|
|
38
38
|
// Parse metadata for timeout configuration
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
39
40
|
const metadata = parseWorkflowMetadata(wf.metadata_json);
|
|
40
41
|
const timeoutMs = metadata.timeoutMs ?? 15 * 60 * 1000; // Default 15 minutes
|
|
41
42
|
|
|
@@ -84,8 +85,10 @@ export class MonitoringQueryService {
|
|
|
84
85
|
|
|
85
86
|
// Determine status
|
|
86
87
|
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
87
89
|
let status: 'pending' | 'running' | 'completed' | 'failed';
|
|
88
90
|
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
89
92
|
let reason: string | undefined;
|
|
90
93
|
|
|
91
94
|
if (!startEvent) {
|
|
@@ -107,6 +110,7 @@ export class MonitoringQueryService {
|
|
|
107
110
|
|
|
108
111
|
// Calculate duration if stage started and completed/failed
|
|
109
112
|
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
110
114
|
let duration: number | undefined;
|
|
111
115
|
if (startEvent && (completeEvent || failedEvent)) {
|
|
112
116
|
const endEvent = completeEvent || failedEvent;
|
|
@@ -40,7 +40,7 @@ import { withLockAsync } from '../utils/file-lock.js';
|
|
|
40
40
|
* Excluded (NOT system sessions):
|
|
41
41
|
* - User sessions like agent:main:feishu:user:xxx — third component is channel type
|
|
42
42
|
*/
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
function isSystemSession(state: SessionState): boolean {
|
|
45
45
|
const { sessionId, sessionKey, trigger } = state;
|
|
46
46
|
|
|
@@ -270,7 +270,7 @@ async function writeState(stateDir: string, state: NocturnalRuntimeState): Promi
|
|
|
270
270
|
* @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
|
|
271
271
|
* @returns IdleCheckResult with full diagnostic information
|
|
272
272
|
*/
|
|
273
|
-
|
|
273
|
+
|
|
274
274
|
export function checkWorkspaceIdle(
|
|
275
275
|
workspaceDir: string,
|
|
276
276
|
options: {
|
|
@@ -321,6 +321,7 @@ export function checkWorkspaceIdle(
|
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
324
325
|
let reason: string;
|
|
325
326
|
if (mostRecentActivityAt === 0) {
|
|
326
327
|
reason = 'No active sessions found — workspace is idle';
|
|
@@ -357,7 +358,7 @@ export function checkWorkspaceIdle(
|
|
|
357
358
|
* @param options - Cooldown configuration options
|
|
358
359
|
* @returns CooldownCheckResult
|
|
359
360
|
*/
|
|
360
|
-
|
|
361
|
+
|
|
361
362
|
export function checkCooldown(
|
|
362
363
|
stateDir: string,
|
|
363
364
|
principleId?: string,
|
|
@@ -369,7 +370,7 @@ export function checkCooldown(
|
|
|
369
370
|
} = {}
|
|
370
371
|
): CooldownCheckResult {
|
|
371
372
|
const {
|
|
372
|
-
|
|
373
|
+
|
|
373
374
|
globalCooldownMs: _globalCooldownMs = DEFAULT_GLOBAL_COOLDOWN_MS,
|
|
374
375
|
principleCooldownMs: _principleCooldownMs = DEFAULT_PRINCIPLE_COOLDOWN_MS,
|
|
375
376
|
maxRunsPerWindow = DEFAULT_MAX_RUNS_PER_WINDOW,
|
|
@@ -389,6 +390,7 @@ export function checkCooldown(
|
|
|
389
390
|
if (cooldownEnd > now) {
|
|
390
391
|
globalCooldownActive = true;
|
|
391
392
|
globalCooldownRemainingMs = cooldownEnd - now;
|
|
393
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
392
394
|
globalCooldownUntil = state.globalCooldownUntil;
|
|
393
395
|
}
|
|
394
396
|
}
|
|
@@ -558,7 +560,7 @@ export interface PreflightCheckResult {
|
|
|
558
560
|
* @param idleCheckOverride - Optional override for idle check result (for testing)
|
|
559
561
|
*/
|
|
560
562
|
|
|
561
|
-
// eslint-disable-next-line
|
|
563
|
+
// eslint-disable-next-line @typescript-eslint/max-params -- complexity 12, refactor candidate
|
|
562
564
|
export function checkPreflight(
|
|
563
565
|
workspaceDir: string,
|
|
564
566
|
stateDir: string,
|
|
@@ -289,12 +289,16 @@ function invokeStubReflector(
|
|
|
289
289
|
const hasPain = snapshot.stats.totalPainEvents > 0;
|
|
290
290
|
const hasFailures = (snapshot.stats.failureCount ?? 0) > 0;
|
|
291
291
|
|
|
292
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
292
293
|
let badDecision: string;
|
|
294
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
293
295
|
let betterDecision: string;
|
|
296
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
294
297
|
let rationale: string;
|
|
295
298
|
|
|
296
299
|
if (hasGateBlocks && snapshot.gateBlocks.length > 0) {
|
|
297
300
|
// Use actual gate block content
|
|
301
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
298
302
|
const block = snapshot.gateBlocks[0];
|
|
299
303
|
const tool = block.toolName ?? 'a tool';
|
|
300
304
|
const file = block.filePath ? ` on ${block.filePath}` : '';
|
|
@@ -303,6 +307,7 @@ function invokeStubReflector(
|
|
|
303
307
|
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.`;
|
|
304
308
|
} else if (hasPain && snapshot.painEvents.length > 0) {
|
|
305
309
|
// Use actual pain event content
|
|
310
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
306
311
|
const pain = snapshot.painEvents[0];
|
|
307
312
|
const painSource = pain.source ?? 'unknown';
|
|
308
313
|
const painReason = pain.reason ? `: ${pain.reason}` : '';
|
|
@@ -398,6 +403,7 @@ function buildGateBlockRefs(snapshot: NocturnalSessionSnapshot): string[] {
|
|
|
398
403
|
}
|
|
399
404
|
|
|
400
405
|
|
|
406
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
401
407
|
function buildDefaultArtificerOutput(
|
|
402
408
|
ruleId: string,
|
|
403
409
|
artifact: NocturnalArtifact,
|
|
@@ -447,6 +453,7 @@ function buildDefaultArtificerOutput(
|
|
|
447
453
|
}
|
|
448
454
|
|
|
449
455
|
|
|
456
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
450
457
|
function persistCodeCandidate(
|
|
451
458
|
workspaceDir: string,
|
|
452
459
|
stateDir: string,
|
|
@@ -543,6 +550,7 @@ function persistCodeCandidate(
|
|
|
543
550
|
}
|
|
544
551
|
|
|
545
552
|
|
|
553
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
546
554
|
function maybePersistArtificerCandidate(
|
|
547
555
|
workspaceDir: string,
|
|
548
556
|
stateDir: string,
|
|
@@ -784,9 +792,11 @@ export function executeNocturnalReflection(
|
|
|
784
792
|
// Step 5: Artifact generation (Trinity or single-reflector)
|
|
785
793
|
// -------------------------------------------------------------------------
|
|
786
794
|
|
|
795
|
+
// eslint-disable-next-line no-useless-assignment
|
|
787
796
|
let trinityArtifact: TrinityDraftArtifact | null = null;
|
|
788
797
|
let trinityResult: TrinityResult | null = null;
|
|
789
798
|
|
|
799
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
790
800
|
let rawJson: string;
|
|
791
801
|
|
|
792
802
|
if (options.skipReflector) {
|
|
@@ -1011,6 +1021,7 @@ export function executeNocturnalReflection(
|
|
|
1011
1021
|
};
|
|
1012
1022
|
|
|
1013
1023
|
|
|
1024
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1014
1025
|
let persistedPath: string;
|
|
1015
1026
|
try {
|
|
1016
1027
|
persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
|
|
@@ -1138,6 +1149,7 @@ export async function executeNocturnalReflectionAsync(
|
|
|
1138
1149
|
// If runtime adapter is provided, use async Trinity path
|
|
1139
1150
|
if (options.runtimeAdapter) {
|
|
1140
1151
|
|
|
1152
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
1141
1153
|
return executeNocturnalReflectionWithAdapter(workspaceDir, stateDir, options);
|
|
1142
1154
|
}
|
|
1143
1155
|
|
|
@@ -1188,10 +1200,13 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1188
1200
|
|
|
1189
1201
|
// Step 2: Target selection (or use override to skip)
|
|
1190
1202
|
|
|
1203
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1191
1204
|
let selectedPrincipleId: string | undefined;
|
|
1192
1205
|
|
|
1206
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1193
1207
|
let selectedSessionId: string | undefined;
|
|
1194
1208
|
|
|
1209
|
+
// eslint-disable-next-line no-useless-assignment
|
|
1195
1210
|
let snapshot: NocturnalSessionSnapshot | null = null;
|
|
1196
1211
|
|
|
1197
1212
|
if (options.principleIdOverride && options.snapshotOverride) {
|
|
@@ -1214,6 +1229,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1214
1229
|
// Skip Selector: use provided principleId and snapshot directly
|
|
1215
1230
|
selectedPrincipleId = options.principleIdOverride;
|
|
1216
1231
|
selectedSessionId = snapshotValidation.snapshot.sessionId;
|
|
1232
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1217
1233
|
snapshot = snapshotValidation.snapshot;
|
|
1218
1234
|
// Calculate violation density from snapshot stats for meaningful diagnostics
|
|
1219
1235
|
const snapStats = snapshotValidation.snapshot.stats;
|
|
@@ -1270,8 +1286,10 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1270
1286
|
}
|
|
1271
1287
|
|
|
1272
1288
|
|
|
1289
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1273
1290
|
selectedPrincipleId = selection.selectedPrincipleId;
|
|
1274
1291
|
|
|
1292
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1275
1293
|
selectedSessionId = selection.selectedSessionId;
|
|
1276
1294
|
|
|
1277
1295
|
if (!selectedPrincipleId || !selectedSessionId) {
|
|
@@ -1305,9 +1323,11 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1305
1323
|
|
|
1306
1324
|
// Step 4: Trinity execution via adapter (async)
|
|
1307
1325
|
|
|
1326
|
+
// eslint-disable-next-line no-useless-assignment
|
|
1308
1327
|
let trinityArtifact: TrinityDraftArtifact | null = null;
|
|
1309
1328
|
let trinityResult: TrinityResult | null = null;
|
|
1310
1329
|
|
|
1330
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1311
1331
|
let rawJson: string;
|
|
1312
1332
|
|
|
1313
1333
|
if (options.skipReflector) {
|
|
@@ -1414,6 +1434,7 @@ async function executeNocturnalReflectionWithAdapter(
|
|
|
1414
1434
|
// Step 7: Persist artifact
|
|
1415
1435
|
const artifactWithBoundedAction = { ...arbiterResult.artifact, boundedAction: execResult.boundedAction };
|
|
1416
1436
|
|
|
1437
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
1417
1438
|
let persistedPath: string;
|
|
1418
1439
|
try {
|
|
1419
1440
|
persistedPath = persistArtifact(workspaceDir, artifactWithBoundedAction);
|
|
@@ -287,6 +287,7 @@ export class NocturnalTargetSelector {
|
|
|
287
287
|
};
|
|
288
288
|
|
|
289
289
|
|
|
290
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
290
291
|
constructor(
|
|
291
292
|
workspaceDir: string,
|
|
292
293
|
stateDir: string,
|
|
@@ -528,6 +529,7 @@ export class NocturnalTargetSelector {
|
|
|
528
529
|
* This is a convenience wrapper for the common case.
|
|
529
530
|
*/
|
|
530
531
|
|
|
532
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
531
533
|
export function selectNocturnalTarget(
|
|
532
534
|
workspaceDir: string,
|
|
533
535
|
stateDir: string,
|
|
@@ -362,7 +362,7 @@ export class RuntimeSummaryService {
|
|
|
362
362
|
return { session: sessions[0], reason: 'latest_active' };
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
|
|
366
366
|
private static mergeSessionSnapshots(
|
|
367
367
|
persistedSessions: PersistedSessionState[],
|
|
368
368
|
workspaceDir: string
|
|
@@ -420,6 +420,7 @@ export class RuntimeSummaryService {
|
|
|
420
420
|
* Queue is the only authoritative execution truth source.
|
|
421
421
|
*/
|
|
422
422
|
|
|
423
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
423
424
|
private static buildDirectiveSummary(
|
|
424
425
|
queue: QueueItem[] | null,
|
|
425
426
|
directive: DirectiveFile | null,
|
|
@@ -511,9 +512,9 @@ export class RuntimeSummaryService {
|
|
|
511
512
|
})
|
|
512
513
|
.slice(-MAX_SOURCE_EVENTS)
|
|
513
514
|
.reverse();
|
|
514
|
-
|
|
515
|
+
|
|
515
516
|
|
|
516
|
-
|
|
517
|
+
|
|
517
518
|
return filtered.map((entry) => {
|
|
518
519
|
if (entry.type === 'pain_signal') {
|
|
519
520
|
return {
|
|
@@ -584,7 +585,7 @@ export class RuntimeSummaryService {
|
|
|
584
585
|
return [...merged.values()].sort((a, b) => (a.ts || '').localeCompare(b.ts || ''));
|
|
585
586
|
}
|
|
586
587
|
|
|
587
|
-
|
|
588
|
+
|
|
588
589
|
private static getEventDedupKey(entry: EventLogEntry): string {
|
|
589
590
|
const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
|
|
590
591
|
if (eventId) {
|
|
@@ -641,7 +642,7 @@ export class RuntimeSummaryService {
|
|
|
641
642
|
return candidate ? new Date(candidate).getTime() : NaN;
|
|
642
643
|
}
|
|
643
644
|
|
|
644
|
-
|
|
645
|
+
|
|
645
646
|
private static buildDirectiveTaskPreview(item: QueueItem): string {
|
|
646
647
|
const task = typeof item.task === 'string' ? item.task.trim() : '';
|
|
647
648
|
if (task && task.toLowerCase() !== 'undefined') {
|
|
@@ -70,6 +70,7 @@ export class DeepReflectWorkflowManager extends WorkflowManagerBase {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
73
74
|
protected override generateWorkflowId(): string {
|
|
74
75
|
return `wf_dr_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
75
76
|
}
|
|
@@ -150,7 +151,7 @@ export const deepReflectWorkflowSpec: SubagentWorkflowSpec<DeepReflectResult> =
|
|
|
150
151
|
};
|
|
151
152
|
},
|
|
152
153
|
|
|
153
|
-
|
|
154
|
+
|
|
154
155
|
async persistResult(ctx: WorkflowPersistContext<DeepReflectResult>): Promise<void> {
|
|
155
156
|
const { result, metadata, workspaceDir } = ctx;
|
|
156
157
|
|
|
@@ -71,6 +71,7 @@ export class EmpathyObserverWorkflowManager extends WorkflowManagerBase {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
74
75
|
protected override createWorkflowMetadata<TResult>(
|
|
75
76
|
spec: SubagentWorkflowSpec<TResult>,
|
|
76
77
|
options: {
|
|
@@ -104,6 +105,7 @@ export class EmpathyObserverWorkflowManager extends WorkflowManagerBase {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
107
109
|
protected override generateWorkflowId(): string {
|
|
108
110
|
return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
109
111
|
}
|
|
@@ -243,7 +243,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
243
243
|
// #213: Wrap fire-and-forget Promise with .catch() to prevent
|
|
244
244
|
// unhandled promise rejections if anything throws outside the try-catch
|
|
245
245
|
// (e.g., during parameter construction or environment errors).
|
|
246
|
-
|
|
246
|
+
|
|
247
247
|
Promise.resolve().then(async () => {
|
|
248
248
|
try {
|
|
249
249
|
const result = await executeNocturnalReflectionAsync(
|
|
@@ -372,6 +372,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
|
|
375
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
375
376
|
async notifyLifecycleEvent(
|
|
376
377
|
|
|
377
378
|
_workflowId: string,
|
|
@@ -652,7 +653,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
652
653
|
* Derives stage events from TrinityResult.telemetry and TrinityResult.failures.
|
|
653
654
|
* Always records _start event for each stage that ran, plus _complete or _failed based on outcome.
|
|
654
655
|
*/
|
|
655
|
-
|
|
656
|
+
|
|
656
657
|
private recordStageEvents(workflowId: string, result: TrinityResult): void {
|
|
657
658
|
const { telemetry, failures } = result;
|
|
658
659
|
|
|
@@ -156,6 +156,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
|
|
|
156
156
|
* Subclasses override to add type-specific fields.
|
|
157
157
|
*/
|
|
158
158
|
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
159
160
|
protected createWorkflowMetadata<TResult>(
|
|
160
161
|
spec: SubagentWorkflowSpec<TResult>,
|
|
161
162
|
options: {
|
|
@@ -182,6 +183,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
|
|
|
182
183
|
* Subclasses override to call store.createWorkflow() with type-specific metadata.
|
|
183
184
|
*/
|
|
184
185
|
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
185
187
|
protected async createWorkflowRecord<TResult>(
|
|
186
188
|
workflowId: string,
|
|
187
189
|
childSessionKey: string,
|
|
@@ -214,6 +216,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
|
|
|
214
216
|
// ── Protected Helpers ────────────────────────────────────────────────────
|
|
215
217
|
|
|
216
218
|
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
217
220
|
protected buildRunParams<TResult>(
|
|
218
221
|
spec: SubagentWorkflowSpec<TResult>,
|
|
219
222
|
options: {
|
|
@@ -312,6 +315,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
|
|
|
312
315
|
error?: string
|
|
313
316
|
): Promise<void> {
|
|
314
317
|
|
|
318
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
315
319
|
let workflow;
|
|
316
320
|
try {
|
|
317
321
|
workflow = this.store.getWorkflow(workflowId);
|
|
@@ -449,7 +453,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
|
|
|
449
453
|
}
|
|
450
454
|
}
|
|
451
455
|
|
|
452
|
-
|
|
456
|
+
|
|
453
457
|
async sweepExpiredWorkflows(maxAgeMs?: number): Promise<number> {
|
|
454
458
|
const ttl = maxAgeMs ?? this.defaultTtlMs;
|
|
455
459
|
const expired = this.store.getExpiredWorkflows(ttl);
|
|
@@ -523,6 +527,7 @@ export abstract class WorkflowManagerBase implements WorkflowManager {
|
|
|
523
527
|
|
|
524
528
|
// ── Private Helpers ───────────────────────────────────────────────────────
|
|
525
529
|
|
|
530
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
526
531
|
protected generateWorkflowId(): string {
|
|
527
532
|
// Subclasses override the prefix part via wf_ prefix pattern
|
|
528
533
|
return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
@@ -233,6 +233,7 @@ export class WorkflowStore {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
|
|
236
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
236
237
|
recordEvent(
|
|
237
238
|
workflowId: string,
|
|
238
239
|
eventType: string,
|
|
@@ -269,6 +270,7 @@ export class WorkflowStore {
|
|
|
269
270
|
* same idempotency_key already exists, this is a no-op (idempotent).
|
|
270
271
|
*/
|
|
271
272
|
|
|
273
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
272
274
|
recordStageOutput(
|
|
273
275
|
workflowId: string,
|
|
274
276
|
stage: 'dreamer' | 'philosopher',
|