principles-disciple 1.14.0 → 1.16.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 +4 -2
- package/scripts/bootstrap-rules.mjs +66 -0
- package/scripts/validate-live-path.ts +356 -0
- package/src/core/bootstrap-rules.ts +177 -0
- package/src/core/principle-tree-migration.ts +196 -0
- package/src/service/evolution-worker.ts +81 -61
- package/src/service/monitoring-query-service.ts +277 -0
- package/src/service/nocturnal-service.ts +9 -1
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +10 -2
- package/tests/core/bootstrap-rules.test.ts +582 -0
- package/tests/core/principle-tree-migration.test.ts +77 -0
- package/tests/scripts/validate-live-path.test.ts +286 -0
- package/tests/service/evolution-worker.nocturnal.test.ts +208 -0
- package/tests/service/monitoring-query-service.test.ts +113 -0
- package/tests/service/nocturnal-runtime-hardening.test.ts +85 -0
- package/ui/src/charts.tsx +4 -1
- package/ui/src/pages/ThinkingModelsPage.tsx +9 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
const { mockExecuteNocturnalReflectionAsync } = vi.hoisted(() => ({
|
|
7
|
+
mockExecuteNocturnalReflectionAsync: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
vi.mock('../../src/service/nocturnal-service.js', () => ({
|
|
11
|
+
executeNocturnalReflectionAsync: mockExecuteNocturnalReflectionAsync,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
vi.mock('../../src/utils/subagent-probe.js', () => ({
|
|
15
|
+
isSubagentRuntimeAvailable: vi.fn(() => true),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
vi.mock('../../src/core/nocturnal-paths.js', () => ({
|
|
19
|
+
resolveNocturnalDir: vi.fn((workspaceDir: string) => path.join(workspaceDir, '.state', 'nocturnal', 'samples')),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
import { NocturnalWorkflowManager, nocturnalWorkflowSpec } from '../../src/service/subagent-workflow/nocturnal-workflow-manager.js';
|
|
23
|
+
import { safeRmDir } from '../test-utils.js';
|
|
24
|
+
|
|
25
|
+
describe('NocturnalWorkflowManager runtime hardening', () => {
|
|
26
|
+
let workspaceDir: string;
|
|
27
|
+
let stateDir: string;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-nocturnal-wf-'));
|
|
32
|
+
stateDir = path.join(workspaceDir, '.state');
|
|
33
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
safeRmDir(workspaceDir);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('marks workflow terminal_error when async pipeline throws a gateway-only runtime error', async () => {
|
|
41
|
+
mockExecuteNocturnalReflectionAsync.mockRejectedValue(
|
|
42
|
+
new Error('Plugin runtime subagent methods are only available during a gateway request.')
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const manager = new NocturnalWorkflowManager({
|
|
46
|
+
workspaceDir,
|
|
47
|
+
stateDir,
|
|
48
|
+
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() } as any,
|
|
49
|
+
runtimeAdapter: {} as any,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const handle = await manager.startWorkflow(nocturnalWorkflowSpec, {
|
|
53
|
+
parentSessionId: 'sleep_reflection:test',
|
|
54
|
+
taskInput: {},
|
|
55
|
+
metadata: {
|
|
56
|
+
snapshot: {
|
|
57
|
+
sessionId: 'session-1',
|
|
58
|
+
startedAt: '2026-04-10T00:00:00.000Z',
|
|
59
|
+
updatedAt: '2026-04-10T00:01:00.000Z',
|
|
60
|
+
assistantTurns: [],
|
|
61
|
+
userTurns: [],
|
|
62
|
+
toolCalls: [],
|
|
63
|
+
painEvents: [],
|
|
64
|
+
gateBlocks: [],
|
|
65
|
+
stats: {
|
|
66
|
+
totalAssistantTurns: 1,
|
|
67
|
+
totalToolCalls: 1,
|
|
68
|
+
totalPainEvents: 0,
|
|
69
|
+
totalGateBlocks: 0,
|
|
70
|
+
failureCount: 0,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await Promise.resolve();
|
|
77
|
+
await Promise.resolve();
|
|
78
|
+
|
|
79
|
+
const summary = await manager.getWorkflowDebugSummary(handle.workflowId);
|
|
80
|
+
expect(summary?.state).toBe('terminal_error');
|
|
81
|
+
expect(summary?.recentEvents.some((event) => event.eventType === 'nocturnal_failed')).toBe(true);
|
|
82
|
+
|
|
83
|
+
manager.dispose();
|
|
84
|
+
});
|
|
85
|
+
});
|
package/ui/src/charts.tsx
CHANGED
|
@@ -861,6 +861,7 @@ interface LineChartProps {
|
|
|
861
861
|
showDots?: boolean;
|
|
862
862
|
showArea?: boolean;
|
|
863
863
|
unit?: string;
|
|
864
|
+
emptyText?: string;
|
|
864
865
|
}
|
|
865
866
|
|
|
866
867
|
export function LineChart({
|
|
@@ -872,11 +873,13 @@ export function LineChart({
|
|
|
872
873
|
showDots = true,
|
|
873
874
|
showArea = true,
|
|
874
875
|
unit = '',
|
|
876
|
+
emptyText = '',
|
|
875
877
|
}: LineChartProps) {
|
|
876
878
|
if (!data || data.length === 0) {
|
|
879
|
+
if (!emptyText) return null;
|
|
877
880
|
return (
|
|
878
881
|
<div style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--text-secondary)', fontSize: '0.85rem' }}>
|
|
879
|
-
|
|
882
|
+
{emptyText}
|
|
880
883
|
</div>
|
|
881
884
|
);
|
|
882
885
|
}
|
|
@@ -256,7 +256,7 @@ export function ThinkingModelsPage() {
|
|
|
256
256
|
/* ── Has data: full dashboard ── */
|
|
257
257
|
<>
|
|
258
258
|
{/* Coverage Trend */}
|
|
259
|
-
{data.coverageTrend.length >= 1
|
|
259
|
+
{data.coverageTrend.length >= 1 ? (
|
|
260
260
|
<section className="panel" style={{ marginBottom: SPACE[4] }}>
|
|
261
261
|
<h3 className="section-title">
|
|
262
262
|
{t('thinkingModels.coverageTrend')}
|
|
@@ -269,8 +269,14 @@ export function ThinkingModelsPage() {
|
|
|
269
269
|
showGrid
|
|
270
270
|
showDots
|
|
271
271
|
showArea
|
|
272
|
+
emptyText={t('common.noData')}
|
|
272
273
|
/>
|
|
273
274
|
</section>
|
|
275
|
+
) : (
|
|
276
|
+
<EmptyState
|
|
277
|
+
title={t('thinkingModels.emptyCoverageTrend')}
|
|
278
|
+
description={t('thinkingModels.emptyCoverageTrendDesc')}
|
|
279
|
+
/>
|
|
274
280
|
)}
|
|
275
281
|
|
|
276
282
|
{/* Search + Sort + Filter */}
|
|
@@ -445,6 +451,7 @@ export function ThinkingModelsPage() {
|
|
|
445
451
|
showGrid={false}
|
|
446
452
|
showDots
|
|
447
453
|
showArea
|
|
454
|
+
emptyText={t('common.noData')}
|
|
448
455
|
/>
|
|
449
456
|
</article>
|
|
450
457
|
);
|
|
@@ -511,6 +518,7 @@ export function ThinkingModelsPage() {
|
|
|
511
518
|
showGrid
|
|
512
519
|
showDots
|
|
513
520
|
showArea
|
|
521
|
+
emptyText={t('common.noData')}
|
|
514
522
|
/>
|
|
515
523
|
</article>
|
|
516
524
|
) : (
|