principles-disciple 1.10.0 → 1.12.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 (230) 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 +94 -5
  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 +17 -42
  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 +83 -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 +300 -57
  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 +713 -0
  77. package/src/core/profile.ts +3 -1
  78. package/src/core/promotion-gate.ts +14 -18
  79. package/src/core/replay-engine.ts +562 -0
  80. package/src/core/risk-calculator.ts +6 -4
  81. package/src/core/rule-host-helpers.ts +39 -0
  82. package/src/core/rule-host-types.ts +82 -0
  83. package/src/core/rule-host.ts +245 -0
  84. package/src/core/rule-implementation-runtime.ts +38 -0
  85. package/src/core/schema/db-types.ts +16 -0
  86. package/src/core/schema/index.ts +26 -0
  87. package/src/core/schema/migration-runner.ts +207 -0
  88. package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
  89. package/src/core/schema/migrations/002-init-central.ts +122 -0
  90. package/src/core/schema/migrations/003-init-workflow.ts +55 -0
  91. package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
  92. package/src/core/schema/migrations/index.ts +31 -0
  93. package/src/core/schema/schema-definitions.ts +650 -0
  94. package/src/core/session-tracker.ts +6 -4
  95. package/src/core/shadow-observation-registry.ts +6 -3
  96. package/src/core/system-logger.ts +2 -2
  97. package/src/core/thinking-models.ts +182 -46
  98. package/src/core/thinking-os-parser.ts +164 -0
  99. package/src/core/training-program.ts +7 -7
  100. package/src/core/trajectory.ts +42 -36
  101. package/src/core/workspace-context.ts +77 -11
  102. package/src/core/workspace-dir-validation.ts +152 -0
  103. package/src/hooks/AGENTS.md +31 -0
  104. package/src/hooks/bash-risk.ts +3 -1
  105. package/src/hooks/edit-verification.ts +9 -5
  106. package/src/hooks/gate-block-helper.ts +5 -1
  107. package/src/hooks/gate.ts +152 -5
  108. package/src/hooks/gfi-gate.ts +9 -2
  109. package/src/hooks/lifecycle-routing.ts +124 -0
  110. package/src/hooks/lifecycle.ts +12 -12
  111. package/src/hooks/llm.ts +17 -109
  112. package/src/hooks/message-sanitize.ts +5 -3
  113. package/src/hooks/pain.ts +19 -15
  114. package/src/hooks/progressive-trust-gate.ts +7 -1
  115. package/src/hooks/prompt.ts +169 -60
  116. package/src/hooks/subagent.ts +5 -4
  117. package/src/hooks/thinking-checkpoint.ts +2 -0
  118. package/src/hooks/trajectory-collector.ts +15 -12
  119. package/src/http/principles-console-route.ts +31 -68
  120. package/src/i18n/commands.ts +2 -2
  121. package/src/index.ts +126 -40
  122. package/src/service/central-database.ts +131 -43
  123. package/src/service/central-health-service.ts +47 -0
  124. package/src/service/central-overview-service.ts +135 -0
  125. package/src/service/central-sync-service.ts +87 -0
  126. package/src/service/control-ui-query-service.ts +46 -36
  127. package/src/service/event-log-auditor.ts +261 -0
  128. package/src/service/evolution-query-service.ts +23 -22
  129. package/src/service/evolution-worker.ts +565 -261
  130. package/src/service/health-query-service.ts +213 -36
  131. package/src/service/nocturnal-runtime.ts +8 -4
  132. package/src/service/nocturnal-service.ts +499 -59
  133. package/src/service/nocturnal-target-selector.ts +5 -7
  134. package/src/service/runtime-summary-service.ts +2 -1
  135. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
  136. package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
  137. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
  138. package/src/service/subagent-workflow/index.ts +2 -0
  139. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +155 -285
  140. package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
  141. package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
  142. package/src/service/subagent-workflow/types.ts +9 -4
  143. package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
  144. package/src/service/subagent-workflow/workflow-store.ts +71 -11
  145. package/src/service/trajectory-service.ts +2 -1
  146. package/src/tools/critique-prompt.ts +1 -1
  147. package/src/tools/deep-reflect.ts +175 -209
  148. package/src/tools/model-index.ts +2 -1
  149. package/src/types/event-types.ts +2 -2
  150. package/src/types/principle-tree-schema.ts +29 -23
  151. package/src/utils/file-lock.ts +5 -3
  152. package/src/utils/io.ts +5 -2
  153. package/src/utils/nlp.ts +5 -46
  154. package/src/utils/node-vm-polyfill.ts +11 -0
  155. package/src/utils/plugin-logger.ts +2 -0
  156. package/src/utils/retry.ts +572 -0
  157. package/src/utils/subagent-probe.ts +1 -1
  158. package/templates/langs/en/core/AGENTS.md +0 -13
  159. package/templates/langs/en/core/SOUL.md +1 -31
  160. package/templates/langs/en/core/TOOLS.md +0 -4
  161. package/templates/langs/en/principles/THINKING_OS.md +64 -0
  162. package/templates/langs/en/skills/admin/SKILL.md +0 -1
  163. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
  164. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
  165. package/templates/langs/zh/core/AGENTS.md +0 -22
  166. package/templates/langs/zh/core/SOUL.md +1 -31
  167. package/templates/langs/zh/core/TOOLS.md +0 -4
  168. package/templates/langs/zh/principles/THINKING_OS.md +64 -0
  169. package/templates/langs/zh/skills/admin/SKILL.md +0 -1
  170. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
  171. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
  172. package/tests/commands/evolution-status.test.ts +119 -0
  173. package/tests/commands/implementation-lifecycle.test.ts +362 -0
  174. package/tests/core/code-implementation-storage.test.ts +398 -0
  175. package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
  176. package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
  177. package/tests/core/nocturnal-artificer.test.ts +241 -0
  178. package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
  179. package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
  180. package/tests/core/pd-task-store.test.ts +126 -0
  181. package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
  182. package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
  183. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
  184. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
  185. package/tests/core/principle-training-state.test.ts +228 -1
  186. package/tests/core/principle-tree-ledger.test.ts +423 -0
  187. package/tests/core/regression-v1-9-1.test.ts +265 -0
  188. package/tests/core/replay-engine.test.ts +234 -0
  189. package/tests/core/rule-host-helpers.test.ts +120 -0
  190. package/tests/core/rule-host.test.ts +389 -0
  191. package/tests/core/rule-implementation-runtime.test.ts +64 -0
  192. package/tests/core/workspace-context.test.ts +53 -0
  193. package/tests/core/workspace-dir-validation.test.ts +272 -0
  194. package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
  195. package/tests/hooks/pain.test.ts +74 -10
  196. package/tests/hooks/prompt.test.ts +63 -1
  197. package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
  198. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
  199. package/tests/service/data-endpoints-regression.test.ts +834 -0
  200. package/tests/service/evolution-worker.test.ts +0 -123
  201. package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
  202. package/tests/utils/nlp.test.ts +1 -19
  203. package/tests/utils/retry.test.ts +327 -0
  204. package/ui/src/App.tsx +1 -1
  205. package/ui/src/api.ts +4 -0
  206. package/ui/src/charts.tsx +366 -0
  207. package/ui/src/components/WorkspaceConfig.tsx +107 -75
  208. package/ui/src/i18n/ui.ts +55 -22
  209. package/ui/src/pages/OverviewPage.tsx +441 -81
  210. package/ui/src/styles.css +43 -0
  211. package/ui/src/types.ts +17 -1
  212. package/src/agents/nocturnal-dreamer.md +0 -152
  213. package/src/agents/nocturnal-philosopher.md +0 -138
  214. package/src/agents/nocturnal-reflector.md +0 -126
  215. package/src/agents/nocturnal-scribe.md +0 -164
  216. package/templates/workspace/.principles/00-kernel.md +0 -51
  217. package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  218. package/templates/workspace/.principles/PRINCIPLES.md +0 -20
  219. package/templates/workspace/.principles/PROFILE.json +0 -54
  220. package/templates/workspace/.principles/PROFILE.schema.json +0 -56
  221. package/templates/workspace/.principles/THINKING_OS.md +0 -64
  222. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  223. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  224. package/templates/workspace/.principles/models/_INDEX.md +0 -27
  225. package/templates/workspace/.principles/models/first_principles.md +0 -62
  226. package/templates/workspace/.principles/models/marketing_4p.md +0 -52
  227. package/templates/workspace/.principles/models/porter_five.md +0 -63
  228. package/templates/workspace/.principles/models/swot.md +0 -60
  229. package/templates/workspace/.principles/models/user_story_map.md +0 -63
  230. 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,6 +50,8 @@ 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';
54
57
  import { SystemLogger } from './core/system-logger.js';
@@ -79,6 +82,16 @@ function computeRuntimeShadowTaskFingerprint(event: PluginHookSubagentSpawningEv
79
82
  return crypto.createHash('sha256').update(JSON.stringify(payload)).digest('hex').slice(0, 16);
80
83
  }
81
84
 
85
+ import { resolveValidWorkspaceDir, validateWorkspaceDir } from './core/workspace-dir-validation.js';
86
+
87
+ function resolveToolHookWorkspaceDir(
88
+ ctx: { workspaceDir?: string; agentId?: string },
89
+ api: OpenClawPluginApi,
90
+ source: string,
91
+ ): string {
92
+ return resolveValidWorkspaceDir(ctx, api, { source });
93
+ }
94
+
82
95
  const plugin = {
83
96
  name: "Principles Disciple",
84
97
  description: "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
@@ -88,6 +101,20 @@ const plugin = {
88
101
  PathResolver.setExtensionRoot(api.rootDir ?? '.');
89
102
  api.registerHttpRoute(createPrinciplesConsoleRoute(api));
90
103
 
104
+ // ── Startup Health Check: Verify workspaceDir resolution ──
105
+ // Catches OpenClaw context bugs early (e.g., missing workspaceDir in tool hooks)
106
+ setTimeout(() => {
107
+ const testCtx = { agentId: 'main' };
108
+ const toolWorkspaceDir = resolveToolHookWorkspaceDir(testCtx, api, 'startup.health_check');
109
+ const toolIssue = validateWorkspaceDir(toolWorkspaceDir);
110
+ if (toolIssue) {
111
+ api.logger.error(`[PD:health] Tool hook workspaceDir is INVALID: "${toolWorkspaceDir}" - ${toolIssue}`);
112
+ api.logger.error(`[PD:health] Tool hook events will be written to the WRONG .state directory!`);
113
+ } else {
114
+ api.logger.info(`[PD:health] Tool hook workspaceDir OK: "${toolWorkspaceDir}"`);
115
+ }
116
+ }, 1000);
117
+
91
118
  const language = (api.pluginConfig?.language as string) || 'en';
92
119
 
93
120
  // ── Hook: Prompt Building ──
@@ -127,22 +154,23 @@ const plugin = {
127
154
  api.on(
128
155
  'before_tool_call',
129
156
  (event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext): PluginHookBeforeToolCallResult | void => {
130
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
157
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx, api, 'before_tool_call');
131
158
  try {
132
159
  const pluginConfig = api.pluginConfig ?? {};
133
- const logger = api.logger;
160
+ const {logger} = api;
134
161
  const result = handleBeforeToolCall(event, { ...ctx, workspaceDir, pluginConfig, logger });
135
-
162
+
136
163
  WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
137
164
  hook: 'before_tool_call'
138
- });
139
-
165
+ }, { flushImmediately: true });
166
+
140
167
  return result;
141
168
  } catch (err) {
142
- WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
169
+ const fallbackDir = resolveToolHookWorkspaceDir(ctx, api, 'before_tool_call');
170
+ WorkspaceContext.fromHookContext({ workspaceDir: fallbackDir }).eventLog.recordHookExecution({
143
171
  hook: 'before_tool_call',
144
172
  error: String(err)
145
- });
173
+ }, { flushImmediately: true });
146
174
  api.logger.error(`[PD] Error in before_tool_call: ${String(err)}`);
147
175
  }
148
176
  }
@@ -152,20 +180,21 @@ const plugin = {
152
180
  api.on(
153
181
  'after_tool_call',
154
182
  (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
155
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
183
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx, api, 'after_tool_call');
156
184
  try {
157
185
  const pluginConfig = api.pluginConfig ?? {};
158
186
  // Pass api separately to handleAfterToolCall to maintain type safety
159
187
  handleAfterToolCall(event, { ...ctx, workspaceDir, pluginConfig }, api);
160
-
188
+
161
189
  WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
162
190
  hook: 'after_tool_call'
163
- });
191
+ }, { flushImmediately: true });
164
192
  } catch (err) {
165
- WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
193
+ const fallbackDir = resolveToolHookWorkspaceDir(ctx, api, 'after_tool_call');
194
+ WorkspaceContext.fromHookContext({ workspaceDir: fallbackDir }).eventLog.recordHookExecution({
166
195
  hook: 'after_tool_call',
167
196
  error: String(err)
168
- });
197
+ }, { flushImmediately: true });
169
198
  api.logger.error(`[PD:EmpathyObserver] Error in after_tool_call: ${String(err)}`);
170
199
  }
171
200
  }
@@ -175,10 +204,10 @@ const plugin = {
175
204
  api.on(
176
205
  'llm_output',
177
206
  (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
178
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
207
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx as unknown as Record<string, unknown>, api, 'llm_output');
179
208
  try {
180
209
  handleLlmOutput(event, { ...ctx, workspaceDir });
181
-
210
+
182
211
  WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
183
212
  hook: 'llm_output',
184
213
  sessionId: ctx.sessionId
@@ -194,28 +223,16 @@ const plugin = {
194
223
  }
195
224
  );
196
225
 
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
226
  // ── Hook: Trajectory Collection (Behavior Evolution Phase 0) ──
210
227
  // Note: after_tool_call and llm_output are safe to collect
211
- // before_message_write conflicts with message-sanitize, skipping for now
212
228
  api.on(
213
229
  'after_tool_call',
214
230
  (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
215
231
  try {
216
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
232
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx, api, 'trajectory.after_tool_call');
217
233
  TrajectoryCollector.handleAfterToolCall(event, { ...ctx, workspaceDir });
218
- } catch (err) {
234
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: catch binding intentionally unused
235
+ } catch (_err) {
219
236
  // Non-critical: don't log, just skip
220
237
  }
221
238
  }
@@ -225,9 +242,10 @@ const plugin = {
225
242
  'llm_output',
226
243
  (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
227
244
  try {
228
- const workspaceDir = ctx.workspaceDir || api.resolvePath('.');
245
+ const workspaceDir = resolveToolHookWorkspaceDir(ctx as unknown as Record<string, unknown>, api, 'trajectory.llm_output');
229
246
  TrajectoryCollector.handleLlmOutput(event, { ...ctx, workspaceDir });
230
- } catch (err) {
247
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: catch binding intentionally unused
248
+ } catch (_err) {
231
249
  // Non-critical: don't log, just skip
232
250
  }
233
251
  }
@@ -236,7 +254,8 @@ const plugin = {
236
254
  // ── Hook: Subagent Loop Closure ──
237
255
  api.on(
238
256
  'subagent_spawning',
239
- (event: PluginHookSubagentSpawningEvent, ctx: PluginHookSubagentContext): void | PluginHookSubagentSpawningResult => {
257
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: ctx param required by hook callback signature but not used in this handler
258
+ (event: PluginHookSubagentSpawningEvent, _ctx: PluginHookSubagentContext): void | PluginHookSubagentSpawningResult => {
240
259
  try {
241
260
  // Resolve workspace via official API, falling back to PathResolver
242
261
  const workspaceDir = resolveWorkspaceDirFromApi(api, event.agentId) || '.';
@@ -259,7 +278,7 @@ const plugin = {
259
278
 
260
279
  if (shouldRecordShadow) {
261
280
  const observation = recordShadowRouting(workspaceDir, {
262
- checkpointId: decision.activeCheckpointId!,
281
+ checkpointId: decision.activeCheckpointId!, // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: !!decision.activeCheckpointId guard above ensures truthiness
263
282
  workerProfile: agentId as WorkerProfile,
264
283
  taskFingerprint: computeRuntimeShadowTaskFingerprint(event),
265
284
  });
@@ -327,6 +346,8 @@ const plugin = {
327
346
  EvolutionWorkerService.api = api;
328
347
  api.registerService(EvolutionWorkerService);
329
348
  api.registerService(TrajectoryService);
349
+ api.registerService(PDTaskService);
350
+ api.registerService(CentralSyncService);
330
351
  } catch (err) {
331
352
  api.logger.error(`[PD] Failed to register EvolutionWorkerService: ${String(err)}`);
332
353
  }
@@ -366,8 +387,8 @@ const plugin = {
366
387
  api.registerCommand({
367
388
  name: "pd-daily",
368
389
  description: getCommandDescription('pd-daily', language),
369
- handler: (_ctx) => {
370
- return { text: language === 'zh'
390
+ handler: () => {
391
+ return { text: language === 'zh'
371
392
  ? "请执行 pd-daily 技能来配置并发送进化日报。系统将引导你完成配置流程,包括发送时间、渠道和报告风格偏好。"
372
393
  : "Please execute the pd-daily skill to configure and send your daily evolution report. The system will guide you through the configuration process." };
373
394
  }
@@ -376,7 +397,7 @@ const plugin = {
376
397
  api.registerCommand({
377
398
  name: "pd-grooming",
378
399
  description: getCommandDescription('pd-grooming', language),
379
- handler: (_ctx) => {
400
+ handler: () => {
380
401
  return { text: language === 'zh'
381
402
  ? "请执行 pd-grooming 技能来执行大扫除。例如输入: '执行 pd-grooming 技能'"
382
403
  : "Please execute the pd-grooming skill to clean up. For example: 'Execute pd-grooming skill'" };
@@ -386,7 +407,7 @@ const plugin = {
386
407
  api.registerCommand({
387
408
  name: "pd-help",
388
409
  description: getCommandDescription('pd-help', language),
389
- handler: (_ctx) => {
410
+ handler: () => {
390
411
  if (language === 'zh') {
391
412
  return { text: `
392
413
  📖 **Principles Disciple 命令大全**
@@ -687,6 +708,71 @@ const plugin = {
687
708
  }
688
709
  });
689
710
 
711
+ // ── Implementation Lifecycle Commands (Phase 13) ──
712
+ api.registerCommand({
713
+ name: "pd-promote-impl",
714
+ description: 'Promote a candidate implementation to active [list|show <id>|<id>]',
715
+ acceptsArgs: true,
716
+ handler: (ctx) => {
717
+ try {
718
+ const workspaceDir = api.resolvePath('.');
719
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
720
+ return handlePromoteImplCommand(ctx);
721
+ } catch (err) {
722
+ api.logger.error(`[PD] Command /pd-promote-impl failed: ${String(err)}`);
723
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
724
+ }
725
+ }
726
+ });
727
+
728
+ api.registerCommand({
729
+ name: "pd-disable-impl",
730
+ description: 'Disable an active implementation [list|<id> --reason "..."]',
731
+ acceptsArgs: true,
732
+ handler: (ctx) => {
733
+ try {
734
+ const workspaceDir = api.resolvePath('.');
735
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
736
+ return handleDisableImplCommand(ctx);
737
+ } catch (err) {
738
+ api.logger.error(`[PD] Command /pd-disable-impl failed: ${String(err)}`);
739
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
740
+ }
741
+ }
742
+ });
743
+
744
+ api.registerCommand({
745
+ name: "pd-archive-impl",
746
+ description: 'Archive an implementation permanently [list|<id>]',
747
+ acceptsArgs: true,
748
+ handler: (ctx) => {
749
+ try {
750
+ const workspaceDir = api.resolvePath('.');
751
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
752
+ return handleArchiveImplCommand(ctx);
753
+ } catch (err) {
754
+ api.logger.error(`[PD] Command /pd-archive-impl failed: ${String(err)}`);
755
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
756
+ }
757
+ }
758
+ });
759
+
760
+ api.registerCommand({
761
+ name: "pd-rollback-impl",
762
+ description: 'Rollback current active implementation to previous active [list|<id> --reason "..."]',
763
+ acceptsArgs: true,
764
+ handler: (ctx) => {
765
+ try {
766
+ const workspaceDir = api.resolvePath('.');
767
+ if (ctx.config) ctx.config.workspaceDir = workspaceDir;
768
+ return handleRollbackImplCommand(ctx);
769
+ } catch (err) {
770
+ api.logger.error(`[PD] Command /pd-rollback-impl failed: ${String(err)}`);
771
+ return { text: language === 'zh' ? '\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u65e5\u5fd7\u3002' : 'Command failed. Check logs.' };
772
+ }
773
+ }
774
+ });
775
+
690
776
  api.registerTool(createDeepReflectTool(api));
691
777
  }
692
778
  };