principles-disciple 1.11.0 → 1.13.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 (233) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -0
  3. package/esbuild.config.js +1 -1
  4. package/openclaw.plugin.json +4 -4
  5. package/package.json +2 -3
  6. package/run-nocturnal.mjs +30 -0
  7. package/scripts/db-migrate.mjs +170 -0
  8. package/scripts/sync-plugin.mjs +250 -6
  9. package/src/commands/archive-impl.ts +136 -0
  10. package/src/commands/capabilities.ts +4 -2
  11. package/src/commands/context.ts +5 -1
  12. package/src/commands/disable-impl.ts +151 -0
  13. package/src/commands/evolution-status.ts +64 -19
  14. package/src/commands/export.ts +8 -6
  15. package/src/commands/focus.ts +8 -20
  16. package/src/commands/nocturnal-review.ts +5 -7
  17. package/src/commands/nocturnal-rollout.ts +1 -12
  18. package/src/commands/nocturnal-train.ts +21 -47
  19. package/src/commands/pain.ts +10 -5
  20. package/src/commands/principle-rollback.ts +4 -2
  21. package/src/commands/promote-impl.ts +274 -0
  22. package/src/commands/rollback-impl.ts +234 -0
  23. package/src/commands/rollback.ts +6 -3
  24. package/src/commands/samples.ts +2 -0
  25. package/src/commands/thinking-os.ts +3 -4
  26. package/src/commands/workflow-debug.ts +2 -1
  27. package/src/config/errors.ts +1 -0
  28. package/src/core/AGENTS.md +34 -0
  29. package/src/core/adaptive-thresholds.ts +4 -3
  30. package/src/core/code-implementation-storage.ts +241 -0
  31. package/src/core/config.ts +5 -2
  32. package/src/core/control-ui-db.ts +29 -10
  33. package/src/core/detection-funnel.ts +12 -7
  34. package/src/core/diagnostician-task-store.ts +156 -0
  35. package/src/core/dictionary.ts +4 -4
  36. package/src/core/empathy-keyword-matcher.ts +7 -3
  37. package/src/core/empathy-types.ts +13 -2
  38. package/src/core/event-log.ts +14 -6
  39. package/src/core/evolution-engine.ts +27 -31
  40. package/src/core/evolution-logger.ts +3 -2
  41. package/src/core/evolution-reducer.ts +110 -31
  42. package/src/core/evolution-types.ts +10 -0
  43. package/src/core/external-training-contract.ts +1 -0
  44. package/src/core/focus-history.ts +38 -24
  45. package/src/core/hygiene/tracker.ts +10 -6
  46. package/src/core/init.ts +5 -2
  47. package/src/core/migration.ts +3 -3
  48. package/src/core/model-deployment-registry.ts +6 -4
  49. package/src/core/model-training-registry.ts +5 -3
  50. package/src/core/nocturnal-arbiter.ts +13 -14
  51. package/src/core/nocturnal-artifact-lineage.ts +117 -0
  52. package/src/core/nocturnal-artificer.ts +257 -0
  53. package/src/core/nocturnal-candidate-scoring.ts +4 -2
  54. package/src/core/nocturnal-compliance.ts +67 -19
  55. package/src/core/nocturnal-dataset.ts +95 -2
  56. package/src/core/nocturnal-executability.ts +2 -3
  57. package/src/core/nocturnal-export.ts +6 -3
  58. package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
  59. package/src/core/nocturnal-trajectory-extractor.ts +10 -3
  60. package/src/core/nocturnal-trinity.ts +319 -61
  61. package/src/core/pain-context-extractor.ts +29 -15
  62. package/src/core/pain.ts +7 -5
  63. package/src/core/path-resolver.ts +16 -15
  64. package/src/core/paths.ts +2 -1
  65. package/src/core/pd-task-reconciler.ts +463 -0
  66. package/src/core/pd-task-service.ts +42 -0
  67. package/src/core/pd-task-store.ts +77 -0
  68. package/src/core/pd-task-types.ts +128 -0
  69. package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
  70. package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
  71. package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
  72. package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
  73. package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
  74. package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
  75. package/src/core/principle-training-state.ts +95 -370
  76. package/src/core/principle-tree-ledger.ts +733 -0
  77. package/src/core/principle-tree-migration.ts +195 -0
  78. package/src/core/profile.ts +3 -1
  79. package/src/core/promotion-gate.ts +14 -18
  80. package/src/core/replay-engine.ts +562 -0
  81. package/src/core/risk-calculator.ts +6 -4
  82. package/src/core/rule-host-helpers.ts +39 -0
  83. package/src/core/rule-host-types.ts +82 -0
  84. package/src/core/rule-host.ts +245 -0
  85. package/src/core/rule-implementation-runtime.ts +38 -0
  86. package/src/core/schema/db-types.ts +16 -0
  87. package/src/core/schema/index.ts +26 -0
  88. package/src/core/schema/migration-runner.ts +207 -0
  89. package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
  90. package/src/core/schema/migrations/002-init-central.ts +122 -0
  91. package/src/core/schema/migrations/003-init-workflow.ts +55 -0
  92. package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
  93. package/src/core/schema/migrations/index.ts +31 -0
  94. package/src/core/schema/schema-definitions.ts +650 -0
  95. package/src/core/session-tracker.ts +6 -4
  96. package/src/core/shadow-observation-registry.ts +6 -3
  97. package/src/core/system-logger.ts +2 -2
  98. package/src/core/thinking-models.ts +182 -46
  99. package/src/core/thinking-os-parser.ts +156 -0
  100. package/src/core/training-program.ts +7 -7
  101. package/src/core/trajectory.ts +42 -36
  102. package/src/core/workspace-context.ts +77 -11
  103. package/src/core/workspace-dir-validation.ts +152 -0
  104. package/src/hooks/AGENTS.md +31 -0
  105. package/src/hooks/bash-risk.ts +3 -1
  106. package/src/hooks/edit-verification.ts +9 -5
  107. package/src/hooks/gate-block-helper.ts +5 -1
  108. package/src/hooks/gate.ts +152 -5
  109. package/src/hooks/gfi-gate.ts +9 -2
  110. package/src/hooks/lifecycle-routing.ts +124 -0
  111. package/src/hooks/lifecycle.ts +12 -12
  112. package/src/hooks/llm.ts +17 -109
  113. package/src/hooks/message-sanitize.ts +5 -3
  114. package/src/hooks/pain.ts +19 -15
  115. package/src/hooks/progressive-trust-gate.ts +7 -1
  116. package/src/hooks/prompt.ts +169 -60
  117. package/src/hooks/subagent.ts +5 -4
  118. package/src/hooks/thinking-checkpoint.ts +2 -0
  119. package/src/hooks/trajectory-collector.ts +15 -12
  120. package/src/http/principles-console-route.ts +31 -68
  121. package/src/i18n/commands.ts +2 -2
  122. package/src/index.ts +130 -40
  123. package/src/service/central-database.ts +131 -43
  124. package/src/service/central-health-service.ts +47 -0
  125. package/src/service/central-overview-service.ts +135 -0
  126. package/src/service/central-sync-service.ts +87 -0
  127. package/src/service/control-ui-query-service.ts +46 -36
  128. package/src/service/event-log-auditor.ts +261 -0
  129. package/src/service/evolution-query-service.ts +23 -22
  130. package/src/service/evolution-worker.ts +565 -261
  131. package/src/service/health-query-service.ts +213 -36
  132. package/src/service/nocturnal-runtime.ts +8 -4
  133. package/src/service/nocturnal-service.ts +503 -59
  134. package/src/service/nocturnal-target-selector.ts +5 -7
  135. package/src/service/runtime-summary-service.ts +2 -1
  136. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
  137. package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
  138. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
  139. package/src/service/subagent-workflow/index.ts +2 -0
  140. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
  141. package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
  142. package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
  143. package/src/service/subagent-workflow/types.ts +9 -4
  144. package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
  145. package/src/service/subagent-workflow/workflow-store.ts +71 -11
  146. package/src/service/trajectory-service.ts +2 -1
  147. package/src/tools/critique-prompt.ts +1 -1
  148. package/src/tools/deep-reflect.ts +175 -209
  149. package/src/tools/model-index.ts +2 -1
  150. package/src/types/event-types.ts +2 -2
  151. package/src/types/principle-tree-schema.ts +29 -23
  152. package/src/utils/file-lock.ts +5 -3
  153. package/src/utils/io.ts +5 -2
  154. package/src/utils/nlp.ts +5 -46
  155. package/src/utils/node-vm-polyfill.ts +11 -0
  156. package/src/utils/plugin-logger.ts +2 -0
  157. package/src/utils/retry.ts +572 -0
  158. package/src/utils/subagent-probe.ts +1 -1
  159. package/templates/langs/en/core/AGENTS.md +0 -13
  160. package/templates/langs/en/core/SOUL.md +1 -31
  161. package/templates/langs/en/core/TOOLS.md +0 -4
  162. package/templates/langs/en/principles/THINKING_OS.md +77 -0
  163. package/templates/langs/en/skills/admin/SKILL.md +0 -1
  164. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
  165. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
  166. package/templates/langs/zh/core/AGENTS.md +0 -22
  167. package/templates/langs/zh/core/SOUL.md +1 -31
  168. package/templates/langs/zh/core/TOOLS.md +0 -4
  169. package/templates/langs/zh/principles/THINKING_OS.md +77 -0
  170. package/templates/langs/zh/skills/admin/SKILL.md +0 -1
  171. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
  172. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
  173. package/tests/commands/evolution-status.test.ts +119 -0
  174. package/tests/commands/implementation-lifecycle.test.ts +362 -0
  175. package/tests/core/code-implementation-storage.test.ts +398 -0
  176. package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
  177. package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
  178. package/tests/core/nocturnal-artificer.test.ts +241 -0
  179. package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
  180. package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
  181. package/tests/core/pd-task-store.test.ts +126 -0
  182. package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
  183. package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
  184. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
  185. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
  186. package/tests/core/principle-training-state.test.ts +228 -1
  187. package/tests/core/principle-tree-ledger.test.ts +423 -0
  188. package/tests/core/regression-v1-9-1.test.ts +265 -0
  189. package/tests/core/replay-engine.test.ts +234 -0
  190. package/tests/core/rule-host-helpers.test.ts +120 -0
  191. package/tests/core/rule-host.test.ts +389 -0
  192. package/tests/core/rule-implementation-runtime.test.ts +64 -0
  193. package/tests/core/workspace-context.test.ts +53 -0
  194. package/tests/core/workspace-dir-validation.test.ts +272 -0
  195. package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
  196. package/tests/hooks/pain.test.ts +74 -10
  197. package/tests/hooks/prompt.test.ts +63 -1
  198. package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
  199. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
  200. package/tests/service/data-endpoints-regression.test.ts +834 -0
  201. package/tests/service/evolution-worker.test.ts +0 -123
  202. package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
  203. package/tests/utils/nlp.test.ts +1 -19
  204. package/tests/utils/retry.test.ts +327 -0
  205. package/ui/src/App.tsx +1 -1
  206. package/ui/src/api.ts +4 -0
  207. package/ui/src/charts.tsx +366 -0
  208. package/ui/src/components/WorkspaceConfig.tsx +107 -75
  209. package/ui/src/i18n/ui.ts +89 -31
  210. package/ui/src/pages/EvolutionPage.tsx +1 -1
  211. package/ui/src/pages/OverviewPage.tsx +441 -81
  212. package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
  213. package/ui/src/styles.css +43 -0
  214. package/ui/src/types.ts +17 -1
  215. package/src/agents/nocturnal-dreamer.md +0 -152
  216. package/src/agents/nocturnal-philosopher.md +0 -138
  217. package/src/agents/nocturnal-reflector.md +0 -126
  218. package/src/agents/nocturnal-scribe.md +0 -164
  219. package/templates/workspace/.principles/00-kernel.md +0 -51
  220. package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  221. package/templates/workspace/.principles/PRINCIPLES.md +0 -20
  222. package/templates/workspace/.principles/PROFILE.json +0 -54
  223. package/templates/workspace/.principles/PROFILE.schema.json +0 -56
  224. package/templates/workspace/.principles/THINKING_OS.md +0 -64
  225. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  226. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  227. package/templates/workspace/.principles/models/_INDEX.md +0 -27
  228. package/templates/workspace/.principles/models/first_principles.md +0 -62
  229. package/templates/workspace/.principles/models/marketing_4p.md +0 -52
  230. package/templates/workspace/.principles/models/porter_five.md +0 -63
  231. package/templates/workspace/.principles/models/swot.md +0 -60
  232. package/templates/workspace/.principles/models/user_story_map.md +0 -63
  233. package/templates/workspace/.state/WORKBOARD.json +0 -4
@@ -1,10 +1,10 @@
1
1
  import Database from 'better-sqlite3';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
- import type { WorkflowRow, WorkflowEventRow, WorkflowState, WorkflowTransport } from './types.js';
4
+ import type { WorkflowRow, WorkflowEventRow, WorkflowState } from './types.js';
5
5
  import type { DreamerOutput, PhilosopherOutput } from '../../core/nocturnal-trinity.js';
6
6
 
7
- const SCHEMA_VERSION = 1;
7
+ const SCHEMA_VERSION = 2;
8
8
 
9
9
  const DEFAULT_BUSY_TIMEOUT_MS = 5000;
10
10
 
@@ -42,7 +42,7 @@ export class WorkflowStore {
42
42
  CREATE TABLE IF NOT EXISTS schema_version (
43
43
  version INTEGER NOT NULL
44
44
  );
45
-
45
+
46
46
  CREATE TABLE IF NOT EXISTS subagent_workflows (
47
47
  workflow_id TEXT PRIMARY KEY,
48
48
  workflow_type TEXT NOT NULL,
@@ -55,6 +55,7 @@ export class WorkflowStore {
55
55
  created_at INTEGER NOT NULL,
56
56
  updated_at INTEGER NOT NULL,
57
57
  last_observed_at INTEGER,
58
+ duration_ms INTEGER,
58
59
  metadata_json TEXT NOT NULL DEFAULT '{}'
59
60
  );
60
61
 
@@ -89,15 +90,29 @@ export class WorkflowStore {
89
90
  `);
90
91
 
91
92
  const row = this.db.prepare('SELECT version FROM schema_version LIMIT 1').get() as { version?: number } | undefined;
92
- if (!row) {
93
+ const currentVersion = row?.version ?? 0;
94
+ if (currentVersion === 0) {
93
95
  this.db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(SCHEMA_VERSION);
94
- } else if (row.version !== SCHEMA_VERSION) {
96
+ } else if (currentVersion < SCHEMA_VERSION) {
97
+ // Run migrations for existing databases
98
+ this.runMigrations(currentVersion, SCHEMA_VERSION);
95
99
  this.db.prepare('UPDATE schema_version SET version = ?').run(SCHEMA_VERSION);
96
100
  }
97
101
  }
98
-
102
+
103
+ private runMigrations(fromVersion: number, toVersion: number): void {
104
+ if (fromVersion < 2 && toVersion >= 2) {
105
+ // v1 → v2: Add duration_ms column for adaptive timeout tracking
106
+ try {
107
+ this.db.exec('ALTER TABLE subagent_workflows ADD COLUMN duration_ms INTEGER');
108
+ console.info(`[PD:WorkflowStore] Schema migration v${fromVersion} → v${toVersion}: added duration_ms column`);
109
+ } catch {
110
+ // Column may already exist if migration was partially applied
111
+ }
112
+ }
113
+ }
114
+
99
115
  createWorkflow(row: Omit<WorkflowRow, 'cleanup_state'>): void {
100
- const now = Date.now();
101
116
  this.db.prepare(`
102
117
  INSERT INTO subagent_workflows (
103
118
  workflow_id, workflow_type, transport, parent_session_id, child_session_key,
@@ -201,7 +216,23 @@ export class WorkflowStore {
201
216
  ORDER BY last_observed_at ASC
202
217
  `).all(cutoff) as WorkflowRow[];
203
218
  }
219
+
220
+ /** List all workflows, optionally filtered by state. */
221
+ listWorkflows(state?: string): WorkflowRow[] {
222
+ if (state) {
223
+ return this.db.prepare(`
224
+ SELECT * FROM subagent_workflows
225
+ WHERE state = ?
226
+ ORDER BY created_at DESC
227
+ `).all(state) as WorkflowRow[];
228
+ }
229
+ return this.db.prepare(`
230
+ SELECT * FROM subagent_workflows
231
+ ORDER BY created_at DESC
232
+ `).all() as WorkflowRow[];
233
+ }
204
234
 
235
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Store interface requires full event context (workflowId, fromState, toState, reason, payload) */
205
236
  recordEvent(
206
237
  workflowId: string,
207
238
  eventType: string,
@@ -237,6 +268,7 @@ export class WorkflowStore {
237
268
  * idempotencyKey must be unique per (workflow_id, stage). If a row with the
238
269
  * same idempotency_key already exists, this is a no-op (idempotent).
239
270
  */
271
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Store interface requires workflowId, stage, output, and idempotencyKey */
240
272
  recordStageOutput(
241
273
  workflowId: string,
242
274
  stage: 'dreamer' | 'philosopher',
@@ -262,24 +294,24 @@ export class WorkflowStore {
262
294
  * Get all stage outputs for a workflow (NOC-13 crash recovery).
263
295
  * Returns outputs ordered by created_at ascending.
264
296
  */
265
- getStageOutputs(workflowId: string): Array<{
297
+ getStageOutputs(workflowId: string): {
266
298
  stage: string;
267
299
  output: DreamerOutput | PhilosopherOutput;
268
300
  idempotencyKey: string;
269
301
  createdAt: number;
270
- }> {
302
+ }[] {
271
303
  const rows = this.db.prepare(`
272
304
  SELECT workflow_id, stage, output_json, idempotency_key, created_at
273
305
  FROM subagent_workflow_stage_outputs
274
306
  WHERE workflow_id = ?
275
307
  ORDER BY created_at ASC
276
- `).all(workflowId) as Array<{
308
+ `).all(workflowId) as {
277
309
  workflow_id: string;
278
310
  stage: string;
279
311
  output_json: string;
280
312
  idempotency_key: string;
281
313
  created_at: number;
282
- }>;
314
+ }[];
283
315
 
284
316
  return rows.map(row => ({
285
317
  workflowId: row.workflow_id,
@@ -325,4 +357,32 @@ export class WorkflowStore {
325
357
  deleteWorkflow(workflowId: string): void {
326
358
  this.db.prepare('DELETE FROM subagent_workflows WHERE workflow_id = ?').run(workflowId);
327
359
  }
360
+
361
+ /**
362
+ * Record the actual completion duration for a workflow.
363
+ * Used by adaptive timeout learning.
364
+ */
365
+ recordDuration(workflowId: string, durationMs: number): void {
366
+ this.db.prepare(`
367
+ UPDATE subagent_workflows SET duration_ms = ?, updated_at = ? WHERE workflow_id = ?
368
+ `).run(durationMs, Date.now(), workflowId);
369
+ }
370
+
371
+ /**
372
+ * Get completion durations for a specific workflow type, ordered by most recent first.
373
+ * Returns an array of duration_ms values for adaptive timeout calculation.
374
+ */
375
+ getCompletionDurations(workflowType: string, limit = 50): number[] {
376
+ const rows = this.db.prepare(`
377
+ SELECT duration_ms FROM subagent_workflows
378
+ WHERE workflow_type = ?
379
+ AND state = 'completed'
380
+ AND duration_ms IS NOT NULL
381
+ AND duration_ms > 0
382
+ ORDER BY created_at DESC
383
+ LIMIT ?
384
+ `).all(workflowType, limit) as { duration_ms: number }[];
385
+
386
+ return rows.map(r => r.duration_ms);
387
+ }
328
388
  }
@@ -6,7 +6,8 @@ export const TrajectoryService: OpenClawPluginService = {
6
6
  id: 'principles-disciple-trajectory',
7
7
  start(ctx: OpenClawPluginServiceContext): void {
8
8
  if (!ctx.workspaceDir) return;
9
- WorkspaceContext.fromHookContext(ctx).trajectory;
9
+ // Trigger lazy initialization of trajectory registry
10
+ void WorkspaceContext.fromHookContext(ctx).trajectory;
10
11
  },
11
12
  stop(ctx: OpenClawPluginServiceContext): void {
12
13
  if (!ctx.workspaceDir) return;
@@ -26,7 +26,7 @@ export function buildCritiquePromptV2(
26
26
  api?: OpenClawPluginApi;
27
27
  }
28
28
  ): string {
29
- const { context, depth = 2, model_id, workspaceDir, api } = params;
29
+ const { context, depth = 2, workspaceDir, api } = params;
30
30
 
31
31
  // 1. 确定工作区目录 (优先级:显式传入 > api.config > official API > api.resolvePath)
32
32
  const effectiveWorkspaceDir = workspaceDir
@@ -1,94 +1,15 @@
1
1
  import type { OpenClawPluginApi } from '../openclaw-sdk.js';
2
2
  import { Type } from '@sinclair/typebox';
3
3
  import * as fs from 'fs';
4
- import * as path from 'node:path';
5
4
  import { EventLogService } from '../core/event-log.js';
6
- import { buildCritiquePromptV2 } from './critique-prompt.js';
7
5
  import { resolvePdPath } from '../core/paths.js';
8
6
  import { resolveWorkspaceDirFromApi } from '../core/path-resolver.js';
9
- import { reflectionLogRetentionDays } from '../types.js';
10
7
  import {
11
8
  DeepReflectWorkflowManager,
12
9
  deepReflectWorkflowSpec,
13
10
  type DeepReflectTaskInput,
14
11
  } from '../service/subagent-workflow/index.js';
15
12
 
16
- function writeToReflectionLog(
17
- workspaceDir: string,
18
- context: string,
19
- insights: string,
20
- modelId?: string,
21
- depth?: number
22
- ): void {
23
- const reflectionLogPath = resolvePdPath(workspaceDir, 'REFLECTION_LOG');
24
- const memoryDir = path.dirname(reflectionLogPath);
25
- if (!fs.existsSync(memoryDir)) {
26
- fs.mkdirSync(memoryDir, { recursive: true });
27
- }
28
-
29
- const timestamp = new Date().toISOString();
30
- const entry = `
31
- ---
32
- ## Reflection at ${timestamp}
33
- **Model**: ${modelId || 'auto-select'}
34
- **Depth**: ${depth || 2}
35
-
36
- ### Context
37
- ${context.substring(0, 500)}${context.length > 500 ? '...' : ''}
38
-
39
- ### Insights
40
- ${insights}
41
-
42
- `;
43
-
44
- const header = `# Reflection Log\n\n> Auto-generated by Deep Reflection Tool\n> Retention: ${reflectionLogRetentionDays} days\n`;
45
-
46
- let existingContent = '';
47
- if (fs.existsSync(reflectionLogPath)) {
48
- existingContent = fs.readFileSync(reflectionLogPath, 'utf8');
49
- }
50
-
51
- const newContent = header + entry + existingContent.replace(header, '');
52
-
53
- const tempPath = `${reflectionLogPath}.tmp`;
54
- fs.writeFileSync(tempPath, newContent, 'utf8');
55
- fs.renameSync(tempPath, reflectionLogPath);
56
- }
57
-
58
- function cleanupReflectionLog(workspaceDir: string): void {
59
- const reflectionLogPath = resolvePdPath(workspaceDir, 'REFLECTION_LOG');
60
-
61
- if (!fs.existsSync(reflectionLogPath)) return;
62
-
63
- const content = fs.readFileSync(reflectionLogPath, 'utf8');
64
- const cutoffDate = new Date();
65
- cutoffDate.setDate(cutoffDate.getDate() - reflectionLogRetentionDays);
66
-
67
- const entryPattern = /---\n## Reflection at (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})/g;
68
- const parts = content.split(entryPattern);
69
-
70
- const header = parts[0];
71
- const validEntries: string[] = [];
72
-
73
- for (let i = 1; i < parts.length; i += 2) {
74
- const timestamp = parts[i];
75
- const entryContent = parts[i + 1] || '';
76
-
77
- const entryDate = new Date(timestamp);
78
- if (entryDate >= cutoffDate) {
79
- validEntries.push(`---\n## Reflection at ${timestamp}${entryContent}`);
80
- }
81
- }
82
-
83
- if (validEntries.length < (parts.length - 1) / 2) {
84
- const newContent = header + validEntries.join('');
85
-
86
- const tempPath = `${reflectionLogPath}.tmp`;
87
- fs.writeFileSync(tempPath, newContent, 'utf8');
88
- fs.renameSync(tempPath, reflectionLogPath);
89
- }
90
- }
91
-
92
13
  interface DeepReflectionConfig {
93
14
  enabled: boolean;
94
15
  mode: 'auto' | 'forced' | 'disabled';
@@ -176,111 +97,164 @@ export function createDeepReflectTool(api: OpenClawPluginApi) {
176
97
  async execute(
177
98
  _toolCallId: string,
178
99
  rawParams: Record<string, unknown>
179
- ): Promise<{ content: Array<{ type: string; text: string }> }> {
100
+ ): Promise<{ content: { type: string; text: string }[] }> {
180
101
  const context = readStringParam(rawParams, 'context') || '';
181
102
  const depth = readNumberParam(rawParams, 'depth') ?? 2;
182
103
  const model_id = readStringParam(rawParams, 'model_id');
183
104
 
184
105
  if (!context) {
185
- return {
186
- content: [{
187
- type: 'text',
188
- text: '❌ 错误: 必须提供反思上下文 (context)。'
189
- }]
190
- };
106
+ return { content: [{ type: 'text', text: '❌ 错误: 必须提供反思上下文 (context)。' }] };
191
107
  }
192
108
 
193
- const effectiveWorkspaceDir =
194
- (api.config?.workspaceDir as string)
195
- || resolveWorkspaceDirFromApi(api)
196
- || api.resolvePath?.('.');
197
-
109
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
110
+ const effectiveWorkspaceDir = resolveReflectionWorkspace(api);
198
111
  if (!effectiveWorkspaceDir) {
199
- return {
200
- content: [{
201
- type: 'text',
202
- text: '❌ 反思执行失败: Workspace directory is required for deep reflection。请检查 API 配置或网络连接。'
203
- }]
204
- };
112
+ return { content: [{ type: 'text', text: '❌ 反思执行失败: Workspace directory is required for deep reflection。请检查 API 配置或网络连接。' }] };
205
113
  }
206
114
 
207
115
  const config = loadConfig(effectiveWorkspaceDir, api);
208
116
  if (config.mode === 'disabled' || !config.enabled) {
209
- return {
210
- content: [{
211
- type: 'text',
212
- text: '⏭️ Deep Reflection 已禁用。'
213
- }]
214
- };
117
+ return { content: [{ type: 'text', text: '⏭️ Deep Reflection 已禁用。' }] };
215
118
  }
216
119
 
217
120
  if (model_id) {
218
121
  safeLog(api, 'warn', `[DeepReflect] The 'model_id' parameter is deprecated. The agent will now auto-select models based on the context index.`);
219
122
  }
220
123
 
221
- const stateDir = resolvePdPath(effectiveWorkspaceDir, 'STATE_DIR');
222
- const eventLog = EventLogService.get(stateDir, api.logger);
223
-
224
124
  try {
225
- // Use workspaceDir as parentSessionId proxy (OpenClaw does not expose sessionId to tools)
226
- const parentSessionId = effectiveWorkspaceDir.replace(/[^a-zA-Z0-9_-]/g, '_').substring(0, 64);
227
-
228
- const manager = new DeepReflectWorkflowManager({
229
- workspaceDir: effectiveWorkspaceDir,
230
- logger: api.logger,
231
- subagent: api.runtime.subagent as any,
232
- });
233
-
234
- const taskInput: DeepReflectTaskInput = { context, depth, model_id };
235
-
236
- const handle = await manager.startWorkflow(deepReflectWorkflowSpec, {
237
- parentSessionId,
238
- workspaceDir: effectiveWorkspaceDir,
239
- taskInput,
240
- });
241
-
242
- // Wait for workflow completion via polling
243
- const startTime = Date.now();
244
- const timeoutMs = config.timeout_ms ?? 60000;
245
- const pollInterval = 500;
246
-
247
- while (Date.now() - startTime < timeoutMs) {
248
- await new Promise(resolve => setTimeout(resolve, pollInterval));
249
- const workflow = (manager as any).store.getWorkflow(handle.workflowId);
250
- if (!workflow) break;
251
-
252
- if (workflow.state === 'completed') {
253
- // Extract insights from reflection log (written by persistResult)
254
- const reflectionLogPath = resolvePdPath(effectiveWorkspaceDir, 'REFLECTION_LOG');
255
- let insights = '';
256
- if (fs.existsSync(reflectionLogPath)) {
257
- const content = fs.readFileSync(reflectionLogPath, 'utf8');
258
- // Extract most recent entry
259
- const match = content.match(/### Insights\n([\s\S]*?)(?=---|$)/);
260
- if (match) {
261
- insights = match[1].trim();
262
- }
263
- }
264
-
265
- if (eventLog) {
266
- eventLog.recordDeepReflection(handle.childSessionKey, {
267
- modelId: model_id || 'auto-select',
268
- modelSelectionMode: model_id ? 'manual' : 'auto',
269
- depth,
270
- contextPreview: context.substring(0, 200),
271
- resultPreview: insights.substring(0, 300),
272
- durationMs: Date.now() - startTime,
273
- passed: true,
274
- timeout: false
275
- });
276
- }
277
-
278
- manager.dispose();
279
-
280
- return {
281
- content: [{
282
- type: 'text',
283
- text: `
125
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
126
+ return await executeReflectionWorkflow(effectiveWorkspaceDir, config, context, depth, model_id, api);
127
+ } catch (err) {
128
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
129
+ return handleReflectionError(err, context, depth, model_id, effectiveWorkspaceDir, api);
130
+ }
131
+ }
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Resolve workspace directory for deep reflection tool.
137
+ */
138
+ function resolveReflectionWorkspace(api: OpenClawPluginApi): string | undefined {
139
+ return (api.config?.workspaceDir as string)
140
+ || resolveWorkspaceDirFromApi(api)
141
+ || api.resolvePath?.('.');
142
+ }
143
+
144
+ /**
145
+ * Execute the deep reflection workflow: start, poll, collect results.
146
+ */
147
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe reflection workflow */
148
+ async function executeReflectionWorkflow(
149
+ effectiveWorkspaceDir: string,
150
+ config: DeepReflectionConfig,
151
+ context: string,
152
+ depth: number,
153
+ model_id: string | undefined,
154
+ api: OpenClawPluginApi,
155
+ ): Promise<{ content: { type: string; text: string }[] }> {
156
+ const stateDir = resolvePdPath(effectiveWorkspaceDir, 'STATE_DIR');
157
+ const eventLog = EventLogService.get(stateDir, api.logger);
158
+ const parentSessionId = effectiveWorkspaceDir.replace(/[^a-zA-Z0-9_-]/g, '_').substring(0, 64);
159
+
160
+ const manager = new DeepReflectWorkflowManager({
161
+ workspaceDir: effectiveWorkspaceDir,
162
+ logger: api.logger,
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: api.runtime.subagent has structurally compatible shape but differs from PluginRuntimeSubagent due to optional provider/model fields
164
+ subagent: api.runtime.subagent as any,
165
+ agentSession: api.runtime.agent?.session,
166
+ });
167
+
168
+ try {
169
+ const taskInput: DeepReflectTaskInput = { context, depth, model_id };
170
+ const handle = await manager.startWorkflow(deepReflectWorkflowSpec, {
171
+ parentSessionId,
172
+ workspaceDir: effectiveWorkspaceDir,
173
+ taskInput,
174
+ });
175
+
176
+ const startTime = Date.now();
177
+ const timeoutMs = config.timeout_ms ?? 60000;
178
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
179
+ return await pollReflectionCompletion(manager, handle, timeoutMs, startTime, eventLog, effectiveWorkspaceDir, context, model_id, depth);
180
+ } finally {
181
+ manager.dispose();
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Poll the reflection workflow until completion, timeout, or error.
187
+ */
188
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe polling */
189
+ async function pollReflectionCompletion(
190
+ manager: DeepReflectWorkflowManager,
191
+ handle: { workflowId: string; childSessionKey: string },
192
+ timeoutMs: number,
193
+ startTime: number,
194
+ eventLog: ReturnType<typeof EventLogService.get>,
195
+ workspaceDir: string,
196
+ context: string,
197
+ model_id: string | undefined,
198
+ depth: number,
199
+ ): Promise<{ content: { type: string; text: string }[] }> {
200
+ const pollInterval = 500;
201
+
202
+ while (Date.now() - startTime < timeoutMs) {
203
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
204
+ const workflowState = manager.getWorkflowState(handle.workflowId);
205
+ if (!workflowState) break;
206
+
207
+ if (workflowState === 'completed') {
208
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
209
+ return formatReflectionSuccess(handle, context, depth, model_id, startTime, eventLog, workspaceDir);
210
+ }
211
+
212
+ if (workflowState === 'terminal_error' || workflowState === 'expired') {
213
+ throw new Error(`Deep-reflect workflow failed: ${workflowState}`);
214
+ }
215
+ }
216
+
217
+ return { content: [{ type: 'text', text: '⚠️ 反思任务执行超时。你可以尝试减少上下文长度或增加深度。' }] };
218
+ }
219
+
220
+ /**
221
+ * Format the success response from a completed reflection.
222
+ */
223
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe formatting */
224
+ function formatReflectionSuccess(
225
+ handle: { childSessionKey: string },
226
+ context: string,
227
+ depth: number,
228
+ model_id: string | undefined,
229
+ startTime: number,
230
+ eventLog: ReturnType<typeof EventLogService.get>,
231
+ workspaceDir: string,
232
+ ): { content: { type: string; text: string }[] } {
233
+ const reflectionLogPath = resolvePdPath(workspaceDir, 'REFLECTION_LOG');
234
+ let insights = '';
235
+ if (fs.existsSync(reflectionLogPath)) {
236
+ const content = fs.readFileSync(reflectionLogPath, 'utf8');
237
+ const match = /### Insights\n([\s\S]*?)(?=---|$)/.exec(content);
238
+ if (match) insights = match[1].trim();
239
+ }
240
+
241
+ if (eventLog) {
242
+ eventLog.recordDeepReflection(handle.childSessionKey, {
243
+ modelId: model_id || 'auto-select',
244
+ modelSelectionMode: model_id ? 'manual' : 'auto',
245
+ depth,
246
+ contextPreview: context.substring(0, 200),
247
+ resultPreview: insights.substring(0, 300),
248
+ durationMs: Date.now() - startTime,
249
+ passed: true,
250
+ timeout: false,
251
+ });
252
+ }
253
+
254
+ return {
255
+ content: [{
256
+ type: 'text',
257
+ text: `
284
258
  # 💎 Deep Reflection Insights
285
259
  ---
286
260
  **Selected Model(s)**: ${model_id || 'auto-select'}
@@ -291,53 +265,45 @@ ${insights || '反思完成,详见 REFLECTION_LOG。'}
291
265
 
292
266
  ---
293
267
  *Generated by Principles Disciple Meta-Cognitive Engine*
294
- `.trim()
295
- }]
296
- };
297
- }
298
-
299
- if (workflow.state === 'terminal_error' || workflow.state === 'expired') {
300
- manager.dispose();
301
- throw new Error(`Deep-reflect workflow failed: ${workflow.state}`);
302
- }
303
- }
304
-
305
- manager.dispose();
306
- return {
307
- content: [{
308
- type: 'text',
309
- text: '⚠️ 反思任务执行超时。你可以尝试减少上下文长度或增加深度。'
310
- }]
311
- };
312
-
313
- } catch (err) {
314
- const errorMsg = err instanceof Error ? err.message : String(err);
315
- safeLog(api, 'error', `[DeepReflect] Reflection failed: ${errorMsg}`);
316
-
317
- if (eventLog) {
318
- eventLog.recordDeepReflection('deep-reflect-error', {
319
- modelId: model_id || 'auto-select',
320
- modelSelectionMode: model_id ? 'manual' : 'auto',
321
- depth,
322
- contextPreview: context.substring(0, 200),
323
- durationMs: 0,
324
- passed: false,
325
- timeout: errorMsg.toLowerCase().includes('timeout'),
326
- error: errorMsg
327
- });
328
- }
329
-
330
- return {
331
- content: [{
332
- type: 'text',
333
- text: `❌ 反思执行失败: ${errorMsg}。请检查 API 配置或网络连接。`
334
- }]
335
- };
336
- }
337
- }
268
+ `.trim(),
269
+ }],
338
270
  };
339
271
  }
340
272
 
273
+ /**
274
+ * Handle reflection errors and format error response.
275
+ */
276
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe error handling */
277
+ function handleReflectionError(
278
+ err: unknown,
279
+ context: string,
280
+ depth: number,
281
+ model_id: string | undefined,
282
+ workspaceDir: string,
283
+ api: OpenClawPluginApi,
284
+ ): { content: { type: string; text: string }[] } {
285
+ const errorMsg = err instanceof Error ? err.message : String(err);
286
+ safeLog(api, 'error', `[DeepReflect] Reflection failed: ${errorMsg}`);
287
+
288
+ const stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
289
+ const eventLog = EventLogService.get(stateDir, api.logger);
290
+
291
+ if (eventLog) {
292
+ eventLog.recordDeepReflection('deep-reflect-error', {
293
+ modelId: model_id || 'auto-select',
294
+ modelSelectionMode: model_id ? 'manual' : 'auto',
295
+ depth,
296
+ contextPreview: context.substring(0, 200),
297
+ durationMs: 0,
298
+ passed: false,
299
+ timeout: errorMsg.toLowerCase().includes('timeout'),
300
+ error: errorMsg,
301
+ });
302
+ }
303
+
304
+ return { content: [{ type: 'text', text: `❌ 反思执行失败: ${errorMsg}。请检查 API 配置或网络连接。` }] };
305
+ }
306
+
341
307
  export const deepReflectTool = {
342
308
  name: 'deep_reflect',
343
309
  description: '执行深层次的元认知反思,分析当前任务的潜在风险、逻辑漏洞或架构改进点。',
@@ -33,7 +33,7 @@ const MAX_INDEX_SIZE = 50 * 1024; // 50KB
33
33
  */
34
34
  function loadCustomConfig(wctx: WorkspaceContext): { modelsDir?: string } | undefined {
35
35
  try {
36
- const config = wctx.config;
36
+ const {config} = wctx;
37
37
  const modelsDir = config.get('deep_reflection.modelsDir');
38
38
  if (typeof modelsDir === 'string' && modelsDir.trim()) {
39
39
  return { modelsDir: modelsDir.trim() };
@@ -61,6 +61,7 @@ export function loadModelIndex(
61
61
  const wctx = WorkspaceContext.fromHookContext({ workspaceDir });
62
62
  const customConfig = loadCustomConfig(wctx);
63
63
 
64
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in both if/else branches
64
65
  let modelsDir: string;
65
66
  if (customConfig?.modelsDir) {
66
67
  modelsDir = path.isAbsolute(customConfig.modelsDir)
@@ -245,11 +245,11 @@ export interface EmpathyEventStats {
245
245
  low: number; // confidence < 0.5
246
246
  };
247
247
  /** Daily trend (last 7 days) */
248
- dailyTrend: Array<{
248
+ dailyTrend: {
249
249
  date: string;
250
250
  count: number;
251
251
  score: number;
252
- }>;
252
+ }[];
253
253
  }
254
254
 
255
255
  export interface GfiStats {