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
@@ -7,6 +7,8 @@ import { getEvolutionQueryService } from '../service/evolution-query-service.js'
7
7
  import { HealthQueryService } from '../service/health-query-service.js';
8
8
  import { TrajectoryRegistry } from '../core/trajectory.js';
9
9
  import { getCentralDatabase } from '../service/central-database.js';
10
+ import { CentralOverviewService } from '../service/central-overview-service.js';
11
+ import { CentralHealthService } from '../service/central-health-service.js';
10
12
 
11
13
  const ROUTE_PREFIX = '/plugins/principles';
12
14
  const API_PREFIX = `${ROUTE_PREFIX}/api`;
@@ -92,6 +94,7 @@ function createService(api: OpenClawPluginApi): ControlUiQueryService {
92
94
  return new ControlUiQueryService(workspaceDir);
93
95
  }
94
96
 
97
+ /* eslint-disable @typescript-eslint/max-params -- Reason: Route handler requires api, pathname, req, and res */
95
98
  function handleApiRoute(
96
99
  api: OpenClawPluginApi,
97
100
  pathname: string,
@@ -99,6 +102,7 @@ function handleApiRoute(
99
102
  res: ServerResponse,
100
103
  ): Promise<boolean> | boolean {
101
104
  // Check authentication for API routes
105
+ /* eslint-disable @typescript-eslint/no-use-before-define -- Reason: validateGatewayAuth is defined later in the file */
102
106
  if (!validateGatewayAuth(req)) {
103
107
  json(res, 401, { error: 'unauthorized', message: 'Valid Gateway token required.' });
104
108
  return true;
@@ -137,61 +141,12 @@ function handleApiRoute(
137
141
  if (pathname === `${API_PREFIX}/central/overview` && method === 'GET') {
138
142
  const days = parseDays(url.searchParams.get('days'));
139
143
  return done(() => {
140
- const centralDb = getCentralDatabase();
141
- const stats = centralDb.getOverviewStats();
142
- const trend = centralDb.getDailyTrend(days);
143
- const regressions = centralDb.getTopRegressions(5);
144
- const thinkingStats = centralDb.getThinkingModelStats();
145
- const workspaces = centralDb.getWorkspaces();
146
-
147
- return {
148
- workspaceDir: 'central',
149
- generatedAt: new Date().toISOString(),
150
- dataFreshness: workspaces.length > 0 ? (workspaces[0].lastSync ?? null) : null,
151
- dataSource: 'central_aggregated_db',
152
- runtimeControlPlaneSource: 'all_workspaces',
153
- summary: {
154
- repeatErrorRate: stats.totalToolCalls > 0
155
- ? stats.totalFailures / stats.totalToolCalls
156
- : 0,
157
- userCorrectionRate: stats.totalToolCalls > 0
158
- ? stats.totalCorrections / stats.totalToolCalls
159
- : 0,
160
- pendingSamples: stats.pendingSamples,
161
- approvedSamples: stats.approvedSamples,
162
- thinkingCoverageRate: stats.totalToolCalls > 0
163
- ? stats.totalThinkingEvents / stats.totalToolCalls
164
- : 0,
165
- painEvents: stats.totalPainEvents,
166
- principleEventCount: 0,
167
- gateBlocks: 0,
168
- taskOutcomes: 0,
169
- },
170
- dailyTrend: trend,
171
- topRegressions: regressions,
172
- sampleQueue: {
173
- counters: {
174
- pending: stats.pendingSamples,
175
- approved: stats.approvedSamples,
176
- rejected: stats.rejectedSamples,
177
- },
178
- preview: [],
179
- },
180
- thinkingSummary: {
181
- activeModels: thinkingStats.activeModels,
182
- dormantModels: thinkingStats.totalModels - thinkingStats.activeModels,
183
- effectiveModels: thinkingStats.models.filter(m => m.coverageRate > 0.1).length,
184
- coverageRate: stats.totalToolCalls > 0
185
- ? stats.totalThinkingEvents / stats.totalToolCalls
186
- : 0,
187
- },
188
- centralInfo: {
189
- workspaceCount: stats.workspaceCount,
190
- enabledWorkspaceCount: stats.enabledWorkspaceCount,
191
- workspaces: stats.workspaceNames,
192
- enabledWorkspaces: stats.enabledWorkspaceNames,
193
- },
194
- };
144
+ const centralOverviewService = new CentralOverviewService();
145
+ try {
146
+ return centralOverviewService.getOverview(days);
147
+ } finally {
148
+ centralOverviewService.dispose();
149
+ }
195
150
  });
196
151
  }
197
152
 
@@ -224,7 +179,14 @@ function handleApiRoute(
224
179
  });
225
180
  }
226
181
 
227
- const workspaceConfigMatch = pathname.match(/^\/plugins\/principles\/api\/central\/workspaces\/([^/]+)$/);
182
+ // === Central Health: per-workspace health indicators ===
183
+ if (pathname === `${API_PREFIX}/central/health` && method === 'GET') {
184
+ return done(() => {
185
+ return new CentralHealthService().getAllWorkspaceHealth();
186
+ });
187
+ }
188
+
189
+ const workspaceConfigMatch = /^\/plugins\/principles\/api\/central\/workspaces\/([^/]+)$/.exec(pathname);
228
190
  if (workspaceConfigMatch && method === 'GET') {
229
191
  return done(() => {
230
192
  const centralDb = getCentralDatabase();
@@ -238,7 +200,7 @@ function handleApiRoute(
238
200
  if (workspaceConfigMatch && method === 'PATCH') {
239
201
  return (async () => {
240
202
  try {
241
- const body = await readJsonBody(req) as Record<string, unknown>;
203
+ const body = await readJsonBody(req);
242
204
  const centralDb = getCentralDatabase();
243
205
  const workspaceName = decodeURIComponent(workspaceConfigMatch[1]);
244
206
  centralDb.updateWorkspaceConfig(workspaceName, {
@@ -264,7 +226,7 @@ function handleApiRoute(
264
226
  if (pathname === `${API_PREFIX}/central/workspaces` && method === 'POST') {
265
227
  return (async () => {
266
228
  try {
267
- const body = await readJsonBody(req) as Record<string, unknown>;
229
+ const body = await readJsonBody(req);
268
230
  const name = typeof body.name === 'string' ? body.name : '';
269
231
  const workspacePath = typeof body.path === 'string' ? body.path : '';
270
232
  if (!name || !workspacePath) {
@@ -299,7 +261,7 @@ function handleApiRoute(
299
261
  }));
300
262
  }
301
263
 
302
- const sampleDetailMatch = pathname.match(/^\/plugins\/principles\/api\/samples\/([^/]+)$/);
264
+ const sampleDetailMatch = /^\/plugins\/principles\/api\/samples\/([^/]+)$/.exec(pathname);
303
265
  if (sampleDetailMatch && method === 'GET') {
304
266
  try {
305
267
  const detail = service.getSampleDetail(decodeURIComponent(sampleDetailMatch[1]));
@@ -318,7 +280,7 @@ function handleApiRoute(
318
280
  }
319
281
  }
320
282
 
321
- const sampleReviewMatch = pathname.match(/^\/plugins\/principles\/api\/samples\/([^/]+)\/review$/);
283
+ const sampleReviewMatch = /^\/plugins\/principles\/api\/samples\/([^/]+)\/review$/.exec(pathname);
322
284
  if (sampleReviewMatch && method === 'POST') {
323
285
  return (async () => {
324
286
  try {
@@ -355,7 +317,7 @@ function handleApiRoute(
355
317
  return done(() => service.getThinkingOverview());
356
318
  }
357
319
 
358
- const thinkingDetailMatch = pathname.match(/^\/plugins\/principles\/api\/thinking\/models\/([^/]+)$/);
320
+ const thinkingDetailMatch = /^\/plugins\/principles\/api\/thinking\/models\/([^/]+)$/.exec(pathname);
359
321
  if (thinkingDetailMatch && method === 'GET') {
360
322
  try {
361
323
  const detail = service.getThinkingModelDetail(decodeURIComponent(thinkingDetailMatch[1]));
@@ -414,7 +376,7 @@ function handleApiRoute(
414
376
  });
415
377
  }
416
378
 
417
- const evolutionTraceMatch = pathname.match(/^\/plugins\/principles\/api\/evolution\/trace\/([^/]+)$/);
379
+ const evolutionTraceMatch = /^\/plugins\/principles\/api\/evolution\/trace\/([^/]+)$/.exec(pathname);
418
380
  if (evolutionTraceMatch && method === 'GET') {
419
381
  const evoService = evolutionService();
420
382
  try {
@@ -590,8 +552,8 @@ function validateGatewayAuth(req: IncomingMessage): boolean {
590
552
  // No token configured, allow all requests
591
553
  return true;
592
554
  }
593
- const authHeader = (req.headers?.['authorization'] as string) || '';
594
- const tokenMatch = authHeader.match(/^Bearer\s+(.+)$/i);
555
+ const authHeader = (req.headers?.authorization as string) || '';
556
+ const tokenMatch = /^Bearer\s+(.+)$/i.exec(authHeader);
595
557
  const providedToken = tokenMatch?.[1];
596
558
  return providedToken === gatewayToken;
597
559
  }
@@ -611,7 +573,7 @@ export function createPrinciplesConsoleRoutes(api: OpenClawPluginApi): OpenClawP
611
573
  async handler(req, res) {
612
574
  if (!api.rootDir) { text(res, 500, 'Plugin rootDir not available'); return true; }
613
575
  const url = new URL(req.url || ROUTE_PREFIX, 'http://127.0.0.1');
614
- const pathname = url.pathname;
576
+ const {pathname} = url;
615
577
  const method = (req.method || 'GET').toUpperCase();
616
578
 
617
579
  // Skip API routes - they'll be handled by the API route
@@ -653,7 +615,7 @@ export function createPrinciplesConsoleRoutes(api: OpenClawPluginApi): OpenClawP
653
615
  match: 'prefix',
654
616
  async handler(req, res) {
655
617
  const url = new URL(req.url || API_PREFIX, 'http://127.0.0.1');
656
- const pathname = url.pathname;
618
+ const {pathname} = url;
657
619
  return handleApiRoute(api, pathname, req, res);
658
620
  },
659
621
  };
@@ -663,7 +625,8 @@ export function createPrinciplesConsoleRoutes(api: OpenClawPluginApi): OpenClawP
663
625
 
664
626
  // Legacy export for backwards compatibility
665
627
  export function createPrinciplesConsoleRoute(api: OpenClawPluginApi): OpenClawPluginHttpRouteParams {
666
- const routes = createPrinciplesConsoleRoutes(api);
628
+ // Side effect: registers all console routes via createPrinciplesConsoleRoutes
629
+ createPrinciplesConsoleRoutes(api);
667
630
  // Return the combined behavior - this will be called from index.ts
668
631
  return {
669
632
  path: ROUTE_PREFIX,
@@ -672,7 +635,7 @@ export function createPrinciplesConsoleRoute(api: OpenClawPluginApi): OpenClawPl
672
635
  async handler(req, res) {
673
636
  if (!api.rootDir) { text(res, 500, 'Plugin rootDir not available'); return true; }
674
637
  const url = new URL(req.url || ROUTE_PREFIX, 'http://127.0.0.1');
675
- const pathname = url.pathname;
638
+ const {pathname} = url;
676
639
  const method = (req.method || 'GET').toUpperCase();
677
640
 
678
641
  if (!pathname.startsWith(ROUTE_PREFIX)) {
@@ -99,7 +99,7 @@ export function getCommandDescription(name: string, lang: string): string {
99
99
  if (!descriptions) {
100
100
  return name;
101
101
  }
102
- return descriptions[normalizedLang] || descriptions['en'] || name;
102
+ return descriptions[normalizedLang] || descriptions.en || name;
103
103
  }
104
104
 
105
105
  /**
@@ -111,7 +111,7 @@ export function getAllCommandDescriptions(lang: string): Record<string, string>
111
111
  const normalizedLang = normalizeLanguage(lang);
112
112
  const result: Record<string, string> = {};
113
113
  for (const [name, descriptions] of Object.entries(commandDescriptions)) {
114
- result[name] = descriptions[normalizedLang] || descriptions['en'] || name;
114
+ result[name] = descriptions[normalizedLang] || descriptions.en || name;
115
115
  }
116
116
  return result;
117
117
  }
package/src/index.ts CHANGED
@@ -15,8 +15,6 @@ import type {
15
15
  PluginHookSubagentSpawningEvent,
16
16
  PluginHookSubagentSpawningResult,
17
17
  PluginHookSubagentContext,
18
- PluginHookBeforeMessageWriteEvent,
19
- PluginHookBeforeMessageWriteResult,
20
18
  } from './openclaw-sdk.js';
21
19
  import * as crypto from 'crypto';
22
20
  import type { WorkerProfile } from './core/model-deployment-registry.js';
@@ -30,7 +28,6 @@ import { handleAfterToolCall } from './hooks/pain.js';
30
28
  import { handleBeforeReset, handleBeforeCompaction, handleAfterCompaction } from './hooks/lifecycle.js';
31
29
  import { handleLlmOutput } from './hooks/llm.js';
32
30
  import { handleSubagentEnded } from './hooks/subagent.js';
33
- import { handleBeforeMessageWrite } from './hooks/message-sanitize.js';
34
31
  import * as TrajectoryCollector from './hooks/trajectory-collector.js';
35
32
  import { handleInitStrategy, handleManageOkr } from './commands/strategy.js';
36
33
  import { handleBootstrapTools, handleResearchTools } from './commands/capabilities.js';
@@ -39,6 +36,10 @@ import { handlePainCommand } from './commands/pain.js';
39
36
  import { handleContextCommand } from './commands/context.js';
40
37
  import { handleFocusCommand } from './commands/focus.js';
41
38
  import { handleRollbackCommand } from './commands/rollback.js';
39
+ import { handlePromoteImplCommand } from './commands/promote-impl.js';
40
+ import { handleDisableImplCommand } from './commands/disable-impl.js';
41
+ import { handleArchiveImplCommand } from './commands/archive-impl.js';
42
+ import { handleRollbackImplCommand } from './commands/rollback-impl.js';
42
43
  import { handleEvolutionStatusCommand } from './commands/evolution-status.js';
43
44
  import { handlePrincipleRollbackCommand } from './commands/principle-rollback.js';
44
45
  import { handleExportCommand } from './commands/export.js';
@@ -49,8 +50,11 @@ import { handleNocturnalRolloutCommand } from './commands/nocturnal-rollout.js';
49
50
  import { handleWorkflowDebugCommand } from './commands/workflow-debug.js';
50
51
  import { EvolutionWorkerService } from './service/evolution-worker.js';
51
52
  import { TrajectoryService } from './service/trajectory-service.js';
53
+ import { PDTaskService } from './core/pd-task-service.js';
54
+ import { CentralSyncService } from './service/central-sync-service.js';
52
55
  import { ensureWorkspaceTemplates } from './core/init.js';
53
56
  import { migrateDirectoryStructure } from './core/migration.js';
57
+ import { runMigrationIfNeeded } from './core/principle-tree-migration.js';
54
58
  import { SystemLogger } from './core/system-logger.js';
55
59
  import { createDeepReflectTool } from './tools/deep-reflect.js';
56
60
  import { PathResolver, resolveWorkspaceDirFromApi } from './core/path-resolver.js';
@@ -79,6 +83,16 @@ function computeRuntimeShadowTaskFingerprint(event: PluginHookSubagentSpawningEv
79
83
  return crypto.createHash('sha256').update(JSON.stringify(payload)).digest('hex').slice(0, 16);
80
84
  }
81
85
 
86
+ import { resolveValidWorkspaceDir, validateWorkspaceDir } from './core/workspace-dir-validation.js';
87
+
88
+ function resolveToolHookWorkspaceDir(
89
+ ctx: { workspaceDir?: string; agentId?: string },
90
+ api: OpenClawPluginApi,
91
+ source: string,
92
+ ): string {
93
+ return resolveValidWorkspaceDir(ctx, api, { source });
94
+ }
95
+
82
96
  const plugin = {
83
97
  name: "Principles Disciple",
84
98
  description: "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
@@ -88,6 +102,20 @@ const plugin = {
88
102
  PathResolver.setExtensionRoot(api.rootDir ?? '.');
89
103
  api.registerHttpRoute(createPrinciplesConsoleRoute(api));
90
104
 
105
+ // ── Startup Health Check: Verify workspaceDir resolution ──
106
+ // Catches OpenClaw context bugs early (e.g., missing workspaceDir in tool hooks)
107
+ setTimeout(() => {
108
+ const testCtx = { agentId: 'main' };
109
+ const toolWorkspaceDir = resolveToolHookWorkspaceDir(testCtx, api, 'startup.health_check');
110
+ const toolIssue = validateWorkspaceDir(toolWorkspaceDir);
111
+ if (toolIssue) {
112
+ api.logger.error(`[PD:health] Tool hook workspaceDir is INVALID: "${toolWorkspaceDir}" - ${toolIssue}`);
113
+ api.logger.error(`[PD:health] Tool hook events will be written to the WRONG .state directory!`);
114
+ } else {
115
+ api.logger.info(`[PD:health] Tool hook workspaceDir OK: "${toolWorkspaceDir}"`);
116
+ }
117
+ }, 1000);
118
+
91
119
  const language = (api.pluginConfig?.language as string) || 'en';
92
120
 
93
121
  // ── Hook: Prompt Building ──
@@ -98,6 +126,9 @@ const plugin = {
98
126
  const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
99
127
  if (!workspaceInitialized && workspaceDir) {
100
128
  migrateDirectoryStructure(api, workspaceDir);
129
+ // Phase 11: Migrate trainingStore principles to tree.principles
130
+ const { stateDir } = WorkspaceContext.fromHookContext({ workspaceDir });
131
+ runMigrationIfNeeded(stateDir, workspaceDir);
101
132
  ensureWorkspaceTemplates(api, workspaceDir, language);
102
133
  SystemLogger.log(workspaceDir, 'SYSTEM_BOOT', `Principles Disciple online. Language: ${language}`);
103
134
  workspaceInitialized = true;
@@ -127,22 +158,23 @@ const plugin = {
127
158
  api.on(
128
159
  'before_tool_call',
129
160
  (event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext): PluginHookBeforeToolCallResult | void => {
130
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
161
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx, api, 'before_tool_call');
131
162
  try {
132
163
  const pluginConfig = api.pluginConfig ?? {};
133
- const logger = api.logger;
164
+ const {logger} = api;
134
165
  const result = handleBeforeToolCall(event, { ...ctx, workspaceDir, pluginConfig, logger });
135
-
166
+
136
167
  WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
137
168
  hook: 'before_tool_call'
138
- });
139
-
169
+ }, { flushImmediately: true });
170
+
140
171
  return result;
141
172
  } catch (err) {
142
- WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
173
+ const fallbackDir = resolveToolHookWorkspaceDir(ctx, api, 'before_tool_call');
174
+ WorkspaceContext.fromHookContext({ workspaceDir: fallbackDir }).eventLog.recordHookExecution({
143
175
  hook: 'before_tool_call',
144
176
  error: String(err)
145
- });
177
+ }, { flushImmediately: true });
146
178
  api.logger.error(`[PD] Error in before_tool_call: ${String(err)}`);
147
179
  }
148
180
  }
@@ -152,20 +184,21 @@ const plugin = {
152
184
  api.on(
153
185
  'after_tool_call',
154
186
  (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
155
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
187
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx, api, 'after_tool_call');
156
188
  try {
157
189
  const pluginConfig = api.pluginConfig ?? {};
158
190
  // Pass api separately to handleAfterToolCall to maintain type safety
159
191
  handleAfterToolCall(event, { ...ctx, workspaceDir, pluginConfig }, api);
160
-
192
+
161
193
  WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
162
194
  hook: 'after_tool_call'
163
- });
195
+ }, { flushImmediately: true });
164
196
  } catch (err) {
165
- WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
197
+ const fallbackDir = resolveToolHookWorkspaceDir(ctx, api, 'after_tool_call');
198
+ WorkspaceContext.fromHookContext({ workspaceDir: fallbackDir }).eventLog.recordHookExecution({
166
199
  hook: 'after_tool_call',
167
200
  error: String(err)
168
- });
201
+ }, { flushImmediately: true });
169
202
  api.logger.error(`[PD:EmpathyObserver] Error in after_tool_call: ${String(err)}`);
170
203
  }
171
204
  }
@@ -175,10 +208,10 @@ const plugin = {
175
208
  api.on(
176
209
  'llm_output',
177
210
  (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
178
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
211
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx as unknown as Record<string, unknown>, api, 'llm_output');
179
212
  try {
180
213
  handleLlmOutput(event, { ...ctx, workspaceDir });
181
-
214
+
182
215
  WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
183
216
  hook: 'llm_output',
184
217
  sessionId: ctx.sessionId
@@ -194,28 +227,16 @@ const plugin = {
194
227
  }
195
228
  );
196
229
 
197
- // ── Hook: Message Sanitization ──
198
- api.on(
199
- 'before_message_write',
200
- (event: PluginHookBeforeMessageWriteEvent): PluginHookBeforeMessageWriteResult | void => {
201
- try {
202
- return handleBeforeMessageWrite(event);
203
- } catch (err) {
204
- api.logger.error(`[PD] Error in before_message_write: ${String(err)}`);
205
- }
206
- }
207
- );
208
-
209
230
  // ── Hook: Trajectory Collection (Behavior Evolution Phase 0) ──
210
231
  // Note: after_tool_call and llm_output are safe to collect
211
- // before_message_write conflicts with message-sanitize, skipping for now
212
232
  api.on(
213
233
  'after_tool_call',
214
234
  (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
215
235
  try {
216
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
236
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx, api, 'trajectory.after_tool_call');
217
237
  TrajectoryCollector.handleAfterToolCall(event, { ...ctx, workspaceDir });
218
- } catch (err) {
238
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Reason: catch binding intentionally unused
239
+ } catch (_err) {
219
240
  // Non-critical: don't log, just skip
220
241
  }
221
242
  }
@@ -225,9 +246,10 @@ const plugin = {
225
246
  'llm_output',
226
247
  (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
227
248
  try {
228
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
249
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx as unknown as Record<string, unknown>, api, 'trajectory.llm_output');
229
250
  TrajectoryCollector.handleLlmOutput(event, { ...ctx, workspaceDir });
230
- } catch (err) {
251
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Reason: catch binding intentionally unused
252
+ } catch (_err) {
231
253
  // Non-critical: don't log, just skip
232
254
  }
233
255
  }
@@ -236,7 +258,8 @@ const plugin = {
236
258
  // ── Hook: Subagent Loop Closure ──
237
259
  api.on(
238
260
  'subagent_spawning',
239
- (event: PluginHookSubagentSpawningEvent, ctx: PluginHookSubagentContext): void | PluginHookSubagentSpawningResult => {
261
+
262
+ (event: PluginHookSubagentSpawningEvent, _ctx: PluginHookSubagentContext): void | PluginHookSubagentSpawningResult => {
240
263
  try {
241
264
  // Resolve workspace via official API, falling back to PathResolver
242
265
  const workspaceDir = resolveWorkspaceDirFromApi(api, event.agentId) || '.';
@@ -259,7 +282,7 @@ const plugin = {
259
282
 
260
283
  if (shouldRecordShadow) {
261
284
  const observation = recordShadowRouting(workspaceDir, {
262
- checkpointId: decision.activeCheckpointId!,
285
+ checkpointId: decision.activeCheckpointId!, // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: !!decision.activeCheckpointId guard above ensures truthiness
263
286
  workerProfile: agentId as WorkerProfile,
264
287
  taskFingerprint: computeRuntimeShadowTaskFingerprint(event),
265
288
  });
@@ -327,6 +350,8 @@ const plugin = {
327
350
  EvolutionWorkerService.api = api;
328
351
  api.registerService(EvolutionWorkerService);
329
352
  api.registerService(TrajectoryService);
353
+ api.registerService(PDTaskService);
354
+ api.registerService(CentralSyncService);
330
355
  } catch (err) {
331
356
  api.logger.error(`[PD] Failed to register EvolutionWorkerService: ${String(err)}`);
332
357
  }
@@ -366,8 +391,8 @@ const plugin = {
366
391
  api.registerCommand({
367
392
  name: "pd-daily",
368
393
  description: getCommandDescription('pd-daily', language),
369
- handler: (_ctx) => {
370
- return { text: language === 'zh'
394
+ handler: () => {
395
+ return { text: language === 'zh'
371
396
  ? "请执行 pd-daily 技能来配置并发送进化日报。系统将引导你完成配置流程,包括发送时间、渠道和报告风格偏好。"
372
397
  : "Please execute the pd-daily skill to configure and send your daily evolution report. The system will guide you through the configuration process." };
373
398
  }
@@ -376,7 +401,7 @@ const plugin = {
376
401
  api.registerCommand({
377
402
  name: "pd-grooming",
378
403
  description: getCommandDescription('pd-grooming', language),
379
- handler: (_ctx) => {
404
+ handler: () => {
380
405
  return { text: language === 'zh'
381
406
  ? "请执行 pd-grooming 技能来执行大扫除。例如输入: '执行 pd-grooming 技能'"
382
407
  : "Please execute the pd-grooming skill to clean up. For example: 'Execute pd-grooming skill'" };
@@ -386,7 +411,7 @@ const plugin = {
386
411
  api.registerCommand({
387
412
  name: "pd-help",
388
413
  description: getCommandDescription('pd-help', language),
389
- handler: (_ctx) => {
414
+ handler: () => {
390
415
  if (language === 'zh') {
391
416
  return { text: `
392
417
  📖 **Principles Disciple 命令大全**
@@ -687,6 +712,71 @@ const plugin = {
687
712
  }
688
713
  });
689
714
 
715
+ // ── Implementation Lifecycle Commands (Phase 13) ──
716
+ api.registerCommand({
717
+ name: "pd-promote-impl",
718
+ description: 'Promote a candidate implementation to active [list|show <id>|<id>]',
719
+ acceptsArgs: true,
720
+ handler: (ctx) => {
721
+ try {
722
+ const workspaceDir = api.resolvePath('.');
723
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
724
+ return handlePromoteImplCommand(ctx);
725
+ } catch (err) {
726
+ api.logger.error(`[PD] Command /pd-promote-impl failed: ${String(err)}`);
727
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
728
+ }
729
+ }
730
+ });
731
+
732
+ api.registerCommand({
733
+ name: "pd-disable-impl",
734
+ description: 'Disable an active implementation [list|<id> --reason "..."]',
735
+ acceptsArgs: true,
736
+ handler: (ctx) => {
737
+ try {
738
+ const workspaceDir = api.resolvePath('.');
739
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
740
+ return handleDisableImplCommand(ctx);
741
+ } catch (err) {
742
+ api.logger.error(`[PD] Command /pd-disable-impl failed: ${String(err)}`);
743
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
744
+ }
745
+ }
746
+ });
747
+
748
+ api.registerCommand({
749
+ name: "pd-archive-impl",
750
+ description: 'Archive an implementation permanently [list|<id>]',
751
+ acceptsArgs: true,
752
+ handler: (ctx) => {
753
+ try {
754
+ const workspaceDir = api.resolvePath('.');
755
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
756
+ return handleArchiveImplCommand(ctx);
757
+ } catch (err) {
758
+ api.logger.error(`[PD] Command /pd-archive-impl failed: ${String(err)}`);
759
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
760
+ }
761
+ }
762
+ });
763
+
764
+ api.registerCommand({
765
+ name: "pd-rollback-impl",
766
+ description: 'Rollback current active implementation to previous active [list|<id> --reason "..."]',
767
+ acceptsArgs: true,
768
+ handler: (ctx) => {
769
+ try {
770
+ const workspaceDir = api.resolvePath('.');
771
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
772
+ return handleRollbackImplCommand(ctx);
773
+ } catch (err) {
774
+ api.logger.error(`[PD] Command /pd-rollback-impl failed: ${String(err)}`);
775
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
776
+ }
777
+ }
778
+ });
779
+
690
780
  api.registerTool(createDeepReflectTool(api));
691
781
  }
692
782
  };