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.
Files changed (93) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/scripts/acceptance-test.mjs +314 -0
  4. package/scripts/seed-nocturnal-scenarios.mjs +16 -9
  5. package/scripts/validate-live-path.ts +141 -17
  6. package/src/commands/archive-impl.ts +3 -0
  7. package/src/commands/context.ts +4 -1
  8. package/src/commands/disable-impl.ts +2 -2
  9. package/src/commands/evolution-status.ts +2 -2
  10. package/src/commands/focus.ts +4 -2
  11. package/src/commands/nocturnal-train.ts +9 -1
  12. package/src/commands/pain.ts +3 -1
  13. package/src/commands/pd-reflect.ts +2 -0
  14. package/src/commands/rollback-impl.ts +5 -1
  15. package/src/commands/rollback.ts +2 -1
  16. package/src/commands/samples.ts +1 -0
  17. package/src/commands/workflow-debug.ts +1 -0
  18. package/src/core/adaptive-thresholds.ts +2 -1
  19. package/src/core/code-implementation-storage.ts +2 -0
  20. package/src/core/config.ts +1 -0
  21. package/src/core/diagnostician-task-store.ts +2 -0
  22. package/src/core/empathy-keyword-matcher.ts +4 -1
  23. package/src/core/event-log.ts +6 -3
  24. package/src/core/evolution-engine.ts +4 -1
  25. package/src/core/evolution-logger.ts +1 -0
  26. package/src/core/external-training-contract.ts +2 -1
  27. package/src/core/focus-history.ts +15 -3
  28. package/src/core/init.ts +3 -1
  29. package/src/core/merge-gate-audit.ts +3 -0
  30. package/src/core/model-deployment-registry.ts +1 -0
  31. package/src/core/model-training-registry.ts +1 -0
  32. package/src/core/nocturnal-arbiter.ts +4 -3
  33. package/src/core/nocturnal-candidate-scoring.ts +5 -0
  34. package/src/core/nocturnal-compliance.ts +22 -1
  35. package/src/core/nocturnal-dataset.ts +3 -1
  36. package/src/core/nocturnal-export.ts +5 -0
  37. package/src/core/nocturnal-reasoning-deriver.ts +6 -1
  38. package/src/core/nocturnal-snapshot-contract.ts +1 -0
  39. package/src/core/nocturnal-trinity.ts +24 -3
  40. package/src/core/pain-context-extractor.ts +3 -1
  41. package/src/core/pain.ts +3 -1
  42. package/src/core/path-resolver.ts +10 -4
  43. package/src/core/pd-task-reconciler.ts +3 -1
  44. package/src/core/pd-task-store.ts +1 -0
  45. package/src/core/principle-internalization/deprecated-readiness.ts +2 -1
  46. package/src/core/principle-training-state.ts +2 -0
  47. package/src/core/principle-tree-ledger.ts +4 -0
  48. package/src/core/principle-tree-migration.ts +2 -1
  49. package/src/core/promotion-gate.ts +7 -1
  50. package/src/core/replay-engine.ts +10 -4
  51. package/src/core/risk-calculator.ts +2 -1
  52. package/src/core/rule-host.ts +3 -2
  53. package/src/core/session-tracker.ts +5 -2
  54. package/src/core/shadow-observation-registry.ts +1 -0
  55. package/src/core/thinking-os-parser.ts +1 -0
  56. package/src/core/trajectory.ts +9 -5
  57. package/src/hooks/bash-risk.ts +2 -0
  58. package/src/hooks/edit-verification.ts +3 -0
  59. package/src/hooks/gate-block-helper.ts +3 -0
  60. package/src/hooks/gate.ts +8 -0
  61. package/src/hooks/gfi-gate.ts +2 -0
  62. package/src/hooks/lifecycle.ts +1 -0
  63. package/src/hooks/llm.ts +1 -0
  64. package/src/hooks/pain.ts +3 -1
  65. package/src/hooks/progressive-trust-gate.ts +3 -0
  66. package/src/hooks/prompt.ts +5 -2
  67. package/src/hooks/subagent.ts +1 -0
  68. package/src/hooks/thinking-checkpoint.ts +1 -0
  69. package/src/hooks/trajectory-collector.ts +2 -1
  70. package/src/http/principles-console-route.ts +5 -2
  71. package/src/index.ts +7 -0
  72. package/src/service/central-health-service.ts +1 -0
  73. package/src/service/central-overview-service.ts +2 -0
  74. package/src/service/evolution-query-service.ts +1 -0
  75. package/src/service/evolution-worker.ts +31 -1
  76. package/src/service/health-query-service.ts +6 -6
  77. package/src/service/monitoring-query-service.ts +4 -0
  78. package/src/service/nocturnal-runtime.ts +7 -5
  79. package/src/service/nocturnal-service.ts +21 -0
  80. package/src/service/nocturnal-target-selector.ts +2 -0
  81. package/src/service/runtime-summary-service.ts +6 -5
  82. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +2 -1
  83. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +2 -0
  84. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +3 -2
  85. package/src/service/subagent-workflow/workflow-manager-base.ts +6 -1
  86. package/src/service/subagent-workflow/workflow-store.ts +2 -0
  87. package/src/tools/deep-reflect.ts +9 -0
  88. package/src/tools/model-index.ts +1 -0
  89. package/src/tools/write-pain-flag.ts +1 -0
  90. package/src/utils/file-lock.ts +1 -0
  91. package/src/utils/io.ts +2 -1
  92. package/tests/core/nocturnal-e2e.test.ts +10 -0
  93. package/tests/tools/write-pain-flag.test.ts +29 -13
@@ -108,6 +108,7 @@ export function createDeepReflectTool(api: OpenClawPluginApi) {
108
108
  }
109
109
 
110
110
 
111
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
111
112
  const effectiveWorkspaceDir = resolveReflectionWorkspace(api);
112
113
 
113
114
  const config = loadConfig(effectiveWorkspaceDir, api);
@@ -121,9 +122,11 @@ export function createDeepReflectTool(api: OpenClawPluginApi) {
121
122
 
122
123
  try {
123
124
 
125
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
124
126
  return await executeReflectionWorkflow(effectiveWorkspaceDir, config, context, depth, model_id, api);
125
127
  } catch (err) {
126
128
 
129
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
127
130
  return handleReflectionError(err, context, depth, model_id, effectiveWorkspaceDir, api);
128
131
  }
129
132
  }
@@ -146,6 +149,7 @@ function resolveReflectionWorkspace(api: OpenClawPluginApi): string {
146
149
  * Execute the deep reflection workflow: start, poll, collect results.
147
150
  */
148
151
 
152
+ // eslint-disable-next-line @typescript-eslint/max-params
149
153
  async function executeReflectionWorkflow(
150
154
  effectiveWorkspaceDir: string,
151
155
  config: DeepReflectionConfig,
@@ -177,6 +181,7 @@ async function executeReflectionWorkflow(
177
181
  const startTime = Date.now();
178
182
  const timeoutMs = config.timeout_ms ?? 60000;
179
183
 
184
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
180
185
  return await pollReflectionCompletion(manager, handle, timeoutMs, startTime, eventLog, effectiveWorkspaceDir, context, model_id, depth);
181
186
  } finally {
182
187
  manager.dispose();
@@ -187,6 +192,7 @@ async function executeReflectionWorkflow(
187
192
  * Poll the reflection workflow until completion, timeout, or error.
188
193
  */
189
194
 
195
+ // eslint-disable-next-line @typescript-eslint/max-params
190
196
  async function pollReflectionCompletion(
191
197
  manager: DeepReflectWorkflowManager,
192
198
  handle: { workflowId: string; childSessionKey: string },
@@ -207,6 +213,7 @@ async function pollReflectionCompletion(
207
213
 
208
214
  if (workflowState === 'completed') {
209
215
 
216
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
210
217
  return formatReflectionSuccess(handle, context, depth, model_id, startTime, eventLog, workspaceDir);
211
218
  }
212
219
 
@@ -222,6 +229,7 @@ async function pollReflectionCompletion(
222
229
  * Format the success response from a completed reflection.
223
230
  */
224
231
 
232
+ // eslint-disable-next-line @typescript-eslint/max-params
225
233
  function formatReflectionSuccess(
226
234
  handle: { childSessionKey: string },
227
235
  context: string,
@@ -275,6 +283,7 @@ ${insights || '反思完成,详见 REFLECTION_LOG。'}
275
283
  * Handle reflection errors and format error response.
276
284
  */
277
285
 
286
+ // eslint-disable-next-line @typescript-eslint/max-params
278
287
  function handleReflectionError(
279
288
  err: unknown,
280
289
  context: string,
@@ -62,6 +62,7 @@ export function loadModelIndex(
62
62
  const customConfig = loadCustomConfig(wctx);
63
63
 
64
64
 
65
+ // eslint-disable-next-line @typescript-eslint/init-declarations
65
66
  let modelsDir: string;
66
67
  if (customConfig?.modelsDir) {
67
68
  modelsDir = path.isAbsolute(customConfig.modelsDir)
@@ -1,5 +1,6 @@
1
1
  import type { OpenClawPluginApi } from '../openclaw-sdk.js';
2
2
  import { Type } from '@sinclair/typebox';
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
4
  import { buildPainFlag, writePainFlag } from '../core/pain.js';
4
5
  import { resolveWorkspaceDirFromApi } from '../core/path-resolver.js';
5
6
  import * as fs from 'fs';
@@ -335,6 +335,7 @@ export async function withAsyncLock<T>(
335
335
 
336
336
  // 创建新的 Promise 链
337
337
 
338
+ // eslint-disable-next-line @typescript-eslint/init-declarations
338
339
  let resolveRelease: () => void;
339
340
  const releasePromise = new Promise<void>(resolve => {
340
341
  resolveRelease = resolve;
package/src/utils/io.ts CHANGED
@@ -2,7 +2,7 @@ import * as path from 'path';
2
2
  import * as fs from 'fs';
3
3
  import { resolvePdPath } from '../core/paths.js';
4
4
 
5
- // eslint-disable-next-line complexity -- complexity 12, refactor candidate
5
+
6
6
  export function normalizePath(filePath: string, projectDir: string): string {
7
7
  if (!filePath) return '';
8
8
 
@@ -18,6 +18,7 @@ export function normalizePath(filePath: string, projectDir: string): string {
18
18
  }
19
19
 
20
20
 
21
+ // eslint-disable-next-line @typescript-eslint/init-declarations
21
22
  let rel: string;
22
23
  if (projectIsWin) {
23
24
  const projectAbs = path.resolve(projectDir);
@@ -40,6 +40,11 @@ describe('Phase 4a: Correction rejected integration', () => {
40
40
  correctionDetected: true, correctionCue: '错了',
41
41
  referencesAssistantTurnId: atId, createdAt: new Date().toISOString(),
42
42
  });
43
+ // Tool call triggers maybeCreateCorrectionSample on success
44
+ trajectory.recordToolCall({
45
+ sessionId: 'corr-session', toolName: 'read', outcome: 'success',
46
+ createdAt: new Date().toISOString(),
47
+ });
43
48
 
44
49
  // Verify sample was created
45
50
  const samples = trajectory.listCorrectionSamples('pending');
@@ -68,6 +73,11 @@ describe('Phase 4a: Correction rejected integration', () => {
68
73
  correctionDetected: true, correctionCue: '改进',
69
74
  referencesAssistantTurnId: atId, createdAt: new Date().toISOString(),
70
75
  });
76
+ // Tool call triggers maybeCreateCorrectionSample on success
77
+ trajectory.recordToolCall({
78
+ sessionId: 'approved-session', toolName: 'read', outcome: 'success',
79
+ createdAt: new Date().toISOString(),
80
+ });
71
81
 
72
82
  const samples = trajectory.listCorrectionSamples('pending');
73
83
  expect(samples.length).toBe(1);
@@ -122,15 +122,28 @@ describe('write_pain_flag tool', () => {
122
122
  expect(api._logs.some((l: any) => l.level === 'warn')).toBe(true);
123
123
  });
124
124
 
125
- it('returns clear error when workspace cannot be resolved', async () => {
126
- const api = createMockApi('') as any;
127
- const tool = createWritePainFlagTool(api);
125
+ it('falls back to PathResolver when config.workspaceDir is not set', async () => {
126
+ // Even without explicit workspaceDir, the tool should succeed
127
+ // by falling back to PathResolver (which finds default workspace)
128
+ const logs: { level: string; message: string }[] = [];
129
+ const api = {
130
+ config: {},
131
+ logger: {
132
+ info: (m: string) => logs.push({ level: 'info', message: m }),
133
+ warn: (m: string) => logs.push({ level: 'warn', message: m }),
134
+ error: (m: string) => logs.push({ level: 'error', message: m }),
135
+ debug: (m: string) => logs.push({ level: 'debug', message: m }),
136
+ },
137
+ runtime: { subagent: null, agent: null },
138
+ _logs: logs,
139
+ } as any;
128
140
 
129
- const result = await tool.execute('test-3', { reason: 'Test error' });
141
+ const tool = createWritePainFlagTool(api);
142
+ const result = await tool.execute('test-3', { reason: 'Test fallback' });
130
143
 
131
- expect(result.content[0].text).toContain('❌');
132
- expect(result.content[0].text).toContain('workspace');
133
- expect(api._logs.some((l: any) => l.level === 'error')).toBe(true);
144
+ // Should succeed via PathResolver fallback
145
+ expect(result.content[0].text).toContain('');
146
+ expect(result.content[0].text).toContain('Test fallback');
134
147
  });
135
148
 
136
149
  // ─────────────────────────────────────────────────────────
@@ -209,16 +222,19 @@ describe('write_pain_flag tool', () => {
209
222
  expect(text).toContain('heartbeat');
210
223
  });
211
224
 
212
- it('provides clear failure feedback with error message', async () => {
225
+ it('handles missing state directory by creating it automatically', async () => {
213
226
  const api = createMockApi(workspaceDir) as any;
214
- // Simulate workspace resolution failure by removing the config
215
- (api as any).config = {};
227
+ // Remove .state directory to test auto-creation
228
+ if (fs.existsSync(stateDir)) {
229
+ fs.rmSync(stateDir, { recursive: true, force: true });
230
+ }
216
231
 
217
232
  const tool = createWritePainFlagTool(api);
218
- const result = await tool.execute('test-7', { reason: 'Should fail' });
233
+ const result = await tool.execute('test-auto', { reason: 'Auto-create state dir' });
219
234
 
220
- expect(result.content[0].text).toContain('');
221
- expect(result.content[0].text).toContain('workspace');
235
+ expect(result.content[0].text).toContain('');
236
+ const painFlagPath = path.join(stateDir, '.pain_flag');
237
+ expect(fs.existsSync(painFlagPath)).toBe(true);
222
238
  });
223
239
 
224
240
  // ─────────────────────────────────────────────────────────