principles-disciple 1.7.6 → 1.8.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 (106) hide show
  1. package/dist/commands/context.js +5 -15
  2. package/dist/commands/evolution-status.js +2 -9
  3. package/dist/commands/export.js +61 -8
  4. package/dist/commands/nocturnal-review.d.ts +24 -0
  5. package/dist/commands/nocturnal-review.js +265 -0
  6. package/dist/commands/nocturnal-rollout.d.ts +27 -0
  7. package/dist/commands/nocturnal-rollout.js +671 -0
  8. package/dist/commands/nocturnal-train.d.ts +25 -0
  9. package/dist/commands/nocturnal-train.js +919 -0
  10. package/dist/commands/pain.js +8 -21
  11. package/dist/constants/tools.d.ts +2 -2
  12. package/dist/constants/tools.js +1 -1
  13. package/dist/core/adaptive-thresholds.d.ts +186 -0
  14. package/dist/core/adaptive-thresholds.js +300 -0
  15. package/dist/core/config.d.ts +2 -38
  16. package/dist/core/config.js +6 -61
  17. package/dist/core/event-log.d.ts +1 -2
  18. package/dist/core/event-log.js +0 -3
  19. package/dist/core/evolution-engine.js +1 -21
  20. package/dist/core/evolution-reducer.d.ts +7 -1
  21. package/dist/core/evolution-reducer.js +56 -4
  22. package/dist/core/evolution-types.d.ts +61 -9
  23. package/dist/core/evolution-types.js +31 -9
  24. package/dist/core/external-training-contract.d.ts +276 -0
  25. package/dist/core/external-training-contract.js +269 -0
  26. package/dist/core/local-worker-routing.d.ts +175 -0
  27. package/dist/core/local-worker-routing.js +525 -0
  28. package/dist/core/model-deployment-registry.d.ts +218 -0
  29. package/dist/core/model-deployment-registry.js +503 -0
  30. package/dist/core/model-training-registry.d.ts +295 -0
  31. package/dist/core/model-training-registry.js +475 -0
  32. package/dist/core/nocturnal-arbiter.d.ts +159 -0
  33. package/dist/core/nocturnal-arbiter.js +534 -0
  34. package/dist/core/nocturnal-candidate-scoring.d.ts +137 -0
  35. package/dist/core/nocturnal-candidate-scoring.js +266 -0
  36. package/dist/core/nocturnal-compliance.d.ts +175 -0
  37. package/dist/core/nocturnal-compliance.js +824 -0
  38. package/dist/core/nocturnal-dataset.d.ts +224 -0
  39. package/dist/core/nocturnal-dataset.js +443 -0
  40. package/dist/core/nocturnal-executability.d.ts +85 -0
  41. package/dist/core/nocturnal-executability.js +331 -0
  42. package/dist/core/nocturnal-export.d.ts +124 -0
  43. package/dist/core/nocturnal-export.js +275 -0
  44. package/dist/core/nocturnal-paths.d.ts +124 -0
  45. package/dist/core/nocturnal-paths.js +214 -0
  46. package/dist/core/nocturnal-trajectory-extractor.d.ts +242 -0
  47. package/dist/core/nocturnal-trajectory-extractor.js +307 -0
  48. package/dist/core/nocturnal-trinity.d.ts +311 -0
  49. package/dist/core/nocturnal-trinity.js +880 -0
  50. package/dist/core/paths.d.ts +6 -0
  51. package/dist/core/paths.js +6 -0
  52. package/dist/core/principle-training-state.d.ts +121 -0
  53. package/dist/core/principle-training-state.js +321 -0
  54. package/dist/core/promotion-gate.d.ts +238 -0
  55. package/dist/core/promotion-gate.js +529 -0
  56. package/dist/core/session-tracker.d.ts +10 -0
  57. package/dist/core/session-tracker.js +14 -0
  58. package/dist/core/shadow-observation-registry.d.ts +217 -0
  59. package/dist/core/shadow-observation-registry.js +308 -0
  60. package/dist/core/training-program.d.ts +233 -0
  61. package/dist/core/training-program.js +433 -0
  62. package/dist/core/trajectory.d.ts +95 -1
  63. package/dist/core/trajectory.js +220 -6
  64. package/dist/core/workspace-context.d.ts +0 -6
  65. package/dist/core/workspace-context.js +0 -12
  66. package/dist/hooks/bash-risk.d.ts +6 -6
  67. package/dist/hooks/bash-risk.js +8 -8
  68. package/dist/hooks/gate-block-helper.js +1 -1
  69. package/dist/hooks/gate.d.ts +1 -1
  70. package/dist/hooks/gate.js +2 -2
  71. package/dist/hooks/gfi-gate.d.ts +3 -3
  72. package/dist/hooks/gfi-gate.js +15 -14
  73. package/dist/hooks/pain.js +6 -9
  74. package/dist/hooks/progressive-trust-gate.d.ts +21 -49
  75. package/dist/hooks/progressive-trust-gate.js +51 -204
  76. package/dist/hooks/prompt.d.ts +11 -11
  77. package/dist/hooks/prompt.js +158 -72
  78. package/dist/hooks/subagent.js +43 -6
  79. package/dist/i18n/commands.js +8 -8
  80. package/dist/index.js +129 -28
  81. package/dist/service/evolution-worker.d.ts +42 -4
  82. package/dist/service/evolution-worker.js +321 -13
  83. package/dist/service/nocturnal-runtime.d.ts +183 -0
  84. package/dist/service/nocturnal-runtime.js +352 -0
  85. package/dist/service/nocturnal-service.d.ts +163 -0
  86. package/dist/service/nocturnal-service.js +787 -0
  87. package/dist/service/nocturnal-target-selector.d.ts +145 -0
  88. package/dist/service/nocturnal-target-selector.js +315 -0
  89. package/dist/service/phase3-input-filter.d.ts +2 -23
  90. package/dist/service/phase3-input-filter.js +3 -27
  91. package/dist/service/runtime-summary-service.d.ts +0 -10
  92. package/dist/service/runtime-summary-service.js +1 -54
  93. package/dist/tools/deep-reflect.js +2 -1
  94. package/dist/types/event-types.d.ts +2 -10
  95. package/dist/types/runtime-summary.d.ts +1 -8
  96. package/dist/types.d.ts +0 -3
  97. package/dist/types.js +0 -2
  98. package/openclaw.plugin.json +1 -1
  99. package/package.json +1 -1
  100. package/templates/langs/en/skills/pd-mentor/SKILL.md +5 -5
  101. package/templates/langs/zh/skills/pd-mentor/SKILL.md +5 -5
  102. package/templates/pain_settings.json +0 -6
  103. package/dist/commands/trust.d.ts +0 -4
  104. package/dist/commands/trust.js +0 -78
  105. package/dist/core/trust-engine.d.ts +0 -96
  106. package/dist/core/trust-engine.js +0 -286
@@ -64,7 +64,6 @@ function showStatus(workspaceDir, isZh) {
64
64
  |------|------|------|
65
65
  | 核心原则 | ✅ 始终开启 | 不可关闭 |
66
66
  | 思维模型 | ${config.thinkingOs ? '✅ 开启' : '❌ 关闭'} | /pd-context thinking on/off |
67
- | 信任分数 | ${config.trustScore ? '✅ 开启' : '❌ 关闭'} | /pd-context trust on/off |
68
67
  | 反思日志 | ${config.reflectionLog ? '✅ 开启' : '❌ 关闭'} | /pd-context reflection on/off |
69
68
  | 项目上下文 | ${formatProjectFocus(config.projectFocus, isZh)} | /pd-context focus full/summary/off |
70
69
 
@@ -79,7 +78,6 @@ function showStatus(workspaceDir, isZh) {
79
78
  |---------|--------|---------|
80
79
  | Core Principles | ✅ Always ON | Not configurable |
81
80
  | Thinking OS | ${config.thinkingOs ? '✅ ON' : '❌ OFF'} | /pd-context thinking on/off |
82
- | Trust Score | ${config.trustScore ? '✅ ON' : '❌ OFF'} | /pd-context trust on/off |
83
81
  | Reflection Log | ${config.reflectionLog ? '✅ ON' : '❌ OFF'} | /pd-context reflection on/off |
84
82
  | Project Context | ${formatProjectFocus(config.projectFocus, isZh)} | /pd-context focus full/summary/off |
85
83
 
@@ -106,7 +104,7 @@ function toggleSetting(workspaceDir, key, value, isZh) {
106
104
  }
107
105
  const newValue = config[key];
108
106
  const keyName = isZh
109
- ? { thinkingOs: '思维模型', trustScore: '信任分数', reflectionLog: '反思日志' }[key]
107
+ ? { thinkingOs: '思维模型', reflectionLog: '反思日志' }[key]
110
108
  : key;
111
109
  // No change needed
112
110
  if (oldValue === newValue) {
@@ -184,7 +182,6 @@ function applyPreset(workspaceDir, preset, isZh) {
184
182
  case 'minimal':
185
183
  config = {
186
184
  thinkingOs: false,
187
- trustScore: true,
188
185
  reflectionLog: false,
189
186
  projectFocus: 'off',
190
187
  evolutionContext: { ...defaultContextConfig.evolutionContext }
@@ -193,7 +190,6 @@ function applyPreset(workspaceDir, preset, isZh) {
193
190
  case 'standard':
194
191
  config = {
195
192
  thinkingOs: true,
196
- trustScore: true,
197
193
  reflectionLog: false,
198
194
  projectFocus: 'off',
199
195
  evolutionContext: { ...defaultContextConfig.evolutionContext }
@@ -202,7 +198,6 @@ function applyPreset(workspaceDir, preset, isZh) {
202
198
  case 'full':
203
199
  config = {
204
200
  thinkingOs: true,
205
- trustScore: true,
206
201
  reflectionLog: true,
207
202
  projectFocus: 'summary',
208
203
  evolutionContext: { ...defaultContextConfig.evolutionContext }
@@ -236,13 +231,12 @@ function showHelp(isZh) {
236
231
 
237
232
  **单项控制**:
238
233
  \`/pd-context thinking on/off\` - 开关思维模型
239
- \`/pd-context trust on/off\` - 开关信任分数
240
234
  \`/pd-context reflection on/off\` - 开关反思日志
241
235
  \`/pd-context focus full/summary/off\` - 设置项目上下文模式
242
236
 
243
237
  **预设模式**:
244
- \`/pd-context minimal\` - 最小模式(仅信任分数)
245
- \`/pd-context standard\` - 标准模式(原则+思维模型+信任分数)
238
+ \`/pd-context minimal\` - 最小模式(仅核心原则)
239
+ \`/pd-context standard\` - 标准模式(原则+思维模型)
246
240
  \`/pd-context full\` - 完整模式(全部开启)
247
241
 
248
242
  **注意**: 核心原则始终注入,不可关闭。
@@ -257,13 +251,12 @@ function showHelp(isZh) {
257
251
 
258
252
  **Individual Control**:
259
253
  \`/pd-context thinking on/off\` - Toggle Thinking OS
260
- \`/pd-context trust on/off\` - Toggle Trust Score
261
254
  \`/pd-context reflection on/off\` - Toggle Reflection Log
262
255
  \`/pd-context focus full/summary/off\` - Set Project Context mode
263
256
 
264
257
  **Presets**:
265
- \`/pd-context minimal\` - Minimal mode (trust score only)
266
- \`/pd-context standard\` - Standard mode (principles + thinking + trust)
258
+ \`/pd-context minimal\` - Minimal mode (core principles only)
259
+ \`/pd-context standard\` - Standard mode (principles + thinking)
267
260
  \`/pd-context full\` - Full mode (all enabled)
268
261
 
269
262
  **Note**: Core Principles are always injected and cannot be disabled.
@@ -288,9 +281,6 @@ export function handleContextCommand(ctx) {
288
281
  case 'thinking':
289
282
  result = toggleSetting(workspaceDir, 'thinkingOs', value, isZh);
290
283
  break;
291
- case 'trust':
292
- result = toggleSetting(workspaceDir, 'trustScore', value, isZh);
293
- break;
294
284
  case 'reflection':
295
285
  result = toggleSetting(workspaceDir, 'reflectionLog', value, isZh);
296
286
  break;
@@ -7,9 +7,6 @@ function formatNumber(value) {
7
7
  }
8
8
  return Number.isInteger(value) ? String(value) : value.toFixed(1);
9
9
  }
10
- function formatStage(value) {
11
- return value === null ? '--' : String(value);
12
- }
13
10
  function formatSources(sources) {
14
11
  if (sources.length === 0) {
15
12
  return '--';
@@ -26,8 +23,6 @@ function buildEnglishOutput(workspaceDir, sessionId, warnings, stats, summary) {
26
23
  '================',
27
24
  '',
28
25
  'Control Plane',
29
- `- Legacy Trust: ${formatNumber(summary.legacyTrust.score)}/100 (stage ${formatStage(summary.legacyTrust.stage)}, legacy/frozen, ${summary.legacyTrust.rewardPolicy})`,
30
- `- Runtime Trust Truth: score ${formatNumber(summary.runtime.currentTrustScore)}, classification ${summary.runtime.workspaceState.trustClassification}, frozen ${summary.runtime.workspaceState.frozen === null ? '--' : String(summary.runtime.workspaceState.frozen)}`,
31
26
  `- Session GFI: current ${formatNumber(summary.gfi.current)}, peak ${formatNumber(summary.gfi.peak)} (${summary.gfi.dataQuality})`,
32
27
  `- GFI Sources: ${formatSources(summary.gfi.sources)}`,
33
28
  `- Pain Flag: ${summary.pain.activeFlag ? 'active' : 'inactive'}${summary.pain.activeFlagSource ? ` (${summary.pain.activeFlagSource})` : ''}`,
@@ -39,7 +34,7 @@ function buildEnglishOutput(workspaceDir, sessionId, warnings, stats, summary) {
39
34
  `- Legacy Directive File: ${summary.phase3.legacyDirectiveFilePresent ? 'present' : 'missing'} (compatibility-only display artifact)`,
40
35
  `- Note: Legacy directive file is NOT a truth source for Phase 3 eligibility. Queue is the only authoritative execution truth source.`,
41
36
  `- Active Evolution Task: ${summary.evolution.directive.taskPreview ?? '--'}`,
42
- `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'}, queueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'}, trustInputReady ${summary.phase3.trustInputReady ? '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(', ')})` : ''}${summary.phase3.trustRejectedReasons.length > 0 ? `; trust ${summary.phase3.trustRejectedReasons.slice(0, 2).join(', ')}` : ''}`,
37
+ `- 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(', ')})` : ''}`,
43
38
  `- Phase 3 Legacy Directive File: ${summary.phase3.directiveStatus} (${summary.phase3.directiveIgnoredReason})`,
44
39
  '',
45
40
  'Principles',
@@ -68,8 +63,6 @@ function buildChineseOutput(workspaceDir, sessionId, warnings, stats, summary) {
68
63
  '================',
69
64
  '',
70
65
  '控制面',
71
- `- Legacy Trust: ${formatNumber(summary.legacyTrust.score)}/100(阶段 ${formatStage(summary.legacyTrust.stage)},legacy/frozen,${summary.legacyTrust.rewardPolicy})`,
72
- `- Runtime Trust Truth: score ${formatNumber(summary.runtime.currentTrustScore)},classification ${summary.runtime.workspaceState.trustClassification},frozen ${summary.runtime.workspaceState.frozen === null ? '--' : String(summary.runtime.workspaceState.frozen)}`,
73
66
  `- 会话 GFI: 当前 ${formatNumber(summary.gfi.current)},峰值 ${formatNumber(summary.gfi.peak)}(${summary.gfi.dataQuality})`,
74
67
  `- GFI 来源: ${formatSources(summary.gfi.sources)}`,
75
68
  `- Pain Flag: ${summary.pain.activeFlag ? 'active' : 'inactive'}${summary.pain.activeFlagSource ? `(${summary.pain.activeFlagSource})` : ''}`,
@@ -81,7 +74,7 @@ function buildChineseOutput(workspaceDir, sessionId, warnings, stats, summary) {
81
74
  `- Legacy Directive File: ${summary.phase3.legacyDirectiveFilePresent ? 'present' : 'missing'} (兼容仅显示产物)`,
82
75
  `- 注:Legacy directive file 不是 Phase 3 合格性的真实源。队列是唯一权威的执行真实源。`,
83
76
  `- Active Evolution Task: ${summary.evolution.directive.taskPreview ?? '--'}`,
84
- `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'},queueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'},trustInputReady ${summary.phase3.trustInputReady ? '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(', ')})` : ''}${summary.phase3.trustRejectedReasons.length > 0 ? `;trust ${summary.phase3.trustRejectedReasons.slice(0, 2).join(', ')}` : ''}`,
77
+ `- 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(', ')})` : ''}`,
85
78
  `- Phase 3 Legacy Directive File: ${summary.phase3.directiveStatus} (${summary.phase3.directiveIgnoredReason})`,
86
79
  '',
87
80
  '原则统计',
@@ -1,4 +1,5 @@
1
1
  import { WorkspaceContext } from '../core/workspace-context.js';
2
+ import { exportORPOSamples, listExports } from '../core/nocturnal-export.js';
2
3
  function isZh(ctx) {
3
4
  return String(ctx.config?.language || 'en').startsWith('zh');
4
5
  }
@@ -6,16 +7,68 @@ export function handleExportCommand(ctx) {
6
7
  const workspaceDir = ctx.config?.workspaceDir || process.cwd();
7
8
  const zh = isZh(ctx);
8
9
  const args = (ctx.args || '').trim();
9
- const [subcommand = 'corrections'] = args.split(/\s+/).filter(Boolean);
10
+ const parts = args.split(/\s+/).filter(Boolean);
11
+ const [subcommand = 'corrections'] = parts;
10
12
  const wctx = WorkspaceContext.fromHookContext({ workspaceDir, ...ctx.config });
11
- if (subcommand !== 'analytics' && subcommand !== 'corrections') {
12
- return {
13
- text: zh
14
- ? '无效的导出类型。请使用 `analytics` 或 `corrections [--redacted]`。'
15
- : 'Invalid export target. Use `analytics` or `corrections [--redacted]`.',
16
- };
17
- }
18
13
  try {
14
+ if (subcommand === 'orpo') {
15
+ // Nocturnal ORPO export
16
+ // Usage: pd-export orpo [--family=<targetModelFamily>]
17
+ const familyArg = parts.find((p) => p.startsWith('--family='));
18
+ const targetModelFamily = familyArg ? familyArg.split('=')[1] : undefined;
19
+ const result = exportORPOSamples(workspaceDir, targetModelFamily);
20
+ if (!result.success) {
21
+ const reasonMap = {
22
+ no_approved_samples: zh
23
+ ? '没有已批准的样本'
24
+ : 'No approved samples',
25
+ family_mismatch: zh
26
+ ? '没有找到指定模型家族的已批准样本'
27
+ : 'No approved samples for the specified target model family',
28
+ all_samples_missing_artifacts: zh
29
+ ? '所有样本的 artifact 文件都缺失'
30
+ : 'All sample artifact files are missing',
31
+ };
32
+ return {
33
+ text: zh
34
+ ? `ORPO 导出失败: ${reasonMap[result.emptyReason ?? ''] ?? result.error}`
35
+ : `ORPO export failed: ${reasonMap[result.emptyReason ?? ''] ?? result.error}`,
36
+ };
37
+ }
38
+ return {
39
+ text: zh
40
+ ? `已导出 ORPO 决策点样本到 ${result.manifest.exportPath},` +
41
+ `共 ${result.manifest.sampleCount} 条,模型家族: ${result.manifest.targetModelFamily},` +
42
+ `数据集指纹: ${result.manifest.datasetFingerprint.substring(0, 16)}...`
43
+ : `Exported ORPO decision-point samples to ${result.manifest.exportPath}, ` +
44
+ `${result.manifest.sampleCount} samples, target: ${result.manifest.targetModelFamily}, ` +
45
+ `dataset fingerprint: ${result.manifest.datasetFingerprint.substring(0, 16)}...`,
46
+ };
47
+ }
48
+ if (subcommand === 'orpo-list') {
49
+ // List previous ORPO exports
50
+ const exports = listExports(workspaceDir);
51
+ if (exports.length === 0) {
52
+ return {
53
+ text: zh
54
+ ? '没有找到 ORPO 导出记录。'
55
+ : 'No ORPO exports found.',
56
+ };
57
+ }
58
+ const lines = exports.slice(0, 10).map((e) => `- ${e.exportId.substring(0, 8)}... | ${e.sampleCount} samples | ${e.targetModelFamily} | ${new Date(e.createdAt).toLocaleDateString()}`);
59
+ return {
60
+ text: zh
61
+ ? `ORPO 导出记录:\n${lines.join('\n')}`
62
+ : `ORPO exports:\n${lines.join('\n')}`,
63
+ };
64
+ }
65
+ if (subcommand !== 'analytics' && subcommand !== 'corrections') {
66
+ return {
67
+ text: zh
68
+ ? '无效的导出类型。请使用 `analytics`、`corrections [--redacted]` 或 `orpo [--family=<target>]`。'
69
+ : 'Invalid export target. Use `analytics`, `corrections [--redacted]`, or `orpo [--family=<target>]`',
70
+ };
71
+ }
19
72
  if (subcommand === 'analytics') {
20
73
  const result = wctx.trajectory.exportAnalytics();
21
74
  return {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Nocturnal Review Command — Human Review Queue for Nocturnal Dataset
3
+ * =================================================================
4
+ *
5
+ * PURPOSE: Provide human review gateway before training export.
6
+ * Reviews operate on nocturnal dataset records, NOT on artifact files directly.
7
+ *
8
+ * COMMAND SHAPE:
9
+ * pd-nocturnal-review list — list pending review samples
10
+ * pd-nocturnal-review approve <fingerprint> [reason] — approve for training
11
+ * pd-nocturnal-review reject <fingerprint> [reason] — reject sample
12
+ * pd-nocturnal-review show <fingerprint> — show sample lineage summary
13
+ * pd-nocturnal-review set-family <fingerprint> <family> — set target model family
14
+ * pd-nocturnal-review stats — show dataset statistics
15
+ *
16
+ * DESIGN CONSTRAINTS:
17
+ * - Review state lives in dataset registry, not in artifacts
18
+ * - approve/reject require reason (unless default is provided)
19
+ * - Rejected samples cannot enter export
20
+ * - Superseded samples cannot enter export
21
+ * - No Web UI (CLI only)
22
+ */
23
+ import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
24
+ export declare function handleNocturnalReviewCommand(ctx: PluginCommandContext): PluginCommandResult;
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Nocturnal Review Command — Human Review Queue for Nocturnal Dataset
3
+ * =================================================================
4
+ *
5
+ * PURPOSE: Provide human review gateway before training export.
6
+ * Reviews operate on nocturnal dataset records, NOT on artifact files directly.
7
+ *
8
+ * COMMAND SHAPE:
9
+ * pd-nocturnal-review list — list pending review samples
10
+ * pd-nocturnal-review approve <fingerprint> [reason] — approve for training
11
+ * pd-nocturnal-review reject <fingerprint> [reason] — reject sample
12
+ * pd-nocturnal-review show <fingerprint> — show sample lineage summary
13
+ * pd-nocturnal-review set-family <fingerprint> <family> — set target model family
14
+ * pd-nocturnal-review stats — show dataset statistics
15
+ *
16
+ * DESIGN CONSTRAINTS:
17
+ * - Review state lives in dataset registry, not in artifacts
18
+ * - approve/reject require reason (unless default is provided)
19
+ * - Rejected samples cannot enter export
20
+ * - Superseded samples cannot enter export
21
+ * - No Web UI (CLI only)
22
+ */
23
+ import * as fs from 'fs';
24
+ import { listDatasetRecords, getDatasetRecord, updateReviewStatus, updateTargetModelFamily, getDatasetStats, } from '../core/nocturnal-dataset.js';
25
+ function isZh(ctx) {
26
+ return String(ctx.config?.language || 'en').startsWith('zh');
27
+ }
28
+ function formatRecord(record, includeDetails = false) {
29
+ const lines = [];
30
+ lines.push(` sampleFingerprint: ${record.sampleFingerprint.substring(0, 16)}...`);
31
+ lines.push(` artifactId: ${record.artifactId}`);
32
+ lines.push(` sessionId: ${record.sessionId}`);
33
+ lines.push(` principleId: ${record.principleId}`);
34
+ lines.push(` targetModelFamily: ${record.targetModelFamily ?? '(none)'}`);
35
+ lines.push(` reviewStatus: ${record.reviewStatus}`);
36
+ if (record.reviewReason) {
37
+ lines.push(` reviewReason: ${record.reviewReason}`);
38
+ }
39
+ lines.push(` createdAt: ${new Date(record.createdAt).toLocaleString('en-US')}`);
40
+ if (includeDetails) {
41
+ const artifactPath = record.artifactPath;
42
+ if (fs.existsSync(artifactPath)) {
43
+ try {
44
+ const content = fs.readFileSync(artifactPath, 'utf-8');
45
+ const artifact = JSON.parse(content);
46
+ lines.push(` artifact: ${artifactPath}`);
47
+ if (artifact.badDecision) {
48
+ lines.push(` badDecision: ${artifact.badDecision.substring(0, 80)}...`);
49
+ }
50
+ if (artifact.betterDecision) {
51
+ lines.push(` betterDecision: ${artifact.betterDecision.substring(0, 80)}...`);
52
+ }
53
+ if (artifact.rationale) {
54
+ lines.push(` rationale: ${artifact.rationale.substring(0, 80)}...`);
55
+ }
56
+ }
57
+ catch {
58
+ lines.push(` artifact: (could not read)`);
59
+ }
60
+ }
61
+ else {
62
+ lines.push(` artifact: ${artifactPath} (MISSING)`);
63
+ }
64
+ }
65
+ return lines.join('\n');
66
+ }
67
+ function statusLabel(status, zh) {
68
+ const labels = {
69
+ pending_review: { zh: '待审核', en: 'pending_review' },
70
+ approved_for_training: { zh: '已批准', en: 'approved_for_training' },
71
+ rejected: { zh: '已拒绝', en: 'rejected' },
72
+ superseded: { zh: '已替代', en: 'superseded' },
73
+ };
74
+ return labels[status][zh ? 'zh' : 'en'];
75
+ }
76
+ export function handleNocturnalReviewCommand(ctx) {
77
+ const workspaceDir = ctx.config?.workspaceDir || process.cwd();
78
+ const zh = isZh(ctx);
79
+ const args = (ctx.args || '').trim();
80
+ const parts = args.split(/\s+/).filter(Boolean);
81
+ const [action = 'list'] = parts;
82
+ try {
83
+ if (action === 'list') {
84
+ const pending = listDatasetRecords(workspaceDir, { reviewStatus: 'pending_review' });
85
+ const all = listDatasetRecords(workspaceDir);
86
+ if (pending.length === 0) {
87
+ return {
88
+ text: zh
89
+ ? '没有待审核的 nocturnal 样本。'
90
+ : 'No pending nocturnal samples for review.',
91
+ };
92
+ }
93
+ const lines = pending.map((r) => {
94
+ const label = statusLabel(r.reviewStatus, zh);
95
+ return `[${label}] ${r.sampleFingerprint.substring(0, 16)}... | ${r.principleId} | ${r.targetModelFamily ?? '(no family)'}`;
96
+ });
97
+ return {
98
+ text: zh
99
+ ? `待审核样本 (${pending.length}/${all.length}):\n${lines.join('\n')}\n\n使用 /pd-nocturnal-review show <fingerprint> 查看详情。`
100
+ : `Pending review samples (${pending.length}/${all.length}):\n${lines.join('\n')}\n\nUse /pd-nocturnal-review show <fingerprint> for details.`,
101
+ };
102
+ }
103
+ if (action === 'show') {
104
+ const fingerprint = parts[1];
105
+ if (!fingerprint) {
106
+ return {
107
+ text: zh
108
+ ? '用法: /pd-nocturnal-review show <fingerprint>'
109
+ : 'Usage: /pd-nocturnal-review show <fingerprint>',
110
+ };
111
+ }
112
+ const record = getDatasetRecord(workspaceDir, fingerprint);
113
+ if (!record) {
114
+ return {
115
+ text: zh
116
+ ? `未找到样本: ${fingerprint}`
117
+ : `Sample not found: ${fingerprint}`,
118
+ };
119
+ }
120
+ const details = formatRecord(record, true);
121
+ return {
122
+ text: zh
123
+ ? `样本详情:\n${details}`
124
+ : `Sample details:\n${details}`,
125
+ };
126
+ }
127
+ if (action === 'approve') {
128
+ const fingerprint = parts[1];
129
+ const reason = parts.slice(2).join(' ') || (zh ? 'Approved by human reviewer' : 'Approved by human reviewer');
130
+ if (!fingerprint) {
131
+ return {
132
+ text: zh
133
+ ? '用法: /pd-nocturnal-review approve <fingerprint> [reason]'
134
+ : 'Usage: /pd-nocturnal-review approve <fingerprint> [reason]',
135
+ };
136
+ }
137
+ const record = getDatasetRecord(workspaceDir, fingerprint);
138
+ if (!record) {
139
+ return {
140
+ text: zh
141
+ ? `未找到样本: ${fingerprint}`
142
+ : `Sample not found: ${fingerprint}`,
143
+ };
144
+ }
145
+ if (record.reviewStatus === 'approved_for_training') {
146
+ return {
147
+ text: zh
148
+ ? `样本已是 approved_for_training 状态。`
149
+ : `Sample is already approved_for_training.`,
150
+ };
151
+ }
152
+ if (record.reviewStatus === 'rejected') {
153
+ return {
154
+ text: zh
155
+ ? `样本已被 rejected,需要先重置为 pending_review 才能重新审核。`
156
+ : `Sample is rejected. Reset to pending_review first before re-reviewing.`,
157
+ };
158
+ }
159
+ if (record.reviewStatus === 'superseded') {
160
+ return {
161
+ text: zh
162
+ ? `样本已被 superseded,无法更改。`
163
+ : `Sample is superseded and cannot be changed.`,
164
+ };
165
+ }
166
+ const updated = updateReviewStatus(workspaceDir, fingerprint, 'approved_for_training', reason);
167
+ return {
168
+ text: zh
169
+ ? `样本已批准用于训练:\n fingerprint: ${updated.sampleFingerprint.substring(0, 16)}...\n principleId: ${updated.principleId}\n targetModelFamily: ${updated.targetModelFamily ?? '(none)'}\n reason: ${updated.reviewReason}`
170
+ : `Sample approved for training:\n fingerprint: ${updated.sampleFingerprint.substring(0, 16)}...\n principleId: ${updated.principleId}\n targetModelFamily: ${updated.targetModelFamily ?? '(none)'}\n reason: ${updated.reviewReason}`,
171
+ };
172
+ }
173
+ if (action === 'reject') {
174
+ const fingerprint = parts[1];
175
+ const reason = parts.slice(2).join(' ') || (zh ? 'Rejected by human reviewer' : 'Rejected by human reviewer');
176
+ if (!fingerprint) {
177
+ return {
178
+ text: zh
179
+ ? '用法: /pd-nocturnal-review reject <fingerprint> [reason]'
180
+ : 'Usage: /pd-nocturnal-review reject <fingerprint> [reason]',
181
+ };
182
+ }
183
+ const record = getDatasetRecord(workspaceDir, fingerprint);
184
+ if (!record) {
185
+ return {
186
+ text: zh
187
+ ? `未找到样本: ${fingerprint}`
188
+ : `Sample not found: ${fingerprint}`,
189
+ };
190
+ }
191
+ if (record.reviewStatus === 'rejected') {
192
+ return {
193
+ text: zh
194
+ ? `样本已是 rejected 状态。`
195
+ : `Sample is already rejected.`,
196
+ };
197
+ }
198
+ if (record.reviewStatus === 'superseded') {
199
+ return {
200
+ text: zh
201
+ ? `样本已被 superseded,无法更改。`
202
+ : `Sample is superseded and cannot be changed.`,
203
+ };
204
+ }
205
+ const updated = updateReviewStatus(workspaceDir, fingerprint, 'rejected', reason);
206
+ return {
207
+ text: zh
208
+ ? `样本已拒绝:\n fingerprint: ${updated.sampleFingerprint.substring(0, 16)}...\n reason: ${updated.reviewReason}`
209
+ : `Sample rejected:\n fingerprint: ${updated.sampleFingerprint.substring(0, 16)}...\n reason: ${updated.reviewReason}`,
210
+ };
211
+ }
212
+ if (action === 'set-family') {
213
+ const fingerprint = parts[1];
214
+ const family = parts[2];
215
+ if (!fingerprint || !family) {
216
+ return {
217
+ text: zh
218
+ ? '用法: /pd-nocturnal-review set-family <fingerprint> <model-family>'
219
+ : 'Usage: /pd-nocturnal-review set-family <fingerprint> <model-family>',
220
+ };
221
+ }
222
+ const record = getDatasetRecord(workspaceDir, fingerprint);
223
+ if (!record) {
224
+ return {
225
+ text: zh
226
+ ? `未找到样本: ${fingerprint}`
227
+ : `Sample not found: ${fingerprint}`,
228
+ };
229
+ }
230
+ if (record.reviewStatus === 'superseded') {
231
+ return {
232
+ text: zh
233
+ ? `样本已被 superseded,无法修改模型家族。`
234
+ : `Sample is superseded and cannot be modified.`,
235
+ };
236
+ }
237
+ const updated = updateTargetModelFamily(workspaceDir, fingerprint, family);
238
+ return {
239
+ text: zh
240
+ ? `已设置目标模型家族:\n fingerprint: ${updated.sampleFingerprint.substring(0, 16)}...\n targetModelFamily: ${updated.targetModelFamily}`
241
+ : `Target model family set:\n fingerprint: ${updated.sampleFingerprint.substring(0, 16)}...\n targetModelFamily: ${updated.targetModelFamily}`,
242
+ };
243
+ }
244
+ if (action === 'stats') {
245
+ const stats = getDatasetStats(workspaceDir);
246
+ return {
247
+ text: zh
248
+ ? `Nocturnal Dataset 统计:\n 总样本数: ${stats.total}\n 待审核: ${stats.pendingReview}\n 已批准训练: ${stats.approvedForTraining}\n 已拒绝: ${stats.rejected}\n 已替代: ${stats.superseded}\n 可导出 (按家族): ${Object.entries(stats.exportReadyByFamily).map(([f, c]) => `${f}: ${c}`).join(', ') || '无'}`
249
+ : `Nocturnal Dataset Stats:\n Total: ${stats.total}\n Pending review: ${stats.pendingReview}\n Approved for training: ${stats.approvedForTraining}\n Rejected: ${stats.rejected}\n Superseded: ${stats.superseded}\n Export-ready (by family): ${Object.entries(stats.exportReadyByFamily).map(([f, c]) => `${f}: ${c}`).join(', ') || 'none'}`,
250
+ };
251
+ }
252
+ return {
253
+ text: zh
254
+ ? `未知操作: ${action}\n用法:\n /pd-nocturnal-review list\n /pd-nocturnal-review show <fingerprint>\n /pd-nocturnal-review approve <fingerprint> [reason]\n /pd-nocturnal-review reject <fingerprint> [reason]\n /pd-nocturnal-review set-family <fingerprint> <model-family>\n /pd-nocturnal-review stats`
255
+ : `Unknown action: ${action}\nUsage:\n /pd-nocturnal-review list\n /pd-nocturnal-review show <fingerprint>\n /pd-nocturnal-review approve <fingerprint> [reason]\n /pd-nocturnal-review reject <fingerprint> [reason]\n /pd-nocturnal-review set-family <fingerprint> <model-family>\n /pd-nocturnal-review stats`,
256
+ };
257
+ }
258
+ catch (err) {
259
+ return {
260
+ text: zh
261
+ ? `审核操作失败: ${String(err)}`
262
+ : `Review operation failed: ${String(err)}`,
263
+ };
264
+ }
265
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Nocturnal Rollout Command Handler
3
+ * =================================
4
+ *
5
+ * Plugin command handler for nocturnal rollout and promotion operations.
6
+ * Provides commands for:
7
+ * - evaluate-promotion: Evaluate if checkpoint passes promotion gate
8
+ * - advance-promotion: Advance checkpoint promotion state
9
+ * - bind: Bind checkpoint to worker profile
10
+ * - enable-routing: Enable routing for a profile
11
+ * - disable-routing: Disable routing for a profile
12
+ * - rollback: Rollback deployment to previous checkpoint
13
+ * - status: Show deployment status for profiles
14
+ * - show-promotion: Show promotion record for a checkpoint
15
+ *
16
+ * Usage:
17
+ * /nocturnal-rollout evaluate-promotion <checkpointId> [--profile=<profile>]
18
+ * /nocturnal-rollout advance-promotion <checkpointId> [--profile=<profile>] [--review]
19
+ * /nocturnal-rollout bind <checkpointId> --profile=<profile>
20
+ * /nocturnal-rollout enable-routing <profile>
21
+ * /nocturnal-rollout disable-routing <profile>
22
+ * /nocturnal-rollout rollback <profile>
23
+ * /nocturnal-rollout status [--profile=<profile>]
24
+ * /nocturnal-rollout show-promotion <checkpointId>
25
+ */
26
+ import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
27
+ export declare function handleNocturnalRolloutCommand(ctx: PluginCommandContext): PluginCommandResult;