principles-disciple 1.32.0 → 1.34.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 (37) hide show
  1. package/openclaw.plugin.json +4 -4
  2. package/package.json +1 -1
  3. package/src/core/correction-cue-learner.ts +203 -0
  4. package/src/core/correction-types.ts +88 -0
  5. package/src/core/evolution-logger.ts +3 -3
  6. package/src/core/init.ts +67 -0
  7. package/src/service/correction-observer-types.ts +58 -0
  8. package/src/service/correction-observer-workflow-manager.ts +218 -0
  9. package/src/service/evolution-worker.ts +172 -146
  10. package/src/service/nocturnal-service.ts +4 -1
  11. package/src/service/subagent-workflow/index.ts +14 -0
  12. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +3 -1
  13. package/tests/service/evolution-worker.nocturnal.test.ts +14 -1
  14. package/tests/service/evolution-worker.timeout.test.ts +350 -0
  15. package/tests/commands/implementation-lifecycle.test.ts +0 -362
  16. package/tests/core/detection-funnel.test.ts +0 -63
  17. package/tests/core/evolution-e2e.test.ts +0 -58
  18. package/tests/core/evolution-engine-gate-integration.test.ts +0 -543
  19. package/tests/core/evolution-engine.test.ts +0 -562
  20. package/tests/core/evolution-reducer.test.ts +0 -180
  21. package/tests/core/evolution-user-stories.e2e.test.ts +0 -249
  22. package/tests/core/local-worker-routing.test.ts +0 -757
  23. package/tests/core/rule-host.test.ts +0 -389
  24. package/tests/core/trajectory-correction-pain.test.ts +0 -180
  25. package/tests/hooks/gate-edit-verification.test.ts +0 -435
  26. package/tests/hooks/llm.test.ts +0 -308
  27. package/tests/hooks/progressive-trust-gate.test.ts +0 -277
  28. package/tests/hooks/prompt.test.ts +0 -1473
  29. package/tests/index.integration.test.ts +0 -179
  30. package/tests/index.shadow-routing.integration.test.ts +0 -140
  31. package/tests/service/evolution-worker.test.ts +0 -462
  32. package/tests/service/nocturnal-service.test.ts +0 -577
  33. package/tests/service/nocturnal-workflow-manager.test.ts +0 -441
  34. package/tests/tools/critique-prompt.test.ts +0 -260
  35. package/tests/tools/deep-reflect.test.ts +0 -232
  36. package/tests/tools/model-index.test.ts +0 -246
  37. package/tests/ui/app.test.tsx +0 -114
@@ -1,179 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import plugin from '../src/index';
3
- import * as fs from 'fs';
4
-
5
- vi.mock('fs');
6
-
7
- describe('Plugin Integration', () => {
8
- let mockApi: any;
9
- let registeredHooks: Map<string, Function>;
10
- let registeredServices: any[];
11
- let registeredCommands: any[];
12
- let registeredRoutes: any[];
13
-
14
- beforeEach(() => {
15
- registeredHooks = new Map();
16
- registeredServices = [];
17
- registeredCommands = [];
18
- registeredRoutes = [];
19
-
20
- mockApi = {
21
- logger: {
22
- info: vi.fn(),
23
- warn: vi.fn(),
24
- error: vi.fn(),
25
- debug: vi.fn(),
26
- },
27
- pluginConfig: { language: 'en' },
28
- resolvePath: vi.fn((p: string) => `/resolved/${p}`),
29
- on: vi.fn((hookName: string, handler: Function) => {
30
- registeredHooks.set(hookName, handler);
31
- }),
32
- registerService: vi.fn((service: any) => {
33
- registeredServices.push(service);
34
- }),
35
- registerCommand: vi.fn((command: any) => {
36
- registeredCommands.push(command);
37
- }),
38
- registerHttpRoute: vi.fn((route: any) => {
39
- registeredRoutes.push(route);
40
- }),
41
- registerTool: vi.fn(),
42
- };
43
-
44
- vi.mocked(fs.existsSync).mockReturnValue(false);
45
- vi.mocked(fs.mkdirSync).mockReturnValue(undefined);
46
- vi.mocked(fs.copyFileSync).mockReturnValue(undefined);
47
- });
48
-
49
- afterEach(() => {
50
- vi.resetAllMocks();
51
- });
52
-
53
- describe('register()', () => {
54
- it('should NOT call resolvePath(".") during register', () => {
55
- plugin.register(mockApi);
56
-
57
- // 关键:register 不应该调用 resolvePath('.')
58
- // 因为它会返回进程工作目录,而不是配置的 workspace
59
- expect(mockApi.resolvePath).not.toHaveBeenCalled();
60
- });
61
-
62
- it('should register all required hooks', () => {
63
- plugin.register(mockApi);
64
-
65
- const expectedHooks = [
66
- 'before_prompt_build',
67
- 'before_tool_call',
68
- 'after_tool_call',
69
- 'before_reset',
70
- 'before_compaction',
71
- 'llm_output',
72
- 'subagent_spawning',
73
- 'subagent_ended',
74
- ];
75
-
76
- for (const hook of expectedHooks) {
77
- expect(registeredHooks.has(hook)).toBe(true);
78
- }
79
- });
80
-
81
- it('registers the Principles Console HTTP route', () => {
82
- plugin.register(mockApi);
83
-
84
- expect(registeredRoutes).toHaveLength(1);
85
- expect(registeredRoutes[0]).toEqual(expect.objectContaining({
86
- path: '/plugins/principles',
87
- auth: 'plugin',
88
- match: 'prefix',
89
- handler: expect.any(Function),
90
- }));
91
- });
92
-
93
- it('should use ctx.workspaceDir from hook context, not from register', async () => {
94
- plugin.register(mockApi);
95
-
96
- const handler = registeredHooks.get('before_prompt_build');
97
- expect(handler).toBeDefined();
98
-
99
- // 模拟正确的 workspaceDir 来自 context
100
- const mockCtx = {
101
- workspaceDir: '/correct/workspace/from/config',
102
- sessionKey: 'test-session',
103
- trigger: 'user',
104
- };
105
-
106
- // 调用 handler
107
- await handler({ prompt: 'test', messages: [] }, mockCtx);
108
-
109
- // 验证没有使用 resolvePath 的返回值
110
- expect(mockApi.resolvePath).not.toHaveBeenCalled();
111
- });
112
-
113
- it('should register EvolutionWorker service', () => {
114
- plugin.register(mockApi);
115
-
116
- const serviceIds = registeredServices.map((service) => service.id);
117
- expect(serviceIds).toContain('principles-evolution-worker');
118
- expect(serviceIds).toContain('principles-disciple-trajectory');
119
- });
120
-
121
- it('should register all slash commands', () => {
122
- plugin.register(mockApi);
123
-
124
- const commandNames = registeredCommands.map(c => c.name);
125
- const expectedCommands = [
126
- 'pd-init',
127
- 'pd-okr',
128
- 'pd-evolve',
129
- 'pd-bootstrap',
130
- 'pd-research',
131
- 'pd-thinking',
132
- 'pd-status',
133
- 'pd-daily',
134
- 'pd-grooming',
135
- 'pd-help'
136
- ];
137
- for (const cmd of expectedCommands) {
138
- expect(commandNames).toContain(cmd);
139
- }
140
- });
141
- });
142
-
143
- describe('workspaceDir source verification', () => {
144
- it('before_tool_call should use ctx.workspaceDir', () => {
145
- plugin.register(mockApi);
146
-
147
- const handler = registeredHooks.get('before_tool_call');
148
- const mockCtx = { workspaceDir: '/workspace/from/context' };
149
-
150
- // 调用 handler(不会 block)
151
- handler({ tool: 'write', params: {} }, mockCtx);
152
-
153
- // 关键:不应该调用 resolvePath
154
- expect(mockApi.resolvePath).not.toHaveBeenCalled();
155
- });
156
-
157
- it('after_tool_call should use ctx.workspaceDir', () => {
158
- plugin.register(mockApi);
159
-
160
- const handler = registeredHooks.get('after_tool_call');
161
- const mockCtx = { workspaceDir: '/workspace/from/context' };
162
-
163
- handler({ tool: 'write', result: { success: true } }, mockCtx);
164
-
165
- expect(mockApi.resolvePath).not.toHaveBeenCalled();
166
- });
167
-
168
- it('llm_output should use ctx.workspaceDir', () => {
169
- plugin.register(mockApi);
170
-
171
- const handler = registeredHooks.get('llm_output');
172
- const mockCtx = { workspaceDir: '/workspace/from/context' };
173
-
174
- handler({ assistantTexts: ['test'] }, mockCtx);
175
-
176
- expect(mockApi.resolvePath).not.toHaveBeenCalled();
177
- });
178
- });
179
- });
@@ -1,140 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
4
- import * as os from 'os';
5
- import plugin from '../src/index.js';
6
- import {
7
- registerTrainingRun,
8
- startTrainingRun,
9
- completeTrainingRun,
10
- registerCheckpoint,
11
- attachEvalSummary,
12
- markCheckpointDeployable,
13
- } from '../src/core/model-training-registry.js';
14
- import { advancePromotion, DEFAULT_BASELINE_METRICS } from '../src/core/promotion-gate.js';
15
- import { bindCheckpointToWorkerProfile, enableRoutingForProfile } from '../src/core/model-deployment-registry.js';
16
- import { computeShadowStats } from '../src/core/shadow-observation-registry.js';
17
-
18
- function makeTmpDir(): string {
19
- return fs.mkdtempSync(path.join(os.tmpdir(), 'pd-shadow-hook-test-'));
20
- }
21
-
22
- function rmdir(dir: string): void {
23
- try {
24
- if (fs.existsSync(dir)) {
25
- fs.rmSync(dir, { recursive: true, force: true });
26
- }
27
- } catch {
28
- // ignore cleanup errors
29
- }
30
- }
31
-
32
- function setupShadowReadyReaderCheckpoint(stateDir: string): string {
33
- const run = registerTrainingRun(stateDir, {
34
- targetModelFamily: 'qwen2.5-7b-reader',
35
- datasetFingerprint: 'fp-dataset',
36
- exportId: 'export-1',
37
- sampleCount: 10,
38
- configFingerprint: 'fp-config',
39
- });
40
- startTrainingRun(stateDir, run.trainRunId);
41
- completeTrainingRun(stateDir, run.trainRunId);
42
-
43
- const checkpoint = registerCheckpoint(stateDir, {
44
- trainRunId: run.trainRunId,
45
- targetModelFamily: 'qwen2.5-7b-reader',
46
- artifactPath: path.join(stateDir, 'checkpoints', 'ckpt-reader'),
47
- });
48
-
49
- attachEvalSummary(stateDir, checkpoint.checkpointId, {
50
- evalId: 'eval-1',
51
- checkpointId: checkpoint.checkpointId,
52
- benchmarkId: 'bench-1',
53
- targetModelFamily: 'qwen2.5-7b-reader',
54
- mode: 'reduced_prompt',
55
- baselineScore: 0.5,
56
- candidateScore: 0.7,
57
- delta: 0.2,
58
- verdict: 'pass',
59
- });
60
- markCheckpointDeployable(stateDir, checkpoint.checkpointId, true);
61
- advancePromotion(stateDir, {
62
- checkpointId: checkpoint.checkpointId,
63
- targetProfile: 'local-reader',
64
- baselineMetrics: DEFAULT_BASELINE_METRICS,
65
- orchestratorReviewPassed: true,
66
- reviewNote: 'shadow ready for test',
67
- });
68
- bindCheckpointToWorkerProfile(stateDir, 'local-reader', checkpoint.checkpointId);
69
- enableRoutingForProfile(stateDir, 'local-reader');
70
- return checkpoint.checkpointId;
71
- }
72
-
73
- describe('plugin shadow routing integration', () => {
74
- let tmpDir: string;
75
-
76
- beforeEach(() => {
77
- tmpDir = makeTmpDir();
78
- });
79
-
80
- afterEach(() => {
81
- rmdir(tmpDir);
82
- });
83
-
84
- it('records and completes shadow observations from runtime subagent hooks', async () => {
85
- const checkpointId = setupShadowReadyReaderCheckpoint(tmpDir);
86
- const hooks = new Map<string, Function>();
87
-
88
- const api: any = {
89
- logger: { info() {}, warn() {}, error() {}, debug() {} },
90
- pluginConfig: { language: 'en' },
91
- rootDir: tmpDir,
92
- resolvePath: (p: string) => (p === '.' ? tmpDir : path.join(tmpDir, p)),
93
- registerHttpRoute() {},
94
- on(name: string, handler: Function) {
95
- hooks.set(name, handler);
96
- },
97
- registerService() {},
98
- registerCommand() {},
99
- registerTool() {},
100
- };
101
-
102
- plugin.register(api);
103
-
104
- const spawning = hooks.get('subagent_spawning');
105
- const ended = hooks.get('subagent_ended');
106
- expect(spawning).toBeTypeOf('function');
107
- expect(ended).toBeTypeOf('function');
108
-
109
- for (let i = 0; i < 5; i++) {
110
- const sessionKey = `agent:local-reader:test-child-${i}`;
111
- spawning!(
112
- {
113
- childSessionKey: sessionKey,
114
- agentId: 'local-reader',
115
- mode: 'run',
116
- threadRequested: false,
117
- label: 'shadow-test',
118
- },
119
- { workspaceDir: tmpDir }
120
- );
121
-
122
- ended!(
123
- {
124
- targetSessionKey: sessionKey,
125
- targetKind: 'subagent',
126
- reason: 'completed',
127
- outcome: 'ok',
128
- },
129
- { workspaceDir: tmpDir }
130
- );
131
- }
132
-
133
- const stats = computeShadowStats(tmpDir, { checkpointId, windowMs: 7 * 24 * 60 * 60 * 1000 });
134
- expect(stats).not.toBeNull();
135
- expect(stats!.totalCount).toBe(5);
136
- expect(stats!.acceptedCount).toBe(5);
137
- expect(stats!.rejectRate).toBe(0);
138
- expect(stats!.isStatisticallySignificant).toBe(true);
139
- });
140
- });