principles-disciple 1.16.0 → 1.17.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 (129) hide show
  1. package/README.md +13 -5
  2. package/openclaw.plugin.json +4 -4
  3. package/package.json +1 -1
  4. package/src/commands/archive-impl.ts +3 -3
  5. package/src/commands/capabilities.ts +1 -1
  6. package/src/commands/context.ts +3 -3
  7. package/src/commands/disable-impl.ts +1 -1
  8. package/src/commands/evolution-status.ts +2 -2
  9. package/src/commands/focus.ts +2 -2
  10. package/src/commands/nocturnal-train.ts +6 -6
  11. package/src/commands/pain.ts +4 -4
  12. package/src/commands/pd-reflect.ts +87 -0
  13. package/src/commands/rollback-impl.ts +4 -4
  14. package/src/commands/rollback.ts +2 -2
  15. package/src/commands/samples.ts +2 -2
  16. package/src/commands/workflow-debug.ts +1 -1
  17. package/src/config/errors.ts +1 -1
  18. package/src/core/adaptive-thresholds.ts +1 -1
  19. package/src/core/code-implementation-storage.ts +2 -2
  20. package/src/core/config.ts +1 -1
  21. package/src/core/diagnostician-task-store.ts +2 -2
  22. package/src/core/empathy-keyword-matcher.ts +3 -3
  23. package/src/core/event-log.ts +5 -5
  24. package/src/core/evolution-engine.ts +4 -4
  25. package/src/core/evolution-logger.ts +1 -1
  26. package/src/core/evolution-reducer.ts +3 -3
  27. package/src/core/evolution-types.ts +5 -5
  28. package/src/core/external-training-contract.ts +1 -1
  29. package/src/core/focus-history.ts +14 -14
  30. package/src/core/hygiene/tracker.ts +1 -1
  31. package/src/core/init.ts +2 -2
  32. package/src/core/model-deployment-registry.ts +2 -2
  33. package/src/core/model-training-registry.ts +2 -2
  34. package/src/core/nocturnal-arbiter.ts +1 -1
  35. package/src/core/nocturnal-artificer.ts +2 -2
  36. package/src/core/nocturnal-candidate-scoring.ts +2 -2
  37. package/src/core/nocturnal-compliance.ts +3 -3
  38. package/src/core/nocturnal-dataset.ts +3 -3
  39. package/src/core/nocturnal-export.ts +4 -4
  40. package/src/core/nocturnal-rule-implementation-validator.ts +1 -1
  41. package/src/core/nocturnal-snapshot-contract.ts +112 -0
  42. package/src/core/nocturnal-trajectory-extractor.ts +7 -5
  43. package/src/core/nocturnal-trinity.ts +27 -28
  44. package/src/core/pain-context-extractor.ts +3 -3
  45. package/src/core/pain.ts +124 -11
  46. package/src/core/path-resolver.ts +4 -4
  47. package/src/core/pd-task-reconciler.ts +10 -10
  48. package/src/core/pd-task-service.ts +1 -1
  49. package/src/core/pd-task-store.ts +1 -1
  50. package/src/core/principle-internalization/deprecated-readiness.ts +1 -1
  51. package/src/core/principle-training-state.ts +2 -2
  52. package/src/core/principle-tree-ledger.ts +7 -7
  53. package/src/core/promotion-gate.ts +9 -9
  54. package/src/core/replay-engine.ts +12 -12
  55. package/src/core/risk-calculator.ts +1 -1
  56. package/src/core/rule-host-types.ts +2 -2
  57. package/src/core/rule-host.ts +5 -5
  58. package/src/core/schema/db-types.ts +1 -1
  59. package/src/core/schema/schema-definitions.ts +1 -1
  60. package/src/core/session-tracker.ts +96 -4
  61. package/src/core/shadow-observation-registry.ts +3 -3
  62. package/src/core/system-logger.ts +2 -2
  63. package/src/core/thinking-os-parser.ts +1 -1
  64. package/src/core/training-program.ts +2 -2
  65. package/src/core/trajectory.ts +8 -8
  66. package/src/core/workspace-context.ts +2 -2
  67. package/src/core/workspace-dir-service.ts +85 -0
  68. package/src/core/workspace-dir-validation.ts +30 -107
  69. package/src/hooks/bash-risk.ts +3 -3
  70. package/src/hooks/edit-verification.ts +4 -4
  71. package/src/hooks/gate-block-helper.ts +4 -4
  72. package/src/hooks/gate.ts +10 -10
  73. package/src/hooks/gfi-gate.ts +7 -7
  74. package/src/hooks/lifecycle.ts +2 -2
  75. package/src/hooks/llm.ts +1 -1
  76. package/src/hooks/pain.ts +25 -5
  77. package/src/hooks/progressive-trust-gate.ts +7 -7
  78. package/src/hooks/prompt.ts +24 -5
  79. package/src/hooks/subagent.ts +2 -2
  80. package/src/hooks/thinking-checkpoint.ts +2 -2
  81. package/src/hooks/trajectory-collector.ts +1 -1
  82. package/src/http/principles-console-route.ts +14 -6
  83. package/src/i18n/commands.ts +4 -0
  84. package/src/index.ts +181 -185
  85. package/src/service/central-health-service.ts +1 -1
  86. package/src/service/central-overview-service.ts +3 -3
  87. package/src/service/evolution-query-service.ts +1 -1
  88. package/src/service/evolution-worker.ts +209 -104
  89. package/src/service/health-query-service.ts +27 -17
  90. package/src/service/monitoring-query-service.ts +3 -3
  91. package/src/service/nocturnal-runtime.ts +4 -4
  92. package/src/service/nocturnal-service.ts +40 -23
  93. package/src/service/nocturnal-target-selector.ts +2 -2
  94. package/src/service/runtime-summary-service.ts +1 -1
  95. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +1 -1
  96. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +3 -3
  97. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +16 -13
  98. package/src/service/subagent-workflow/runtime-direct-driver.ts +10 -6
  99. package/src/service/subagent-workflow/types.ts +4 -4
  100. package/src/service/subagent-workflow/workflow-manager-base.ts +5 -5
  101. package/src/service/subagent-workflow/workflow-store.ts +2 -2
  102. package/src/tools/critique-prompt.ts +2 -3
  103. package/src/tools/deep-reflect.ts +17 -16
  104. package/src/tools/model-index.ts +1 -1
  105. package/src/utils/file-lock.ts +1 -1
  106. package/src/utils/io.ts +7 -2
  107. package/src/utils/nlp.ts +1 -1
  108. package/src/utils/plugin-logger.ts +2 -2
  109. package/src/utils/retry.ts +3 -2
  110. package/src/utils/subagent-probe.ts +20 -33
  111. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +8 -7
  112. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +8 -7
  113. package/templates/pain_settings.json +1 -1
  114. package/tests/build-artifacts.test.ts +4 -58
  115. package/tests/commands/pd-reflect.test.ts +49 -0
  116. package/tests/core/nocturnal-snapshot-contract.test.ts +70 -0
  117. package/tests/core/pain-auto-repair.test.ts +96 -0
  118. package/tests/core/pain-integration.test.ts +483 -0
  119. package/tests/core/pain.test.ts +5 -4
  120. package/tests/core/workspace-dir-service.test.ts +68 -0
  121. package/tests/core/workspace-dir-validation.test.ts +56 -192
  122. package/tests/hooks/pain.test.ts +20 -0
  123. package/tests/http/principles-console-route.test.ts +42 -20
  124. package/tests/integration/empathy-workflow-integration.test.ts +1 -2
  125. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +9 -17
  126. package/tests/service/empathy-observer-workflow-manager.test.ts +1 -2
  127. package/tests/service/evolution-worker.nocturnal.test.ts +562 -6
  128. package/tests/service/nocturnal-runtime-hardening.test.ts +33 -0
  129. package/tests/utils/subagent-probe.test.ts +32 -0
package/README.md CHANGED
@@ -17,12 +17,21 @@ This plugin integrates with OpenClaw to provide an evolutionary programming fram
17
17
 
18
18
  ### Slash Commands
19
19
 
20
+ All commands support **short aliases** for easier input:
21
+
22
+ | Short | Full Command | Description |
23
+ |-------|--------------|-------------|
24
+ | `/pdi` | `/pd-init` | Initialize workspace |
25
+ | `/pdb` | `/pd-bootstrap` | Scan environment tools |
26
+ | `/pdr` | `/pd-research` | Research tools and capabilities |
27
+ | `/pdt` | `/pd-thinking` | Manage thinking models |
28
+ | `/pdrl` | `/pd-reflect` | Manually trigger nocturnal reflection |
29
+ | `/pdd` | `/pd-daily` | Configure and send daily report |
30
+ | `/pdg` | `/pd-grooming` | Workspace cleanup |
31
+ | `/pdh` | `/pd-help` | Show command reference |
32
+
20
33
  | Command | Description |
21
34
  |---------|-------------|
22
- | `/pd-init` | Initialize workspace |
23
- | `/pd-bootstrap` | Scan environment tools |
24
- | `/pd-research` | Research tools and capabilities |
25
- | `/pd-thinking` | Manage thinking models |
26
35
  | `/pd-status` | View evolution status |
27
36
  | `/pd-context` | Control context injection |
28
37
  | `/pd-focus` | Focus file management |
@@ -34,7 +43,6 @@ This plugin integrates with OpenClaw to provide an evolutionary programming fram
34
43
  | `/nocturnal-train` | Nocturnal training operations |
35
44
  | `/nocturnal-rollout` | Nocturnal rollout and promotion |
36
45
  | `/pd-workflow-debug` | Debug workflow state |
37
- | `/pd-help` | Show command reference |
38
46
 
39
47
  ### Tools
40
48
 
@@ -2,7 +2,7 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.16.0",
5
+ "version": "1.17.0",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
@@ -76,8 +76,8 @@
76
76
  }
77
77
  },
78
78
  "buildFingerprint": {
79
- "gitSha": "a320b37a2418",
80
- "bundleMd5": "6a5838169d98e4e6419e736dd034d93a",
81
- "builtAt": "2026-04-09T13:57:36.081Z"
79
+ "gitSha": "bce835db37a0",
80
+ "bundleMd5": "9e44177badb37ac423669fd187bf2667",
81
+ "builtAt": "2026-04-10T14:01:23.050Z"
82
82
  }
83
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
@@ -51,13 +51,13 @@ export function handleArchiveImplCommand(ctx: PluginCommandContext): PluginComma
51
51
 
52
52
  // Subcommand: list
53
53
  if (subcommand === 'list') {
54
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: Mutual recursion between helper functions - reordering would break logical grouping
54
+
55
55
  return _handleListArchivable(stateDir, isZh);
56
56
  }
57
57
 
58
58
  // Archive by ID
59
59
  const targetId = subcommand;
60
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: Mutual recursion between helper functions - reordering would break logical grouping
60
+
61
61
  return _handleArchiveImpl(workspaceDir, stateDir, targetId, isZh);
62
62
  }
63
63
 
@@ -94,7 +94,7 @@ function _handleListArchivable(
94
94
  return { text: output };
95
95
  }
96
96
 
97
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
97
+
98
98
  function _handleArchiveImpl(
99
99
  workspaceDir: string,
100
100
  stateDir: string,
@@ -24,7 +24,7 @@ function scanEnvironment(wctx: WorkspaceContext): any {
24
24
  available: true,
25
25
  version: versionLine.trim(),
26
26
  };
27
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: catch parameter intentionally unused - we only care that the command failed
27
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Reason: catch parameter intentionally unused - we only care that the command failed
28
28
  } catch (_e) {
29
29
  tools[tool.name] = { available: false };
30
30
  }
@@ -98,7 +98,7 @@ function showStatus(workspaceDir: string, isZh: boolean): string {
98
98
  /**
99
99
  * Toggle a boolean setting
100
100
  */
101
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature requires specific params - refactoring would break public API contract
101
+
102
102
  function toggleSetting(
103
103
  workspaceDir: string,
104
104
  key: 'thinkingOs' | 'reflectionLog',
@@ -213,7 +213,7 @@ function applyPreset(
213
213
  preset: 'minimal' | 'standard' | 'full',
214
214
  isZh: boolean
215
215
  ): string {
216
- // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned in switch block immediately after declaration
216
+
217
217
  let config: ContextInjectionConfig;
218
218
 
219
219
  switch (preset) {
@@ -314,7 +314,7 @@ export function handleContextCommand(ctx: PluginCommandContext): PluginCommandRe
314
314
  // Detect language from context
315
315
  const isZh = (ctx.config?.language as string) === 'zh';
316
316
 
317
- // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned in switch block immediately after declaration
317
+
318
318
  let result: string;
319
319
 
320
320
  switch (subCommand) {
@@ -69,7 +69,7 @@ function _handleListActive(
69
69
  return { text: output };
70
70
  }
71
71
 
72
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
72
+
73
73
  function _handleDisableImpl(
74
74
  workspaceDir: string,
75
75
  stateDir: string,
@@ -45,7 +45,7 @@ function formatRouteRecommendations(
45
45
  .join(', ');
46
46
  }
47
47
 
48
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
48
+
49
49
  function buildEnglishOutput(
50
50
  workspaceDir: string,
51
51
  sessionId: string | null,
@@ -97,7 +97,7 @@ function buildEnglishOutput(
97
97
  return lines.join('\n');
98
98
  }
99
99
 
100
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
100
+
101
101
  function buildChineseOutput(
102
102
  workspaceDir: string,
103
103
  sessionId: string | null,
@@ -281,7 +281,7 @@ async function compressFocus(
281
281
  cleanupHistory(focusPath);
282
282
 
283
283
  // 5. 压缩内容
284
- // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in both try and catch blocks
284
+
285
285
  let compressedContent: string;
286
286
  try {
287
287
  compressedContent = compressFocusContent(oldContent, workspaceDir);
@@ -477,7 +477,7 @@ export async function handleFocusCommand(
477
477
  // 检测语言(与 context.ts 保持一致)
478
478
  const isZh = (ctx.config?.language as string) === 'zh';
479
479
 
480
- // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all switch cases
480
+
481
481
  let result: string;
482
482
 
483
483
  switch (subCommand) {
@@ -270,12 +270,12 @@ Hardware tiers:
270
270
  // This closes the gap in the create-experiment -> trainer -> import-result chain.
271
271
  // NOTE: This blocks until training completes (could be minutes).
272
272
  if (runNow) {
273
- // eslint-disable-next-line @typescript-eslint/no-shadow -- Reason: shadowing is intentional - inner block scoping for trainer execution
273
+
274
274
  const {spec} = createResult;
275
275
  const baseDir = TRAINER_SCRIPTS_DIR;
276
276
  const scriptPath = path.join(baseDir, 'main.py');
277
277
  const specPath = path.join(baseDir, `experiment-${spec.experimentId}.json`);
278
- // eslint-disable-next-line @typescript-eslint/no-shadow -- Reason: shadowing is intentional - inner block scoping for trainer output directory
278
+
279
279
  const {outputDir} = spec;
280
280
  const resultFilePath = path.join(outputDir, `result-${spec.experimentId}.json`);
281
281
 
@@ -286,7 +286,7 @@ Hardware tiers:
286
286
  }
287
287
  fs.writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf-8');
288
288
 
289
- // eslint-disable-next-line @typescript-eslint/init-declarations, @typescript-eslint/consistent-type-imports -- Reason: type assertion required - trainer result type from external contract module
289
+
290
290
  let trainerResult!: import('../core/external-training-contract.js').TrainingExperimentResult;
291
291
 
292
292
  try {
@@ -394,7 +394,7 @@ Hardware tiers:
394
394
 
395
395
  // Process trainer result (register checkpoint)
396
396
  // dry_run returns null (no checkpoint); other statuses throw on error
397
- // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned in try block immediately after declaration
397
+
398
398
  let processed: { checkpointId: string; checkpointRef: string } | null;
399
399
  try {
400
400
  processed = program.processResult({
@@ -536,7 +536,7 @@ Next steps:
536
536
  }
537
537
  }
538
538
 
539
- // eslint-disable-next-line @typescript-eslint/init-declarations, @typescript-eslint/no-explicit-any -- Reason: JSON.parse returns dynamic JSON - type unknown at parse time, narrowed via type narrowing below
539
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: JSON.parse returns dynamic JSON - type unknown at parse time, narrowed via type narrowing below
540
540
  let result: any;
541
541
  try {
542
542
  result = JSON.parse(resultJson);
@@ -567,7 +567,7 @@ Next steps:
567
567
 
568
568
  // Process the result
569
569
  const program = new TrainingProgram(workspaceDir);
570
- // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned in try block immediately after declaration
570
+
571
571
  let processed: { checkpointId: string; checkpointRef: string } | null;
572
572
  try {
573
573
  processed = program.processResult({
@@ -96,7 +96,7 @@ export function handlePainCommand(ctx: PluginCommandContext): PluginCommandResul
96
96
 
97
97
  // Handle empathy subcommand
98
98
  if (args.startsWith('empathy')) {
99
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between main handler and empathy subcommand handler - reordering would break logical grouping
99
+
100
100
  return handleEmpathySubcommand(wctx, args, sessionId, isZh);
101
101
  }
102
102
 
@@ -126,7 +126,7 @@ export function handlePainCommand(ctx: PluginCommandContext): PluginCommandResul
126
126
  const gfiBar = createProgressBar(gfi, 100, 15);
127
127
 
128
128
  // Determine Mental Mode (aligned with prompt.ts logic)
129
- // eslint-disable-next-line no-useless-assignment -- Reason: initial value unused due to immediate reassignment - keeping for type inference compatibility
129
+
130
130
  let mentalMode = '';
131
131
  if (isZh) {
132
132
  if (gfi >= 70) mentalMode = '🚑 救赎模式 (HUMBLE_RECOVERY)';
@@ -139,7 +139,7 @@ export function handlePainCommand(ctx: PluginCommandContext): PluginCommandResul
139
139
  }
140
140
 
141
141
  // Determine health status based on GFI
142
- // eslint-disable-next-line no-useless-assignment -- Reason: initial values unused due to immediate reassignment in all branches
142
+
143
143
  let healthLabel = 'Healthy';
144
144
  let suggestionText = '';
145
145
 
@@ -218,7 +218,7 @@ export function handlePainCommand(ctx: PluginCommandContext): PluginCommandResul
218
218
  /**
219
219
  * Handle /pd-status empathy subcommand
220
220
  */
221
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: empathy subcommand requires all 4 context params - refactoring would break API
221
+
222
222
  function handleEmpathySubcommand(
223
223
  wctx: WorkspaceContext,
224
224
  args: string,
@@ -0,0 +1,87 @@
1
+ /**
2
+ * PD Reflect Command (/pd-reflect)
3
+ *
4
+ * Manually trigger a sleep_reflection task, bypassing idle check.
5
+ * This command must operate on an explicitly resolved active workspace.
6
+ */
7
+
8
+ import { PluginCommandDefinition, PluginCommandContext, PluginCommandResult, OpenClawPluginApi } from '../openclaw-sdk.js';
9
+ import { acquireQueueLock, EVOLUTION_QUEUE_LOCK_SUFFIX } from '../service/evolution-worker.js';
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+
13
+ interface PdReflectContext extends PluginCommandContext {
14
+ api?: OpenClawPluginApi;
15
+ workspaceDir?: string;
16
+ }
17
+
18
+ export const handlePdReflect: PluginCommandDefinition = {
19
+ name: 'pd-reflect',
20
+ description: 'Manually trigger Nocturnal sleep reflection pipeline',
21
+ acceptsArgs: false,
22
+ requireAuth: false,
23
+ handler: async (ctx: PdReflectContext): Promise<PluginCommandResult> => {
24
+ try {
25
+ const workspaceDir = ctx.workspaceDir;
26
+ if (!workspaceDir) {
27
+ return { text: 'Cannot determine workspace directory. Ensure you are in an active workspace.', isError: true };
28
+ }
29
+
30
+ const stateDir = path.join(workspaceDir, '.state');
31
+ const queuePath = path.join(stateDir, 'evolution_queue.json');
32
+
33
+ // Acquire lock before modifying queue
34
+ const releaseLock = await acquireQueueLock(queuePath, ctx.api?.logger, EVOLUTION_QUEUE_LOCK_SUFFIX);
35
+ let taskId: string | undefined;
36
+ try {
37
+ let rawQueue: unknown[] = [];
38
+ try {
39
+ rawQueue = JSON.parse(fs.readFileSync(queuePath, 'utf8')) as unknown[];
40
+ } catch {
41
+ rawQueue = [];
42
+ }
43
+
44
+ const hasPending = rawQueue.some((item: unknown) => {
45
+ const task = item as Record<string, string> | undefined;
46
+ return task?.taskKind === 'sleep_reflection' && (task?.status === 'pending' || task?.status === 'in_progress');
47
+ });
48
+ if (hasPending) {
49
+ return { text: 'A sleep_reflection task is already pending. Wait for it to complete or fail.' };
50
+ }
51
+
52
+ const now = new Date();
53
+ taskId = `manual_${now.getTime().toString(36).slice(-8)}`;
54
+ const nowIso = now.toISOString();
55
+
56
+ rawQueue.push({
57
+ id: taskId,
58
+ taskKind: 'sleep_reflection',
59
+ priority: 'high',
60
+ score: 50,
61
+ source: 'manual',
62
+ reason: 'Manual reflection triggered via /pd-reflect',
63
+ trigger_text_preview: 'User commanded /pd-reflect',
64
+ timestamp: nowIso,
65
+ enqueued_at: nowIso,
66
+ status: 'pending',
67
+ traceId: taskId,
68
+ retryCount: 0,
69
+ maxRetries: 1,
70
+ });
71
+
72
+ fs.writeFileSync(queuePath, JSON.stringify(rawQueue, null, 2), 'utf8');
73
+ } finally {
74
+ releaseLock();
75
+ }
76
+
77
+ return {
78
+ text: `Nocturnal reflection task enqueued: \`${taskId}\`\n\nIt will be processed in the next evolution worker cycle (~15s). Check .state/nocturnal/samples/ for results.`,
79
+ };
80
+ } catch (error) {
81
+ return {
82
+ text: `Failed to trigger reflection: ${String(error)}`,
83
+ isError: true,
84
+ };
85
+ }
86
+ },
87
+ };
@@ -57,11 +57,11 @@ export function handleRollbackImplCommand(ctx: PluginCommandContext): PluginComm
57
57
 
58
58
  // List active
59
59
  if (subcommand === 'list' || subcommand === '') {
60
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: Mutual recursion between helper functions - reordering would break logical grouping
60
+
61
61
  return _handleListActiveRollback(stateDir, isZh);
62
62
  }
63
63
 
64
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: Mutual recursion between helper functions - reordering would break logical grouping
64
+
65
65
  return _handleRollbackImpl(workspaceDir, stateDir, implId, reason, isZh, ctx.sessionId);
66
66
  }
67
67
 
@@ -103,7 +103,7 @@ function _handleListActiveRollback(
103
103
  return { text: output };
104
104
  }
105
105
 
106
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
106
+
107
107
  function _handleRollbackImpl(
108
108
  workspaceDir: string,
109
109
  stateDir: string,
@@ -138,7 +138,7 @@ function _handleRollbackImpl(
138
138
  // Step 1: Current active -> disabled
139
139
  transitionImplementationState(stateDir, implId, 'disabled');
140
140
 
141
- // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in if/else branches before use
141
+
142
142
  let restoredMessage: string;
143
143
 
144
144
  if (previousActiveId && allImpls.some((i) => i.id === previousActiveId)) {
@@ -44,9 +44,9 @@ Usage:
44
44
  };
45
45
  }
46
46
 
47
- // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in if/else branches before use
47
+
48
48
  let eventId: string | null;
49
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: triggerMethod is reserved for future extension - tracking rollback trigger source
49
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Reason: triggerMethod is reserved for future extension - tracking rollback trigger source
50
50
  const _triggerMethod = 'user_command' as const;
51
51
 
52
52
  if (args === 'last') {
@@ -29,11 +29,11 @@ export function handleSamplesCommand(ctx: PluginCommandContext): PluginCommandRe
29
29
  }
30
30
  const normalizedDecision = decision === 'approve' ? 'approved' : 'rejected';
31
31
  const note = noteParts.join(' ').trim();
32
- // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try block, catch has early return
32
+
33
33
  let record;
34
34
  try {
35
35
  record = wctx.trajectory.reviewCorrectionSample(sampleId, normalizedDecision, note);
36
- /* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: error handling only - returning failure response */
36
+ /* eslint-disable @typescript-eslint/no-unused-vars -- Reason: error handling only - returning failure response */
37
37
  } catch (error) {
38
38
  return {
39
39
  text: zh
@@ -20,7 +20,7 @@ function formatState(state: string): string {
20
20
  return `${icon} ${state}`;
21
21
  }
22
22
 
23
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: debug output builder requires all context parameters - refactoring would break API
23
+
24
24
  function buildOutput(
25
25
  workflowId: string,
26
26
  summary: ReturnType<InstanceType<typeof WorkflowStore>['getWorkflow']>,
@@ -15,7 +15,7 @@
15
15
  export class PdError extends Error {
16
16
  constructor(
17
17
  message: string,
18
- // eslint-disable-next-line no-unused-vars -- public parameter property, accessed externally
18
+
19
19
  public readonly code: string,
20
20
  options?: { cause?: unknown }
21
21
  ) {
@@ -300,7 +300,7 @@ export function getEffectiveThresholds(stateDir: string): ThresholdValues {
300
300
  * @param reason - Reason for the adjustment (required for tracking)
301
301
  * @returns UpdateThresholdResult
302
302
  */
303
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: threshold state update requires all parameters - refactoring would break API
303
+
304
304
  export function updateThresholdState(
305
305
  stateDir: string,
306
306
  thresholdName: ThresholdName,
@@ -134,7 +134,7 @@ export function writeManifest(
134
134
  });
135
135
  }
136
136
 
137
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: entry source writer requires state + id + source - refactoring would break API
137
+
138
138
  export function writeEntrySource(
139
139
  stateDir: string,
140
140
  implId: string,
@@ -189,7 +189,7 @@ export function loadEntrySource(stateDir: string, implId: string): string | null
189
189
  *
190
190
  * Idempotent: calling again with the same implId will NOT overwrite an existing entry.js.
191
191
  */
192
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: asset dir creation requires state + id + version - refactoring would break API
192
+
193
193
  export function createImplementationAssetDir(
194
194
  stateDir: string,
195
195
  implId: string,
@@ -290,7 +290,7 @@ export class PainConfig {
290
290
  /**
291
291
  * Basic validation for critical settings
292
292
  */
293
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: validate is a pure validation function that modifies settings in place, no this reference needed
293
+
294
294
  private validate(settings: PainSettings): void {
295
295
  // Ensure intervals are positive
296
296
  if (settings.intervals.worker_poll_ms < 1000) settings.intervals.worker_poll_ms = 15 * 60 * 1000;
@@ -82,7 +82,7 @@ export async function addDiagnosticianTask(
82
82
  ): Promise<void> {
83
83
  const filePath = resolveTasksPath(stateDir);
84
84
  await withLockAsync(filePath, async () => {
85
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function defined later in file, hoisting pattern
85
+
86
86
  const store = readTaskStoreSync(filePath);
87
87
  store.tasks[taskId] = {
88
88
  prompt,
@@ -105,7 +105,7 @@ export async function completeDiagnosticianTask(
105
105
  ): Promise<void> {
106
106
  const filePath = resolveTasksPath(stateDir);
107
107
  await withLockAsync(filePath, async () => {
108
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function defined later in file, hoisting pattern
108
+
109
109
  const store = readTaskStoreSync(filePath);
110
110
  delete store.tasks[taskId];
111
111
  const tmpPath = filePath + '.tmp';
@@ -80,7 +80,7 @@ export function loadKeywordStore(stateDir: string, language?: 'zh' | 'en'): Empa
80
80
  try {
81
81
  if (!fs.existsSync(filePath)) {
82
82
  const store = createDefaultKeywordStore(language);
83
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function defined later in file, hoisting pattern
83
+
84
84
  saveKeywordStore(stateDir, store);
85
85
  return store;
86
86
  }
@@ -92,7 +92,7 @@ export function loadKeywordStore(stateDir: string, language?: 'zh' | 'en'): Empa
92
92
  if (!parsed.terms || !parsed.stats || !parsed.version) {
93
93
  console.warn('[PD:Empathy] Invalid keyword store format, creating default');
94
94
  const store = createDefaultKeywordStore(language);
95
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function defined later in file, hoisting pattern
95
+
96
96
  saveKeywordStore(stateDir, store);
97
97
  return store;
98
98
  }
@@ -101,7 +101,7 @@ export function loadKeywordStore(stateDir: string, language?: 'zh' | 'en'): Empa
101
101
  } catch (e) {
102
102
  console.warn(`[PD:Empathy] Failed to load keyword store: ${e}`);
103
103
  const store = createDefaultKeywordStore(language);
104
- // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function defined later in file, hoisting pattern
104
+
105
105
  saveKeywordStore(stateDir, store);
106
106
  return store;
107
107
  }
@@ -107,7 +107,7 @@ export class EventLog {
107
107
  this.record('warn', 'failure', sessionId, { message, ...context });
108
108
  }
109
109
 
110
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: event record requires type + category + session + data - refactoring would break internal API
110
+
111
111
  private record(
112
112
  type: EventType,
113
113
  category: EventCategory,
@@ -134,7 +134,7 @@ export class EventLog {
134
134
  }
135
135
  }
136
136
 
137
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: utility method doesn't require this - pure function
137
+
138
138
  private formatDate(date: Date): string {
139
139
  return date.toISOString().split('T')[0];
140
140
  }
@@ -160,7 +160,7 @@ export class EventLog {
160
160
  }
161
161
 
162
162
  if (entry.type === 'tool_call') {
163
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: data used for type narrowing only, actual fields accessed via stats
163
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Reason: data used for type narrowing only, actual fields accessed via stats
164
164
  const _data = entry.data as unknown as ToolCallEventData;
165
165
  stats.tools.total++;
166
166
  if (entry.category === 'success') stats.tools.success++;
@@ -243,7 +243,7 @@ export class EventLog {
243
243
  return this.eventBuffer.map((entry) => ({ ...entry, data: { ...entry.data } }));
244
244
  }
245
245
 
246
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: getEventDedupKey is a pure key-generation function, no this reference needed
246
+
247
247
  private getEventDedupKey(entry: EventLogEntry): string {
248
248
  const eventId = typeof (entry.data as { eventId?: unknown } | undefined)?.eventId === 'string'
249
249
  ? String((entry.data as { eventId?: string }).eventId)
@@ -459,7 +459,7 @@ export class EventLog {
459
459
  * Rollback an empathy event by ID.
460
460
  * Returns the rolled back score, or 0 if event not found.
461
461
  */
462
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: rollback requires eventId + sessionId + reason + triggeredBy - refactoring would break API
462
+
463
463
  rollbackEmpathyEvent(eventId: string, sessionId: string | undefined, reason: string, triggeredBy: 'user_command' | 'natural_language' | 'system'): number {
464
464
  const allEvents = this.getMergedEvents();
465
465
  let foundEvent: { entry: EventLogEntry; data: PainSignalEventData } | null = null;
@@ -323,7 +323,7 @@ export class EvolutionEngine {
323
323
 
324
324
  // ===== 事件管理 =====
325
325
 
326
- // eslint-disable-next-line @typescript-eslint/max-params -- Reason: event creation requires all event fields - refactoring would break internal structure
326
+
327
327
  private createEvent(
328
328
  type: 'success' | 'failure',
329
329
  taskHash: string,
@@ -385,7 +385,7 @@ export class EvolutionEngine {
385
385
  return this.createNewScorecard();
386
386
  }
387
387
 
388
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: createNewScorecard is a factory function, no this reference needed
388
+
389
389
  private createNewScorecard(): EvolutionScorecard {
390
390
  const now = new Date().toISOString();
391
391
  return {
@@ -444,7 +444,7 @@ export class EvolutionEngine {
444
444
  } catch (e) {
445
445
  console.error(`[Evolution] Failed to save scorecard: ${String(e)}`);
446
446
  this.scheduleRetrySave();
447
- /* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: intentionally ignored - cleanup failure should not mask original error */
447
+ /* eslint-disable @typescript-eslint/no-unused-vars -- Reason: intentionally ignored - cleanup failure should not mask original error */
448
448
  try { fs.unlinkSync(tempPath); } catch (_e) {
449
449
  // Cleanup failure intentionally ignored - should not mask original error
450
450
  }
@@ -527,7 +527,7 @@ export class EvolutionEngine {
527
527
 
528
528
  // ===== 工具方法 =====
529
529
 
530
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: generateId is a pure utility function, no this reference needed
530
+
531
531
  private generateId(): string {
532
532
  return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
533
533
  }
@@ -264,7 +264,7 @@ export class EvolutionLogger {
264
264
  durationMs?: number;
265
265
  principlesGenerated?: number;
266
266
  }): void {
267
- // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all if/else branches
267
+
268
268
  let summary: string;
269
269
  if (params.resolution === 'marker_detected' || params.resolution === 'late_marker_principle_created') {
270
270
  summary = `任务 ${params.taskId} 完成,已生成 ${params.principlesGenerated || 0} 条原则`;
@@ -22,7 +22,7 @@ import type {
22
22
  import { isCompleteDetectorMetadata } from './evolution-types.js';
23
23
  import { updateTrainingStore } from './principle-tree-ledger.js';
24
24
 
25
- /* eslint-disable no-unused-vars -- Reason: interface method params are type signatures, implementations use actual values */
25
+
26
26
  export interface EvolutionReducer {
27
27
 
28
28
  emit(_event: EvolutionLoopEvent): void;
@@ -72,7 +72,7 @@ export interface EvolutionReducer {
72
72
  lastPromotedAt: string | null;
73
73
  };
74
74
  }
75
- /* eslint-enable no-unused-vars */
75
+
76
76
 
77
77
  const PROBATION_SUCCESS_THRESHOLD = 3;
78
78
  const CIRCUIT_BREAKER_THRESHOLD = 3;
@@ -663,7 +663,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
663
663
  });
664
664
  }
665
665
 
666
- // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: event timestamp intentionally unused, only data payload matters
666
+
667
667
  private onPainDetected(data: PainDetectedData, _eventTs: string): void {
668
668
  const trigger = String(data.reason ?? data.source ?? 'unknown trigger');
669
669