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
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Disable Implementation Command — CLI `/pd-disable-impl`
3
+ * ========================================================
4
+ *
5
+ * PURPOSE: Manually disable an active implementation (e.g., regression in production).
6
+ *
7
+ * FLOW:
8
+ * 1. List active implementations
9
+ * 2. Select target, ask for reason
10
+ * 3. Transition active -> disabled (or candidate -> disabled)
11
+ * 4. Record disabledAt, disabledBy, disabledReason
12
+ */
13
+
14
+ import { WorkspaceContext } from '../core/workspace-context.js';
15
+ import { refreshPrincipleLifecycle } from '../core/principle-internalization/lifecycle-refresh.js';
16
+ import {
17
+ loadLedger,
18
+ transitionImplementationState,
19
+ updateImplementation,
20
+ getAllowedTransitions,
21
+ } from '../core/principle-tree-ledger.js';
22
+ import type { Implementation, ImplementationLifecycleState } from '../types/principle-tree-schema.js';
23
+ import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
24
+
25
+ /**
26
+ * Get all implementations from the ledger.
27
+ */
28
+ function getAllImplementations(stateDir: string): Implementation[] {
29
+ const ledger = loadLedger(stateDir);
30
+ return Object.values(ledger.tree.implementations);
31
+ }
32
+
33
+ function _handleListActive(
34
+ stateDir: string,
35
+ isZh: boolean
36
+ ): PluginCommandResult {
37
+ const allImpls = getAllImplementations(stateDir);
38
+ const activeImpls = allImpls.filter(
39
+ (impl) => impl.lifecycleState === 'active' || impl.lifecycleState === 'candidate'
40
+ );
41
+
42
+ if (activeImpls.length === 0) {
43
+ return {
44
+ text: isZh
45
+ ? '\n\u2139\ufe0f \u6ca1\u6709\u53ef\u7981\u7528\u7684\u5b9e\u73b0\u3002'
46
+ : '\n\u2139\ufe0f No implementations available to disable.',
47
+ };
48
+ }
49
+
50
+ let output = isZh
51
+ ? '\n\ud83d\udccb \u53ef\u7981\u7528\u5b9e\u73b0\n'
52
+ : '\n\ud83d\udccb Available Implementations to Disable\n';
53
+ output += `${'='.repeat(50)}\n`;
54
+
55
+ for (const impl of activeImpls) {
56
+ const stateLabel = impl.lifecycleState || 'candidate';
57
+ output += ` ${impl.id}\n`;
58
+ output += ` Rule: ${impl.ruleId} | State: ${stateLabel} | Version: ${impl.version}\n`;
59
+ if (impl.disabledReason) {
60
+ output += ` Previous reason: ${impl.disabledReason}\n`;
61
+ }
62
+ output += '\n';
63
+ }
64
+
65
+ output += isZh
66
+ ? `\u7528\u6cd5: /pd-disable-impl <implId> --reason "\u539f\u56e0"`
67
+ : `Usage: /pd-disable-impl <implId> --reason "reason"`;
68
+
69
+ return { text: output };
70
+ }
71
+
72
+ // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
73
+ function _handleDisableImpl(
74
+ workspaceDir: string,
75
+ stateDir: string,
76
+ implId: string,
77
+ reason: string | null,
78
+ isZh: boolean,
79
+ sessionId?: string,
80
+ ): PluginCommandResult {
81
+ const allImpls = getAllImplementations(stateDir);
82
+ const target = allImpls.find((i) => i.id === implId);
83
+
84
+ if (!target) {
85
+ return {
86
+ text: isZh
87
+ ? `\u274c \u672a\u627e\u5230\u5b9e\u73b0: ${implId}`
88
+ : `\u274c Implementation not found: ${implId}`,
89
+ };
90
+ }
91
+
92
+ const currentState = target.lifecycleState || 'candidate';
93
+
94
+ // Validate: active -> disabled or candidate -> disabled
95
+ if (currentState !== 'active' && currentState !== 'candidate') {
96
+ const allowed = getAllowedTransitions(currentState as ImplementationLifecycleState);
97
+ return {
98
+ text: isZh
99
+ ? `\u274c \u5b9e\u73b0 ${implId} \u5f53\u524d\u72b6\u6001: ${currentState}\n\u5141\u8bb8\u8fc7\u6e21: ${allowed.join(', ') || '\u65e0\u8fc7\u6e21'}`
100
+ : `\u274c Implementation ${implId} is in state: ${currentState}\nAllowed transitions: ${allowed.join(', ') || 'none'}`,
101
+ };
102
+ }
103
+
104
+ const reasonText = reason || (isZh ? '\u7528\u6237\u624b\u52a8\u7981\u7528' : 'User manual disable');
105
+
106
+ transitionImplementationState(stateDir, implId, 'disabled');
107
+ updateImplementation(stateDir, implId, {
108
+ disabledAt: new Date().toISOString(),
109
+ disabledBy: sessionId || 'manual',
110
+ disabledReason: reasonText,
111
+ });
112
+ refreshPrincipleLifecycle(workspaceDir, stateDir);
113
+
114
+ return {
115
+ text: isZh
116
+ ? `\n\u2705 \u5b9e\u73b0\u5df2\u7981\u7528: ${implId}\n \u72b6\u6001: ${currentState} -> disabled\n \u539f\u56e0: ${reasonText}`
117
+ : `\n\u2705 Implementation disabled: ${implId}\n State: ${currentState} -> disabled\n Reason: ${reasonText}`,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Handle the /pd-disable-impl command.
123
+ *
124
+ * Usage:
125
+ * /pd-disable-impl list - List active implementations
126
+ * /pd-disable-impl <implId> - Disable an implementation
127
+ * /pd-disable-impl <implId> --reason "<reason>" - Disable with reason
128
+ */
129
+ export function handleDisableImplCommand(ctx: PluginCommandContext): PluginCommandResult {
130
+ const workspaceDir = (ctx.config?.workspaceDir as string) || process.cwd();
131
+ const {stateDir} = WorkspaceContext.fromHookContext({ ...ctx, workspaceDir });
132
+ const lang = (ctx.config?.language as string) || 'en';
133
+ const isZh = lang === 'zh';
134
+
135
+ const args = (ctx.args || '').trim();
136
+
137
+ // Parse args: [implId] [--reason "..."]
138
+ const parts = args.split(/\s+/);
139
+ const subcommand = parts[0] || '';
140
+ const implId = subcommand === 'list' ? '' : subcommand;
141
+ const reasonMatch = (/--reason\s+"([^"]+)"/.exec(args)) || (/--reason\s+(\S+)/.exec(args));
142
+ const reason = reasonMatch ? reasonMatch[1] : null;
143
+
144
+ // Subcommand: list
145
+ if (subcommand === 'list' || subcommand === '') {
146
+ return _handleListActive(stateDir, isZh);
147
+ }
148
+
149
+ // Disable
150
+ return _handleDisableImpl(workspaceDir, stateDir, implId, reason, isZh, ctx.sessionId);
151
+ }
@@ -1,7 +1,9 @@
1
- import { EvolutionReducerImpl } from '../core/evolution-reducer.js';
1
+ import type { EvolutionReducerImpl } from '../core/evolution-reducer.js';
2
+ import type { InternalizationRouteRecommendation } from '../core/principle-internalization/internalization-routing-policy.js';
3
+ import { WorkspaceContext } from '../core/workspace-context.js';
2
4
  import { normalizeLanguage } from '../i18n/commands.js';
3
- import { RuntimeSummaryService } from '../service/runtime-summary-service.js';
4
5
  import type { PluginCommandContext } from '../openclaw-sdk.js';
6
+ import { RuntimeSummaryService } from '../service/runtime-summary-service.js';
5
7
 
6
8
  function formatNumber(value: number | null): string {
7
9
  if (value === null || Number.isNaN(value)) {
@@ -11,7 +13,7 @@ function formatNumber(value: number | null): string {
11
13
  }
12
14
 
13
15
  function formatSources(
14
- sources: Array<{ source: string; score?: number }>
16
+ sources: { source: string; score?: number }[],
15
17
  ): string {
16
18
  if (sources.length === 0) {
17
19
  return '--';
@@ -21,17 +23,36 @@ function formatSources(
21
23
  .map((source) =>
22
24
  source.score === undefined
23
25
  ? source.source
24
- : `${source.source}(${formatNumber(source.score)})`
26
+ : `${source.source}(${formatNumber(source.score)})`,
25
27
  )
26
28
  .join(', ');
27
29
  }
28
30
 
31
+ function formatRouteRecommendations(
32
+ recommendations: InternalizationRouteRecommendation[],
33
+ emptyLabel: string,
34
+ ): string {
35
+ if (recommendations.length === 0) {
36
+ return emptyLabel;
37
+ }
38
+
39
+ return recommendations
40
+ .slice(0, 3)
41
+ .map(
42
+ (recommendation) =>
43
+ `${recommendation.principleId}:${recommendation.route}@${formatNumber(recommendation.confidence)}`,
44
+ )
45
+ .join(', ');
46
+ }
47
+
48
+ // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
29
49
  function buildEnglishOutput(
30
50
  workspaceDir: string,
31
51
  sessionId: string | null,
32
52
  warnings: string[],
33
53
  stats: ReturnType<EvolutionReducerImpl['getStats']>,
34
- summary: ReturnType<typeof RuntimeSummaryService.getSummary>
54
+ summary: ReturnType<typeof RuntimeSummaryService.getSummary>,
55
+ recommendations: InternalizationRouteRecommendation[],
35
56
  ): string {
36
57
  const lines: string[] = [
37
58
  'Evolution Status',
@@ -47,7 +68,7 @@ function buildEnglishOutput(
47
68
  'Evolution',
48
69
  `- Queue: pending ${summary.evolution.queue.pending}, in_progress ${summary.evolution.queue.inProgress}, completed ${summary.evolution.queue.completed} (${summary.evolution.dataQuality})`,
49
70
  `- Legacy Directive File: ${summary.phase3.legacyDirectiveFilePresent ? 'present' : 'missing'} (compatibility-only display artifact)`,
50
- `- Note: Legacy directive file is NOT a truth source for Phase 3 eligibility. Queue is the only authoritative execution truth source.`,
71
+ '- Note: Legacy directive file is NOT a truth source for Phase 3 eligibility. Queue is the only authoritative execution truth source.',
51
72
  `- Active Evolution Task: ${summary.evolution.directive.taskPreview ?? '--'}`,
52
73
  `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'}, queueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'}, eligible ${summary.phase3.evolutionEligible}, reference_only ${summary.phase3.evolutionReferenceOnly}, rejected ${summary.phase3.evolutionRejected}${summary.phase3.evolutionReferenceOnlyReasons.length > 0 ? ` (reference ${summary.phase3.evolutionReferenceOnlyReasons.slice(0, 2).join(', ')})` : ''}${summary.phase3.evolutionRejectedReasons.length > 0 ? ` (${summary.phase3.evolutionRejectedReasons.slice(0, 3).join(', ')})` : ''}`,
53
74
  `- Phase 3 Legacy Directive File: ${summary.phase3.directiveStatus} (${summary.phase3.directiveIgnoredReason})`,
@@ -58,6 +79,7 @@ function buildEnglishOutput(
58
79
  `- active principles: ${stats.activeCount}`,
59
80
  `- deprecated principles: ${stats.deprecatedCount}`,
60
81
  `- last promoted: ${stats.lastPromotedAt ?? 'none'}`,
82
+ `- internalization routes: ${formatRouteRecommendations(recommendations, '--')}`,
61
83
  '',
62
84
  'Metadata',
63
85
  `- workspace: ${workspaceDir}`,
@@ -75,30 +97,32 @@ function buildEnglishOutput(
75
97
  return lines.join('\n');
76
98
  }
77
99
 
100
+ // eslint-disable-next-line @typescript-eslint/max-params -- Reason: Command handler signature must match OpenClaw plugin interface - breaking API change to options objects would affect public contracts
78
101
  function buildChineseOutput(
79
102
  workspaceDir: string,
80
103
  sessionId: string | null,
81
104
  warnings: string[],
82
105
  stats: ReturnType<EvolutionReducerImpl['getStats']>,
83
- summary: ReturnType<typeof RuntimeSummaryService.getSummary>
106
+ summary: ReturnType<typeof RuntimeSummaryService.getSummary>,
107
+ recommendations: InternalizationRouteRecommendation[],
84
108
  ): string {
85
109
  const lines: string[] = [
86
110
  '进化状态',
87
111
  '================',
88
112
  '',
89
113
  '控制面',
90
- `- 会话 GFI: 当前 ${formatNumber(summary.gfi.current)},峰值 ${formatNumber(summary.gfi.peak)}(${summary.gfi.dataQuality})`,
114
+ `- 会话 GFI: 当前 ${formatNumber(summary.gfi.current)},峰值 ${formatNumber(summary.gfi.peak)} (${summary.gfi.dataQuality})`,
91
115
  `- GFI 来源: ${formatSources(summary.gfi.sources)}`,
92
- `- Pain Flag: ${summary.pain.activeFlag ? 'active' : 'inactive'}${summary.pain.activeFlagSource ? `(${summary.pain.activeFlagSource})` : ''}`,
116
+ `- Pain Flag: ${summary.pain.activeFlag ? 'active' : 'inactive'}${summary.pain.activeFlagSource ? ` (${summary.pain.activeFlagSource})` : ''}`,
93
117
  `- 最近 Pain 信号: ${summary.pain.lastSignal ? `${summary.pain.lastSignal.source}${summary.pain.lastSignal.reason ? ` - ${summary.pain.lastSignal.reason}` : ''}` : '--'}`,
94
- `- Gate 事件: block ${formatNumber(summary.gate.recentBlocks)},bypass ${formatNumber(summary.gate.recentBypasses)}(${summary.gate.dataQuality})`,
118
+ `- Gate 事件: block ${formatNumber(summary.gate.recentBlocks)},bypass ${formatNumber(summary.gate.recentBypasses)} (${summary.gate.dataQuality})`,
95
119
  '',
96
120
  '进化',
97
- `- 队列: pending ${summary.evolution.queue.pending},in_progress ${summary.evolution.queue.inProgress},completed ${summary.evolution.queue.completed}(${summary.evolution.dataQuality})`,
98
- `- Legacy Directive File: ${summary.phase3.legacyDirectiveFilePresent ? 'present' : 'missing'} (兼容仅显示产物)`,
99
- `- 注:Legacy directive file 不是 Phase 3 合格性的真实源。队列是唯一权威的执行真实源。`,
100
- `- Active Evolution Task: ${summary.evolution.directive.taskPreview ?? '--'}`,
101
- `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'},queueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'},eligible ${summary.phase3.evolutionEligible},reference_only ${summary.phase3.evolutionReferenceOnly},rejected ${summary.phase3.evolutionRejected}${summary.phase3.evolutionReferenceOnlyReasons.length > 0 ? `(reference ${summary.phase3.evolutionReferenceOnlyReasons.slice(0, 2).join(', ')})` : ''}${summary.phase3.evolutionRejectedReasons.length > 0 ? `(${summary.phase3.evolutionRejectedReasons.slice(0, 3).join(', ')})` : ''}`,
121
+ `- 队列: pending ${summary.evolution.queue.pending},in_progress ${summary.evolution.queue.inProgress},completed ${summary.evolution.queue.completed} (${summary.evolution.dataQuality})`,
122
+ `- Legacy Directive File: ${summary.phase3.legacyDirectiveFilePresent ? 'present' : 'missing'}(仅兼容展示)`,
123
+ '- 注意:Legacy directive file 不是 Phase 3 合格性的真实来源,队列才是唯一权威的执行真相。',
124
+ `- 当前进化任务: ${summary.evolution.directive.taskPreview ?? '--'}`,
125
+ `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'},queueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'},eligible ${summary.phase3.evolutionEligible},reference_only ${summary.phase3.evolutionReferenceOnly},rejected ${summary.phase3.evolutionRejected}${summary.phase3.evolutionReferenceOnlyReasons.length > 0 ? ` (reference ${summary.phase3.evolutionReferenceOnlyReasons.slice(0, 2).join(', ')})` : ''}${summary.phase3.evolutionRejectedReasons.length > 0 ? ` (${summary.phase3.evolutionRejectedReasons.slice(0, 3).join(', ')})` : ''}`,
102
126
  `- Phase 3 Legacy Directive File: ${summary.phase3.directiveStatus} (${summary.phase3.directiveIgnoredReason})`,
103
127
  '',
104
128
  '原则统计',
@@ -107,10 +131,11 @@ function buildChineseOutput(
107
131
  `- 生效原则: ${stats.activeCount}`,
108
132
  `- 已废弃原则: ${stats.deprecatedCount}`,
109
133
  `- 最近晋升: ${stats.lastPromotedAt ?? '无'}`,
134
+ `- 内化路由: ${formatRouteRecommendations(recommendations, '--')}`,
110
135
  '',
111
136
  '元数据',
112
137
  `- 工作区: ${workspaceDir}`,
113
- `- Session: ${sessionId ?? '--'}(${summary.metadata.selectedSessionReason})`,
138
+ `- Session: ${sessionId ?? '--'} (${summary.metadata.selectedSessionReason})`,
114
139
  `- 生成时间: ${summary.metadata.generatedAt}`,
115
140
  ];
116
141
 
@@ -127,20 +152,40 @@ function buildChineseOutput(
127
152
  export function handleEvolutionStatusCommand(ctx: PluginCommandContext): { text: string } {
128
153
  const workspaceDir = (ctx.config?.workspaceDir as string) || process.cwd();
129
154
  const sessionId = (ctx as { sessionId?: string | null }).sessionId ?? null;
130
- const reducer = new EvolutionReducerImpl({ workspaceDir });
155
+ // #207/#210: Use WorkspaceContext to get evolutionReducer with stateDir
156
+ const wctx = WorkspaceContext.fromHookContext({ workspaceDir });
157
+ const reducer = wctx.evolutionReducer;
131
158
  const stats = reducer.getStats();
132
159
  const summary = RuntimeSummaryService.getSummary(workspaceDir, { sessionId });
160
+ const recommendations = WorkspaceContext.fromHookContext({ workspaceDir })
161
+ .principleLifecycle
162
+ .recomputeAll()
163
+ .map((assessment) => assessment.routeRecommendation);
133
164
  const rawLang = (ctx.config?.language as string) || 'en';
134
165
  const lang = normalizeLanguage(rawLang);
135
166
  const warnings = summary.metadata.warnings.slice(0, 12);
136
167
 
137
168
  if (lang === 'zh') {
138
169
  return {
139
- text: buildChineseOutput(workspaceDir, summary.metadata.sessionId, warnings, stats, summary),
170
+ text: buildChineseOutput(
171
+ workspaceDir,
172
+ summary.metadata.sessionId,
173
+ warnings,
174
+ stats,
175
+ summary,
176
+ recommendations,
177
+ ),
140
178
  };
141
179
  }
142
180
 
143
181
  return {
144
- text: buildEnglishOutput(workspaceDir, summary.metadata.sessionId, warnings, stats, summary),
182
+ text: buildEnglishOutput(
183
+ workspaceDir,
184
+ summary.metadata.sessionId,
185
+ warnings,
186
+ stats,
187
+ summary,
188
+ recommendations,
189
+ ),
145
190
  };
146
191
  }
@@ -42,14 +42,16 @@ export function handleExportCommand(ctx: PluginCommandContext): PluginCommandRes
42
42
  };
43
43
  }
44
44
 
45
+
46
+
45
47
  return {
46
48
  text: zh
47
- ? `已导出 ORPO 决策点样本到 ${result.manifest!.exportPath},` +
48
- `共 ${result.manifest!.sampleCount} 条,模型家族: ${result.manifest!.targetModelFamily},` +
49
- `数据集指纹: ${result.manifest!.datasetFingerprint.substring(0, 16)}...`
50
- : `Exported ORPO decision-point samples to ${result.manifest!.exportPath}, ` +
51
- `${result.manifest!.sampleCount} samples, target: ${result.manifest!.targetModelFamily}, ` +
52
- `dataset fingerprint: ${result.manifest!.datasetFingerprint.substring(0, 16)}...`,
49
+ ? `已导出 ORPO 决策点样本到 ${result.manifest!.exportPath},` + // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: caller guarantees manifest exists via success check
50
+ `共 ${result.manifest!.sampleCount} 条,模型家族: ${result.manifest!.targetModelFamily},` + // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: caller guarantees manifest exists via success check
51
+ `数据集指纹: ${result.manifest!.datasetFingerprint.substring(0, 16)}...` // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: caller guarantees manifest exists via success check
52
+ : `Exported ORPO decision-point samples to ${result.manifest!.exportPath}, ` + // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: caller guarantees manifest exists via success check
53
+ `${result.manifest!.sampleCount} samples, target: ${result.manifest!.targetModelFamily}, ` + // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: caller guarantees manifest exists via success check
54
+ `dataset fingerprint: ${result.manifest!.datasetFingerprint.substring(0, 16)}...`, // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: caller guarantees manifest exists via success check
53
55
  };
54
56
  }
55
57
 
@@ -10,7 +10,6 @@
10
10
 
11
11
  import * as fs from 'fs';
12
12
  import * as path from 'path';
13
- import { randomUUID } from 'node:crypto';
14
13
  import type { PluginCommandContext, PluginCommandResult, OpenClawPluginApi } from '../openclaw-sdk.js';
15
14
  import { WorkspaceContext } from '../core/workspace-context.js';
16
15
  import {
@@ -24,19 +23,6 @@ import {
24
23
  cleanupStaleInfo,
25
24
  } from '../core/focus-history.js';
26
25
 
27
- /**
28
- * 清理 Markdown 代码块围栏
29
- * 移除开头的 ```lang 和结尾的 ```
30
- */
31
- function stripMarkdownFence(content: string): string {
32
- let result = content.trim();
33
- // 移除开头的代码块标记(如 ```markdown, ```text 等)
34
- result = result.replace(/^```[\w]*\n?/, '');
35
- // 移除结尾的代码块标记
36
- result = result.replace(/\n?```$/, '');
37
- return result.trim();
38
- }
39
-
40
26
  /**
41
27
  * 获取工作区目录
42
28
  */
@@ -295,6 +281,7 @@ async function compressFocus(
295
281
  cleanupHistory(focusPath);
296
282
 
297
283
  // 5. 压缩内容
284
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in both try and catch blocks
298
285
  let compressedContent: string;
299
286
  try {
300
287
  compressedContent = compressFocusContent(oldContent, workspaceDir);
@@ -305,10 +292,9 @@ async function compressFocus(
305
292
  }
306
293
 
307
294
  // 6. 更新版本号和日期
308
- const versionParts = oldVersion.split('.');
309
- const majorVersion = parseInt(versionParts[0], 10) || 1;
310
- const newVersion = `${majorVersion + 1}`;
311
- const today = new Date().toISOString().split('T')[0];
295
+ const [majorVersion] = oldVersion.split('.');
296
+ const newVersion = `${(parseInt(majorVersion, 10) || 1) + 1}`;
297
+ const [today] = new Date().toISOString().split('T');
312
298
  const newContent = compressedContent
313
299
  .replace(/\*\*版本\*\*:\s*v[\d.]+/i, `**版本**: v${newVersion}`)
314
300
  .replace(/\*\*更新\*\*:\s*\d{4}-\d{2}-\d{2}/, `**更新**: ${today}`);
@@ -398,7 +384,7 @@ function rollbackFocus(workspaceDir: string, index: number, isZh: boolean): stri
398
384
  // 恢复历史版本
399
385
  const restoredVersion = extractVersion(historyContent);
400
386
  const restoredDate = extractDate(historyContent);
401
- const today = new Date().toISOString().split('T')[0];
387
+ const [today] = new Date().toISOString().split('T');
402
388
 
403
389
  // 获取最大版本号(从当前文件或历史文件中)
404
390
  let maxVersion = parseFloat(restoredVersion) || 1;
@@ -491,6 +477,7 @@ export async function handleFocusCommand(
491
477
  // 检测语言(与 context.ts 保持一致)
492
478
  const isZh = (ctx.config?.language as string) === 'zh';
493
479
 
480
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in all switch cases
494
481
  let result: string;
495
482
 
496
483
  switch (subCommand) {
@@ -506,7 +493,7 @@ export async function handleFocusCommand(
506
493
  result = await compressFocus(workspaceDir, isZh, api);
507
494
  break;
508
495
  case 'rollback':
509
- case 'rb':
496
+ case 'rb': {
510
497
  const index = parseInt(args[1], 10);
511
498
  if (isNaN(index)) {
512
499
  result = isZh
@@ -516,6 +503,7 @@ export async function handleFocusCommand(
516
503
  result = rollbackFocus(workspaceDir, index, isZh);
517
504
  }
518
505
  break;
506
+ }
519
507
  case 'help':
520
508
  case '--help':
521
509
  case '-h':
@@ -27,7 +27,6 @@ import {
27
27
  getDatasetRecord,
28
28
  updateReviewStatus,
29
29
  updateTargetModelFamily,
30
- readDatasetArtifact,
31
30
  getDatasetStats,
32
31
  type NocturnalDatasetRecord,
33
32
  type NocturnalReviewStatus,
@@ -53,7 +52,7 @@ function formatRecord(record: NocturnalDatasetRecord, includeDetails = false): s
53
52
  lines.push(` createdAt: ${new Date(record.createdAt).toLocaleString('en-US')}`);
54
53
 
55
54
  if (includeDetails) {
56
- const artifactPath = record.artifactPath;
55
+ const {artifactPath} = record;
57
56
  if (fs.existsSync(artifactPath)) {
58
57
  try {
59
58
  const content = fs.readFileSync(artifactPath, 'utf-8');
@@ -122,7 +121,7 @@ export function handleNocturnalReviewCommand(ctx: PluginCommandContext): PluginC
122
121
  }
123
122
 
124
123
  if (action === 'show') {
125
- const fingerprint = parts[1];
124
+ const [, fingerprint] = parts;
126
125
  if (!fingerprint) {
127
126
  return {
128
127
  text: zh
@@ -149,7 +148,7 @@ export function handleNocturnalReviewCommand(ctx: PluginCommandContext): PluginC
149
148
  }
150
149
 
151
150
  if (action === 'approve') {
152
- const fingerprint = parts[1];
151
+ const [, fingerprint] = parts;
153
152
  const reason = parts.slice(2).join(' ') || (zh ? 'Approved by human reviewer' : 'Approved by human reviewer');
154
153
 
155
154
  if (!fingerprint) {
@@ -203,7 +202,7 @@ export function handleNocturnalReviewCommand(ctx: PluginCommandContext): PluginC
203
202
  }
204
203
 
205
204
  if (action === 'reject') {
206
- const fingerprint = parts[1];
205
+ const [, fingerprint] = parts;
207
206
  const reason = parts.slice(2).join(' ') || (zh ? 'Rejected by human reviewer' : 'Rejected by human reviewer');
208
207
 
209
208
  if (!fingerprint) {
@@ -249,8 +248,7 @@ export function handleNocturnalReviewCommand(ctx: PluginCommandContext): PluginC
249
248
  }
250
249
 
251
250
  if (action === 'set-family') {
252
- const fingerprint = parts[1];
253
- const family = parts[2];
251
+ const [, fingerprint, family] = parts;
254
252
 
255
253
  if (!fingerprint || !family) {
256
254
  return {
@@ -24,15 +24,12 @@
24
24
  * /nocturnal-rollout show-promotion <checkpointId>
25
25
  */
26
26
 
27
- import { WorkspaceContext } from '../core/workspace-context.js';
28
27
  import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
29
- import type { TrainableWorkerProfile } from '../core/external-training-contract.js';
30
28
  import {
31
29
  evaluatePromotionGate,
32
30
  advancePromotion,
33
31
  getPromotionRecord,
34
32
  listPromotionsByState,
35
- rejectCheckpoint,
36
33
  DEFAULT_BASELINE_METRICS,
37
34
  DEFAULT_MIN_DELTA,
38
35
  DEFAULT_ALLOWED_MARGIN,
@@ -44,9 +41,6 @@ import {
44
41
  disableRoutingForProfile,
45
42
  rollbackDeployment,
46
43
  getDeployment,
47
- getDeploymentLineage,
48
- listDeployments,
49
- getFullDeploymentRegistry,
50
44
  isRoutingEnabledForProfile,
51
45
  type WorkerProfile,
52
46
  } from '../core/model-deployment-registry.js';
@@ -61,7 +55,6 @@ import {
61
55
  } from '../core/shadow-observation-registry.js';
62
56
  import {
63
57
  getCheckpoint,
64
- listCheckpoints,
65
58
  } from '../core/model-training-registry.js';
66
59
 
67
60
  function isZh(ctx: PluginCommandContext): boolean {
@@ -473,10 +466,6 @@ Routing: ${deployment.routingEnabled ? 'Enabled' : 'Disabled'}`,
473
466
  ? getCheckpoint(workspaceDir, deployment.activeCheckpointId)
474
467
  : null;
475
468
 
476
- const lineage = deployment.activeCheckpointId
477
- ? getDeploymentLineage(workspaceDir, profile)
478
- : null;
479
-
480
469
  return {
481
470
  text: zh
482
471
  ? `=== ${profile} 部署状态 ===
@@ -583,7 +572,7 @@ ${promotion.previousPromotionId ? `Previous Promotion: ${promotion.previousPromo
583
572
 
584
573
  // ── List by State ─────────────────────────────────────────────────────
585
574
  if (subcommand === 'list-by-state') {
586
- const stateArg = parts[1];
575
+ const [, stateArg] = parts;
587
576
  if (!stateArg) {
588
577
  return { text: zh ? '错误: 需要状态参数' : 'Error: state argument required' };
589
578
  }