principles-disciple 1.72.0 → 1.74.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 (319) hide show
  1. package/INSTALL.md +1 -3
  2. package/openclaw.plugin.json +10 -5
  3. package/package.json +17 -19
  4. package/scripts/acceptance-test.mjs +16 -73
  5. package/scripts/sync-plugin.mjs +382 -77
  6. package/src/commands/archive-impl.ts +2 -1
  7. package/src/commands/capabilities.ts +2 -2
  8. package/src/commands/context.ts +2 -2
  9. package/src/commands/disable-impl.ts +2 -1
  10. package/src/commands/evolution-status.ts +16 -16
  11. package/src/commands/export.ts +12 -67
  12. package/src/commands/pain.ts +91 -1
  13. package/src/commands/principle-rollback.ts +2 -1
  14. package/src/commands/promote-impl.ts +7 -43
  15. package/src/commands/rollback-impl.ts +2 -1
  16. package/src/commands/rollback.ts +2 -1
  17. package/src/commands/samples.ts +2 -1
  18. package/src/commands/thinking-os.ts +2 -1
  19. package/src/config/errors.ts +18 -2
  20. package/src/constants/diagnostician.ts +2 -2
  21. package/src/constants/tools.ts +2 -1
  22. package/src/core/__tests__/focus-history.test.ts +210 -0
  23. package/src/core/config.ts +1 -1
  24. package/src/core/correction-cue-learner.ts +2 -136
  25. package/src/core/correction-types.ts +16 -88
  26. package/src/core/dictionary.ts +19 -20
  27. package/src/core/empathy-keyword-matcher.ts +17 -289
  28. package/src/core/empathy-types.ts +18 -229
  29. package/src/core/event-log.ts +29 -132
  30. package/src/core/evolution-reducer.ts +21 -2
  31. package/src/core/evolution-types.ts +76 -464
  32. package/src/core/file-store.ts +80 -0
  33. package/src/core/focus-history.ts +228 -955
  34. package/src/core/local-worker-routing.ts +34 -314
  35. package/src/core/merge-gate-audit.ts +0 -195
  36. package/src/core/migration.ts +0 -1
  37. package/src/core/pain-diagnostic-gate.ts +154 -0
  38. package/src/core/pain-signal.ts +21 -138
  39. package/src/core/pain.ts +15 -88
  40. package/src/core/path-resolver.ts +0 -1
  41. package/src/core/paths.ts +0 -1
  42. package/src/core/pd-task-reconciler.ts +26 -115
  43. package/src/core/pd-task-service.ts +9 -9
  44. package/src/core/pd-task-types.ts +23 -127
  45. package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
  46. package/src/core/principle-compiler/code-validator.ts +15 -42
  47. package/src/core/principle-compiler/compiler.ts +100 -15
  48. package/src/core/principle-compiler/index.ts +5 -2
  49. package/src/core/principle-compiler/template-generator.ts +4 -104
  50. package/src/core/principle-injection.ts +10 -202
  51. package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
  52. package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
  53. package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
  54. package/src/core/principle-tree-ledger-adapter.ts +145 -0
  55. package/src/core/principle-tree-ledger.ts +8 -6
  56. package/src/core/reflection/reflection-context.ts +14 -109
  57. package/src/core/replay-engine.ts +8 -500
  58. package/src/core/rule-host-helpers.ts +5 -35
  59. package/src/core/rule-host-types.ts +10 -82
  60. package/src/core/rule-host.ts +6 -63
  61. package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
  62. package/src/core/session-tracker.ts +87 -101
  63. package/src/core/shadow-observation-registry.ts +19 -48
  64. package/src/core/trajectory.ts +3 -1
  65. package/src/core/workflow-funnel-loader.ts +62 -68
  66. package/src/core/workspace-context.ts +46 -0
  67. package/src/core/workspace-dir-service.ts +1 -1
  68. package/src/core/workspace-dir-validation.ts +18 -9
  69. package/src/hooks/AGENTS.md +1 -1
  70. package/src/hooks/gate-block-helper.ts +71 -64
  71. package/src/hooks/gate.ts +183 -31
  72. package/src/hooks/lifecycle.ts +30 -32
  73. package/src/hooks/llm.ts +60 -32
  74. package/src/hooks/pain.ts +297 -103
  75. package/src/hooks/prompt.ts +400 -440
  76. package/src/hooks/subagent.ts +2 -29
  77. package/src/i18n/commands.ts +2 -10
  78. package/src/index.ts +95 -85
  79. package/src/openclaw-sdk.ts +311 -0
  80. package/src/service/central-database.ts +8 -4
  81. package/src/service/evolution-queue-migration.ts +2 -1
  82. package/src/service/evolution-worker.ts +163 -1786
  83. package/src/service/internalization-trigger-adapter.ts +302 -0
  84. package/src/service/keyword-optimization-service.ts +4 -4
  85. package/src/service/monitoring-query-service.ts +1 -215
  86. package/src/service/queue-io.ts +60 -331
  87. package/src/service/runtime-summary-service.ts +59 -16
  88. package/src/service/subagent-workflow/index.ts +0 -41
  89. package/src/service/subagent-workflow/types.ts +9 -120
  90. package/src/service/subagent-workflow/workflow-store.ts +2 -119
  91. package/src/service/workflow-watchdog.ts +0 -43
  92. package/src/types/event-payload.ts +16 -74
  93. package/src/types/event-types.ts +38 -547
  94. package/src/types/hygiene-types.ts +7 -30
  95. package/src/types/principle-tree-schema.ts +20 -222
  96. package/src/types/queue.ts +15 -70
  97. package/src/types/runtime-summary.ts +5 -49
  98. package/src/utils/io.ts +8 -20
  99. package/src/utils/retry.ts +1 -1
  100. package/src/utils/shadow-fingerprint.ts +2 -2
  101. package/src/utils/workspace-resolver.ts +50 -0
  102. package/templates/langs/en/core/AGENTS.md +7 -7
  103. package/templates/langs/en/core/BOOT.md +1 -1
  104. package/templates/langs/en/core/HEARTBEAT.md +2 -2
  105. package/templates/langs/en/principles/THINKING_OS.md +3 -2
  106. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  107. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  108. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  109. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  110. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  111. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  112. package/templates/langs/en/skills/evolve-task/SKILL.md +3 -3
  113. package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
  114. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
  115. package/templates/langs/en/skills/pd-mentor/SKILL.md +2 -3
  116. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
  117. package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
  118. package/templates/langs/zh/core/AGENTS.md +7 -7
  119. package/templates/langs/zh/core/BOOT.md +1 -1
  120. package/templates/langs/zh/core/HEARTBEAT.md +2 -2
  121. package/templates/langs/zh/principles/THINKING_OS.md +3 -2
  122. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  123. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  124. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  125. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
  126. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  127. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  128. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  129. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
  130. package/templates/langs/zh/skills/evolve-task/SKILL.md +4 -4
  131. package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
  132. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
  133. package/templates/langs/zh/skills/pd-mentor/SKILL.md +2 -3
  134. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
  135. package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
  136. package/tests/build-artifacts.test.ts +1 -3
  137. package/tests/commands/evolution-status.test.ts +0 -118
  138. package/tests/core/bootstrap-rules.test.ts +1 -1
  139. package/tests/core/config.test.ts +1 -1
  140. package/tests/core/event-log.test.ts +35 -0
  141. package/tests/core/evolution-engine.test.ts +610 -0
  142. package/tests/core/file-store.test.ts +102 -0
  143. package/tests/core/focus-history.test.ts +203 -11
  144. package/tests/core/merge-gate-audit.test.ts +2 -169
  145. package/tests/core/migration.test.ts +7 -7
  146. package/tests/core/model-deployment-registry.test.ts +7 -1
  147. package/tests/core/model-training-registry.test.ts +19 -0
  148. package/tests/core/observability.test.ts +0 -1
  149. package/tests/core/pain-diagnostic-gate.test.ts +498 -0
  150. package/tests/core/pain.test.ts +0 -1
  151. package/tests/core/path-resolver.test.ts +1 -1
  152. package/tests/core/paths-refactor.test.ts +0 -22
  153. package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
  154. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
  155. package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
  156. package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
  157. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
  158. package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
  159. package/tests/core/reflection-context.test.ts +0 -14
  160. package/tests/core/replay-engine.test.ts +127 -215
  161. package/tests/core/rule-host-helpers.test.ts +2 -2
  162. package/tests/core/rule-implementation-runtime.test.ts +0 -27
  163. package/tests/core/workflow-funnel-loader.test.ts +162 -0
  164. package/tests/core/workspace-context.test.ts +2 -2
  165. package/tests/core/workspace-dir-validation.test.ts +8 -1
  166. package/tests/core-anti-growth.test.ts +191 -0
  167. package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
  168. package/tests/hooks/confirm-first-removal.test.ts +188 -0
  169. package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
  170. package/tests/hooks/gate-auto-correct.test.ts +665 -0
  171. package/tests/hooks/gate-no-path-write-tool.test.ts +172 -0
  172. package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
  173. package/tests/hooks/pain.test.ts +269 -12
  174. package/tests/hooks/prompt-characterization.test.ts +500 -0
  175. package/tests/hooks/prompt-size-guard.test.ts +32 -17
  176. package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
  177. package/tests/index.test.ts +94 -1
  178. package/tests/integration/auto-entry-gate.test.ts +248 -0
  179. package/tests/integration/internalization-trigger-guard.test.ts +69 -0
  180. package/tests/integration/m8-legacy-paths.test.ts +63 -0
  181. package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
  182. package/tests/plugin-config-resolution-cutover.test.ts +359 -0
  183. package/tests/runtime-v2-discovery-guard.test.ts +154 -0
  184. package/tests/service/central-database.test.ts +457 -0
  185. package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
  186. package/tests/service/evolution-worker.timeout.test.ts +11 -129
  187. package/tests/service/internalization-trigger-adapter.test.ts +251 -0
  188. package/tests/service/monitoring-query-service.test.ts +1 -47
  189. package/tests/service/queue-io.test.ts +1 -62
  190. package/tests/service/runtime-summary-service.test.ts +3 -1
  191. package/tests/service/workflow-watchdog.test.ts +0 -91
  192. package/tests/utils/file-lock.test.ts +5 -3
  193. package/tests/utils/session-key.test.ts +52 -0
  194. package/tests/utils/subagent-probe.test.ts +48 -1
  195. package/vitest.config.ts +4 -11
  196. package/.planning/codebase/ARCHITECTURE.md +0 -157
  197. package/.planning/codebase/CONCERNS.md +0 -145
  198. package/.planning/codebase/CONVENTIONS.md +0 -148
  199. package/.planning/codebase/INTEGRATIONS.md +0 -81
  200. package/.planning/codebase/STACK.md +0 -87
  201. package/.planning/codebase/STRUCTURE.md +0 -193
  202. package/.planning/codebase/TESTING.md +0 -243
  203. package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
  204. package/docs/COMMAND_REFERENCE.md +0 -76
  205. package/docs/COMMAND_REFERENCE_EN.md +0 -79
  206. package/scripts/build-web.mjs +0 -46
  207. package/scripts/diagnose-nocturnal.mjs +0 -537
  208. package/scripts/seed-nocturnal-scenarios.mjs +0 -384
  209. package/src/commands/nocturnal-review.ts +0 -322
  210. package/src/commands/nocturnal-rollout.ts +0 -790
  211. package/src/commands/nocturnal-train.ts +0 -986
  212. package/src/commands/pd-reflect.ts +0 -88
  213. package/src/core/adaptive-thresholds.ts +0 -478
  214. package/src/core/diagnostician-task-store.ts +0 -192
  215. package/src/core/nocturnal-arbiter.ts +0 -715
  216. package/src/core/nocturnal-artifact-lineage.ts +0 -116
  217. package/src/core/nocturnal-artificer.ts +0 -257
  218. package/src/core/nocturnal-candidate-scoring.ts +0 -530
  219. package/src/core/nocturnal-compliance.ts +0 -1146
  220. package/src/core/nocturnal-dataset.ts +0 -763
  221. package/src/core/nocturnal-executability.ts +0 -428
  222. package/src/core/nocturnal-export.ts +0 -499
  223. package/src/core/nocturnal-paths.ts +0 -240
  224. package/src/core/nocturnal-reasoning-deriver.ts +0 -343
  225. package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
  226. package/src/core/nocturnal-snapshot-contract.ts +0 -99
  227. package/src/core/nocturnal-trajectory-extractor.ts +0 -512
  228. package/src/core/nocturnal-trinity-types.ts +0 -218
  229. package/src/core/nocturnal-trinity.ts +0 -2680
  230. package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
  231. package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
  232. package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
  233. package/src/http/principles-console-route.ts +0 -709
  234. package/src/service/central-health-service.ts +0 -49
  235. package/src/service/central-overview-service.ts +0 -138
  236. package/src/service/control-ui-query-service.ts +0 -900
  237. package/src/service/cooldown-strategy.ts +0 -97
  238. package/src/service/evolution-pain-context.ts +0 -79
  239. package/src/service/evolution-query-service.ts +0 -407
  240. package/src/service/health-query-service.ts +0 -1038
  241. package/src/service/nocturnal-config.ts +0 -214
  242. package/src/service/nocturnal-runtime.ts +0 -734
  243. package/src/service/nocturnal-service.ts +0 -1605
  244. package/src/service/nocturnal-target-selector.ts +0 -545
  245. package/src/service/sleep-cycle.ts +0 -157
  246. package/src/service/startup-reconciler.ts +0 -112
  247. package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
  248. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
  249. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
  250. package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
  251. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
  252. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
  253. package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
  254. package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
  255. package/src/tools/write-pain-flag.ts +0 -215
  256. package/templates/langs/en/skills/plan-script/SKILL.md +0 -32
  257. package/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
  258. package/tests/commands/nocturnal-review.test.ts +0 -448
  259. package/tests/commands/nocturnal-train.test.ts +0 -97
  260. package/tests/commands/pd-reflect.test.ts +0 -49
  261. package/tests/core/adaptive-thresholds.test.ts +0 -261
  262. package/tests/core/nocturnal-arbiter.test.ts +0 -559
  263. package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
  264. package/tests/core/nocturnal-artificer.test.ts +0 -241
  265. package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
  266. package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
  267. package/tests/core/nocturnal-compliance.test.ts +0 -646
  268. package/tests/core/nocturnal-dataset.test.ts +0 -892
  269. package/tests/core/nocturnal-e2e.test.ts +0 -234
  270. package/tests/core/nocturnal-executability.test.ts +0 -357
  271. package/tests/core/nocturnal-export.test.ts +0 -517
  272. package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
  273. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
  274. package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
  275. package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
  276. package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
  277. package/tests/core/nocturnal-trinity.test.ts +0 -2053
  278. package/tests/core/pain-auto-repair.test.ts +0 -96
  279. package/tests/core/pain-integration.test.ts +0 -510
  280. package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
  281. package/tests/http/principles-console-route.test.ts +0 -162
  282. package/tests/integration/chaos-resilience.test.ts +0 -348
  283. package/tests/integration/empathy-workflow-integration.test.ts +0 -626
  284. package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
  285. package/tests/service/control-ui-query-service.test.ts +0 -121
  286. package/tests/service/cooldown-strategy.test.ts +0 -164
  287. package/tests/service/data-endpoints-regression.test.ts +0 -834
  288. package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
  289. package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
  290. package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
  291. package/tests/service/nocturnal-runtime.test.ts +0 -473
  292. package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
  293. package/tests/service/nocturnal-target-selector.test.ts +0 -615
  294. package/tests/service/startup-reconciler.test.ts +0 -148
  295. package/tests/tools/write-pain-flag.test.ts +0 -358
  296. package/ui/src/App.tsx +0 -45
  297. package/ui/src/api.ts +0 -220
  298. package/ui/src/charts.tsx +0 -955
  299. package/ui/src/components/ErrorState.tsx +0 -6
  300. package/ui/src/components/Loading.tsx +0 -13
  301. package/ui/src/components/ProtectedRoute.tsx +0 -12
  302. package/ui/src/components/Shell.tsx +0 -91
  303. package/ui/src/components/WorkspaceConfig.tsx +0 -178
  304. package/ui/src/components/index.ts +0 -5
  305. package/ui/src/context/auth.tsx +0 -80
  306. package/ui/src/context/theme.tsx +0 -66
  307. package/ui/src/hooks/useAutoRefresh.ts +0 -39
  308. package/ui/src/i18n/ui.ts +0 -473
  309. package/ui/src/main.tsx +0 -16
  310. package/ui/src/pages/EvolutionPage.tsx +0 -333
  311. package/ui/src/pages/FeedbackPage.tsx +0 -138
  312. package/ui/src/pages/GateMonitorPage.tsx +0 -136
  313. package/ui/src/pages/LoginPage.tsx +0 -89
  314. package/ui/src/pages/OverviewPage.tsx +0 -599
  315. package/ui/src/pages/SamplesPage.tsx +0 -174
  316. package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
  317. package/ui/src/styles.css +0 -2020
  318. package/ui/src/types.ts +0 -384
  319. package/ui/src/utils/format.ts +0 -15
@@ -1,790 +0,0 @@
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
-
27
- import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
28
- import { resolvePluginCommandWorkspaceDir } from '../utils/workspace-resolver.js';
29
- import {
30
- evaluatePromotionGate,
31
- advancePromotion,
32
- getPromotionRecord,
33
- listPromotionsByState,
34
- DEFAULT_BASELINE_METRICS,
35
- DEFAULT_MIN_DELTA,
36
- DEFAULT_ALLOWED_MARGIN,
37
- type PromotionState,
38
- } from '../core/promotion-gate.js';
39
- import {
40
- bindCheckpointToWorkerProfile,
41
- enableRoutingForProfile,
42
- disableRoutingForProfile,
43
- rollbackDeployment,
44
- getDeployment,
45
- isRoutingEnabledForProfile,
46
- type WorkerProfile,
47
- } from '../core/model-deployment-registry.js';
48
- import {
49
- classifyTask,
50
- type RoutingInput,
51
- } from '../core/local-worker-routing.js';
52
- import {
53
- completeShadowObservation,
54
- completeShadowObservationByTask,
55
- type ShadowOutcome,
56
- } from '../core/shadow-observation-registry.js';
57
- import {
58
- getCheckpoint,
59
- } from '../core/model-training-registry.js';
60
- import {
61
- runMergeGateAudit,
62
- formatMergeGateAuditReport,
63
- } from '../core/merge-gate-audit.js';
64
- import { resolvePdPath } from '../core/paths.js';
65
-
66
- function isZh(ctx: PluginCommandContext): boolean {
67
- return String(ctx.config?.language || 'en').startsWith('zh');
68
- }
69
-
70
- function parseProfile(arg: string | undefined): WorkerProfile {
71
- if (!arg) return 'local-reader';
72
- if (arg === 'local-reader' || arg === 'local-editor') {
73
- return arg;
74
- }
75
- return 'local-reader';
76
- }
77
-
78
- function formatPromotionState(state: PromotionState, zh: boolean): string {
79
- const map: Record<PromotionState, { en: string; zh: string }> = {
80
- rejected: { en: 'Rejected', zh: '已拒绝' },
81
- candidate_only: { en: 'Candidate Only', zh: '仅候选' },
82
- shadow_ready: { en: 'Shadow Ready', zh: '待 Shadow' },
83
- promotable: { en: 'Promotable', zh: '可晋升' },
84
- };
85
- return map[state][zh ? 'zh' : 'en'];
86
- }
87
-
88
- function formatConstraintCheck(
89
- check: { constraint: string; actual: number; baseline: number; threshold: number; passed: boolean },
90
- zh: boolean
91
- ): string {
92
- const constraintNames: Record<string, string> = {
93
- arbiterRejectRate: zh ? 'Arbiter 拒绝率' : 'Arbiter Reject Rate',
94
- executabilityRejectRate: zh ? '可执行性拒绝率' : 'Executability Reject Rate',
95
- reviewedSubsetQuality: zh ? '质量分数' : 'Quality Score',
96
- };
97
- const name = constraintNames[check.constraint] || check.constraint;
98
- const icon = check.passed ? (zh ? '✅' : 'PASS') : (zh ? '❌' : 'FAIL');
99
- return `${icon} ${name}: ${check.actual.toFixed(4)} (baseline: ${check.baseline.toFixed(4)}, threshold: ${check.threshold.toFixed(4)})`;
100
- }
101
-
102
- export function handleNocturnalRolloutCommand(ctx: PluginCommandContext): PluginCommandResult {
103
- const workspaceDir = resolvePluginCommandWorkspaceDir(ctx, 'nocturnal-rollout');
104
- const zh = isZh(ctx);
105
- const args = (ctx.args || '').trim();
106
- const parts = args.split(/\s+/).filter(Boolean);
107
- const [subcommand = 'help'] = parts;
108
-
109
- // Parse common arguments
110
- const profileArg = parts.find((p) => p.startsWith('--profile='))?.split('=')[1];
111
- const checkpointIdArg = parts.find((p) => p.startsWith('--checkpoint-id='))?.split('=')[1];
112
-
113
- try {
114
- // ── Help ────────────────────────────────────────────────────────────────
115
- if (subcommand === 'help' || subcommand === '--help') {
116
- return {
117
- text: zh
118
- ? ` nocturnal-rollout 命令帮助
119
-
120
- 用法:
121
- /nocturnal-rollout evaluate-promotion <checkpointId> [--profile=<profile>]
122
- /nocturnal-rollout advance-promotion <checkpointId> [--profile=<profile>] [--review]
123
- /nocturnal-rollout bind <checkpointId> --profile=<profile>
124
- /nocturnal-rollout enable-routing <profile>
125
- /nocturnal-rollout disable-routing <profile>
126
- /nocturnal-rollout rollback <profile>
127
- /nocturnal-rollout status [--profile=<profile>]
128
- /nocturnal-rollout show-promotion <checkpointId>
129
-
130
- 说明:
131
- evaluate-promotion - 评估检查点是否通过晋升门
132
- advance-promotion - 推进检查点晋升状态 (需要 --review 标志表示人工审核通过)
133
- bind - 将检查点绑定到工作机配置文件
134
- enable-routing - 启用配置文件路由
135
- disable-routing - 禁用配置文件路由
136
- rollback - 回滚到上一个检查点
137
- status - 显示所有配置文件部署状态
138
- show-promotion - 显示检查点晋升记录
139
-
140
- 配置文件:
141
- local-reader - 本地阅读器 (Phase 7 首发的唯一配置)
142
- local-editor - 本地编辑器 (需要显式启用)
143
-
144
- 阶段状态:
145
- rejected - 检查点不得路由
146
- candidate_only - 检查点有效但尚未准备好 shadow
147
- shadow_ready - 检查点可进入受控 shadow 部署
148
- promotable - 检查点可替换当前活动检查点
149
-
150
- 注意事项:
151
- - 晋升必须经过: 离线 lineage → eval → promotion gate → shadow rollout
152
- - local-reader 是 Phase 7 唯一允许的 rollout 目标
153
- - 路由必须在绑定后显式启用`
154
- : ` nocturnal-rollout command help
155
-
156
- Usage:
157
- /nocturnal-rollout evaluate-promotion <checkpointId> [--profile=<profile>]
158
- /nocturnal-rollout advance-promotion <checkpointId> [--profile=<profile>] [--review]
159
- /nocturnal-rollout bind <checkpointId> --profile=<profile>
160
- /nocturnal-rollout enable-routing <profile>
161
- /nocturnal-rollout disable-routing <profile>
162
- /nocturnal-rollout rollback <profile>
163
- /nocturnal-rollout status [--profile=<profile>]
164
- /nocturnal-rollout show-promotion <checkpointId>
165
-
166
- Description:
167
- evaluate-promotion - Evaluate if checkpoint passes promotion gate
168
- advance-promotion - Advance checkpoint promotion state (requires --review flag for orchestrator review)
169
- bind - Bind checkpoint to worker profile
170
- enable-routing - Enable routing for a profile
171
- disable-routing - Disable routing for a profile
172
- rollback - Rollback to previous checkpoint
173
- status - Show deployment status for all profiles
174
- show-promotion - Show promotion record for a checkpoint
175
-
176
- Profiles:
177
- local-reader - Local reader (only allowed profile for Phase 7)
178
- local-editor - Local editor (requires explicit enablement)
179
-
180
- Promotion States:
181
- rejected - Checkpoint must not be routed
182
- candidate_only - Checkpoint valid but not ready for shadow
183
- shadow_ready - Checkpoint may enter controlled shadow deployment
184
- promotable - Checkpoint may replace current active checkpoint
185
-
186
- Important:
187
- - Promotion requires: offline lineage → eval → promotion gate → shadow rollout
188
- - local-reader is the only allowed rollout target for Phase 7
189
- - Routing must be explicitly enabled after binding`,
190
- };
191
- }
192
-
193
- // ── Evaluate Promotion ────────────────────────────────────────────────
194
- if (subcommand === 'evaluate-promotion') {
195
- const checkpointId = parts[1] || checkpointIdArg;
196
- if (!checkpointId) {
197
- return { text: zh ? '错误: 需要 checkpointId' : 'Error: checkpointId required' };
198
- }
199
-
200
- const profile = parseProfile(profileArg);
201
- const checkpoint = getCheckpoint(workspaceDir, checkpointId);
202
-
203
- if (!checkpoint) {
204
- return { text: zh ? `错误: Checkpoint 未找到: ${checkpointId}` : `Error: Checkpoint not found: ${checkpointId}` };
205
- }
206
-
207
- const result = evaluatePromotionGate(workspaceDir, {
208
- checkpointId,
209
- targetProfile: profile,
210
- baselineMetrics: DEFAULT_BASELINE_METRICS,
211
- minDelta: DEFAULT_MIN_DELTA,
212
- allowedMargin: DEFAULT_ALLOWED_MARGIN,
213
- });
214
-
215
- let text = zh
216
- ? `=== 晋升门评估 ===
217
- Checkpoint: ${checkpointId.substring(0, 8)}...
218
- Profile: ${profile}
219
- 结果: ${result.passes ? (zh ? '通过' : 'PASS') : (zh ? '未通过' : 'FAIL')}
220
- 建议状态: ${result.suggestedState ? formatPromotionState(result.suggestedState, zh) : 'N/A'}
221
-
222
- --- Delta 检查 ---
223
- ${result.deltaCheck.passed ? (zh ? '✅' : 'PASS') : (zh ? '❌' : 'FAIL')} Delta: ${result.deltaCheck.actual >= 0 ? '+' : ''}${result.deltaCheck.actual.toFixed(4)} (阈值: ${result.deltaCheck.threshold.toFixed(4)})
224
-
225
- --- 约束检查 ---`
226
- : `=== Promotion Gate Evaluation ===
227
- Checkpoint: ${checkpointId.substring(0, 8)}...
228
- Profile: ${profile}
229
- Result: ${result.passes ? 'PASS' : 'FAIL'}
230
- Suggested State: ${result.suggestedState ? formatPromotionState(result.suggestedState, zh) : 'N/A'}
231
- Evidence Mode: ${result.evidenceSummary.evidenceMode}
232
- Shadow Samples: ${result.evidenceSummary.shadowSampleCount}
233
-
234
- --- Delta Check ---
235
- ${result.deltaCheck.passed ? 'PASS' : 'FAIL'} Delta: ${result.deltaCheck.actual >= 0 ? '+' : ''}${result.deltaCheck.actual.toFixed(4)} (threshold: ${result.deltaCheck.threshold.toFixed(4)})
236
-
237
- --- Constraint Checks ---`;
238
-
239
- for (const check of result.constraintChecks) {
240
- text += `\n${formatConstraintCheck(check, zh)}`;
241
- }
242
-
243
- if (result.blockers.length > 0) {
244
- text += `\n\n--- Blockers ---`;
245
- for (const blocker of result.blockers) {
246
- text += `\n - ${blocker}`;
247
- }
248
- }
249
-
250
- text += `\n\n--- 下一步 ---
251
- ${result.passes
252
- ? (zh
253
- ? `1. 推进晋升: /nocturnal-rollout advance-promotion ${checkpointId} --profile=${profile} --review
254
- 2. 绑定部署: /nocturnal-rollout bind ${checkpointId} --profile=${profile}`
255
- : `1. Advance promotion: /nocturnal-rollout advance-promotion ${checkpointId} --profile=${profile} --review
256
- 2. Bind deployment: /nocturnal-rollout bind ${checkpointId} --profile=${profile}`)
257
- : (zh
258
- ? `1. 查看 blockers 并修复问题
259
- 2. 重新运行评估前确保满足所有约束`
260
- : `1. Review blockers and fix issues
261
- 2. Ensure all constraints are met before re-evaluation`)}`;
262
-
263
- return { text };
264
- }
265
-
266
- // ── Merge-Gate Audit ──────────────────────────────────────────────────
267
- if (subcommand === 'audit') {
268
- const stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
269
- const report = runMergeGateAudit(workspaceDir, stateDir);
270
- const formatted = formatMergeGateAuditReport(report);
271
- return { text: formatted };
272
- }
273
-
274
- // ── Advance Promotion ─────────────────────────────────────────────────
275
- if (subcommand === 'advance-promotion') {
276
- const checkpointId = parts[1] || checkpointIdArg;
277
- if (!checkpointId) {
278
- return { text: zh ? '错误: 需要 checkpointId' : 'Error: checkpointId required' };
279
- }
280
-
281
- const profile = parseProfile(profileArg);
282
- const hasReview = args.includes('--review');
283
- const noteArg = parts.find((p) => p.startsWith('--note='))?.split('=')[1];
284
- const skipAudit = args.includes('--skip-audit');
285
-
286
- // ── Merge-gate auto-gate: block advance-promotion if audit is BLOCK ──
287
- if (!skipAudit) {
288
- const stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
289
- const auditReport = runMergeGateAudit(workspaceDir, stateDir);
290
- if (auditReport.overallStatus === 'block') {
291
- return {
292
- text: zh
293
- ? `❌ Merge-Gate 审计阻止了晋升:发现 ${auditReport.counts.block} 个阻断项
294
-
295
- ${formatMergeGateAuditReport(auditReport)}
296
-
297
- 如需强制晋升,请添加 --skip-audit 标志。`
298
- : `❌ Merge-Gate audit blocked promotion: ${auditReport.counts.block} blocking issue(s) found
299
-
300
- ${formatMergeGateAuditReport(auditReport)}
301
-
302
- To force promotion, add --skip-audit flag.`,
303
- };
304
- }
305
- }
306
-
307
- try {
308
- const promotion = advancePromotion(workspaceDir, {
309
- checkpointId,
310
- targetProfile: profile,
311
- baselineMetrics: DEFAULT_BASELINE_METRICS,
312
- orchestratorReviewPassed: hasReview,
313
- reviewNote: noteArg,
314
- minDelta: DEFAULT_MIN_DELTA,
315
- allowedMargin: DEFAULT_ALLOWED_MARGIN,
316
- });
317
-
318
- return {
319
- text: zh
320
- ? `✅ 晋升状态已更新
321
- Checkpoint: ${checkpointId.substring(0, 8)}...
322
- 新状态: ${formatPromotionState(promotion.state, zh)}
323
- Review: ${promotion.orchestratorReviewPassed ? (zh ? '通过' : 'Passed') : (zh ? '未审核' : 'Not reviewed')}
324
- Delta: ${promotion.reducedPromptDelta >= 0 ? '+' : ''}${promotion.reducedPromptDelta.toFixed(4)}
325
-
326
- ${promotion.state === 'shadow_ready'
327
- ? (zh ? '⚠️ 进入 shadow 部署。请运行 /nocturnal-rollout bind 完成绑定。' : '⚠️ Ready for shadow deployment. Run /nocturnal-rollout bind to complete binding.')
328
- : promotion.state === 'promotable'
329
- ? (zh ? '✅ 检查点可晋升到生产环境。' : '✅ Checkpoint is promotable to production.')
330
- : promotion.state === 'rejected'
331
- ? (zh ? '❌ 检查点被拒绝。' : '❌ Checkpoint was rejected.')
332
- : (zh ? '等待进一步审核。' : 'Waiting for further review.')}
333
- ${promotion.shadowStartedAt ? `Shadow 开始: ${new Date(promotion.shadowStartedAt).toLocaleString()}` : ''}`
334
- : `✅ Promotion state updated
335
- Checkpoint: ${checkpointId.substring(0, 8)}...
336
- New State: ${formatPromotionState(promotion.state, zh)}
337
- Review: ${promotion.orchestratorReviewPassed ? 'Passed' : 'Not reviewed'}
338
- Delta: ${promotion.reducedPromptDelta >= 0 ? '+' : ''}${promotion.reducedPromptDelta.toFixed(4)}
339
-
340
- ${promotion.state === 'shadow_ready'
341
- ? '⚠️ Ready for shadow deployment. Run /nocturnal-rollout bind to complete binding.'
342
- : promotion.state === 'promotable'
343
- ? '✅ Checkpoint is promotable to production.'
344
- : promotion.state === 'rejected'
345
- ? '❌ Checkpoint was rejected.'
346
- : 'Waiting for further review.'}
347
- ${promotion.shadowStartedAt ? `Shadow started: ${new Date(promotion.shadowStartedAt).toLocaleString()}` : ''}`,
348
- };
349
- } catch (err: unknown) {
350
- return {
351
- text: zh
352
- ? `❌ 晋升失败: ${err instanceof Error ? err.message : String(err)}`
353
- : `❌ Advance promotion failed: ${err instanceof Error ? err.message : String(err)}`,
354
- };
355
- }
356
- }
357
-
358
- // ── Bind ──────────────────────────────────────────────────────────────
359
- if (subcommand === 'bind') {
360
- const checkpointId = parts[1] || checkpointIdArg;
361
- if (!checkpointId) {
362
- return { text: zh ? '错误: 需要 checkpointId' : 'Error: checkpointId required' };
363
- }
364
-
365
- const profile = parseProfile(profileArg);
366
- const noteArg = parts.find((p) => p.startsWith('--note='))?.split('=')[1];
367
-
368
- try {
369
- const deployment = bindCheckpointToWorkerProfile(workspaceDir, profile, checkpointId, noteArg);
370
-
371
- return {
372
- text: zh
373
- ? `✅ 部署已绑定
374
- Profile: ${profile}
375
- Checkpoint: ${checkpointId.substring(0, 8)}...
376
- Artifact: ${deployment.targetModelFamily}
377
- 路由启用: ${deployment.routingEnabled ? (zh ? '是' : 'Yes') : (zh ? '否' : 'No')}
378
-
379
- 下一步:
380
- 1. 启用路由: /nocturnal-rollout enable-routing ${profile}
381
- 2. 或保持禁用进行测试`
382
- : `✅ Deployment bound
383
- Profile: ${profile}
384
- Checkpoint: ${checkpointId.substring(0, 8)}...
385
- Artifact: ${deployment.targetModelFamily}
386
- Routing Enabled: ${deployment.routingEnabled ? 'Yes' : 'No'}
387
-
388
- Next steps:
389
- 1. Enable routing: /nocturnal-rollout enable-routing ${profile}
390
- 2. Or keep disabled for testing`,
391
- };
392
- } catch (err: unknown) {
393
- return {
394
- text: zh
395
- ? `❌ 绑定失败: ${err instanceof Error ? err.message : String(err)}`
396
- : `❌ Bind failed: ${err instanceof Error ? err.message : String(err)}`,
397
- };
398
- }
399
- }
400
-
401
- // ── Enable Routing ────────────────────────────────────────────────────
402
- if (subcommand === 'enable-routing') {
403
- const profile = parseProfile(profileArg || parts[1]);
404
-
405
- try {
406
- const deployment = enableRoutingForProfile(workspaceDir, profile);
407
-
408
- return {
409
- text: zh
410
- ? `✅ 路由已启用
411
- Profile: ${profile}
412
- Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
413
- 路由状态: ${deployment.routingEnabled ? (zh ? '启用' : 'Enabled') : (zh ? '禁用' : 'Disabled')}`
414
- : `✅ Routing enabled
415
- Profile: ${profile}
416
- Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
417
- Routing: ${deployment.routingEnabled ? 'Enabled' : 'Disabled'}`,
418
- };
419
- } catch (err: unknown) {
420
- return {
421
- text: zh
422
- ? `❌ 启用路由失败: ${err instanceof Error ? err.message : String(err)}`
423
- : `❌ Enable routing failed: ${err instanceof Error ? err.message : String(err)}`,
424
- };
425
- }
426
- }
427
-
428
- // ── Disable Routing ────────────────────────────────────────────────────
429
- if (subcommand === 'disable-routing') {
430
- const profile = parseProfile(profileArg || parts[1]);
431
-
432
- try {
433
- const deployment = disableRoutingForProfile(workspaceDir, profile);
434
-
435
- return {
436
- text: zh
437
- ? `✅ 路由已禁用
438
- Profile: ${profile}
439
- Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
440
- 路由状态: ${deployment.routingEnabled ? (zh ? '启用' : 'Enabled') : (zh ? '禁用' : 'Disabled')}
441
-
442
- 注意: 禁用路由后,该配置文件的流量将返回主代理。`
443
- : `✅ Routing disabled
444
- Profile: ${profile}
445
- Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
446
- Routing: ${deployment.routingEnabled ? 'Enabled' : 'Disabled'}
447
-
448
- Note: After disabling routing, traffic for this profile will return to the main agent.`,
449
- };
450
- } catch (err: unknown) {
451
- return {
452
- text: zh
453
- ? `❌ 禁用路由失败: ${err instanceof Error ? err.message : String(err)}`
454
- : `❌ Disable routing failed: ${err instanceof Error ? err.message : String(err)}`,
455
- };
456
- }
457
- }
458
-
459
- // ── Rollback ──────────────────────────────────────────────────────────
460
- if (subcommand === 'rollback') {
461
- const profile = parseProfile(profileArg || parts[1]);
462
- const noteArg = parts.find((p) => p.startsWith('--note='))?.split('=')[1];
463
-
464
- try {
465
- const deployment = rollbackDeployment(workspaceDir, profile, noteArg);
466
-
467
- return {
468
- text: zh
469
- ? `✅ 已回滚
470
- Profile: ${profile}
471
- 新 Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
472
- 上一个 Checkpoint: ${deployment.previousCheckpointId?.substring(0, 8) || 'none'}...
473
- 路由状态: ${deployment.routingEnabled ? (zh ? '启用' : 'Enabled') : (zh ? '禁用' : 'Disabled')}`
474
- : `✅ Rolled back
475
- Profile: ${profile}
476
- New Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
477
- Previous Checkpoint: ${deployment.previousCheckpointId?.substring(0, 8) || 'none'}...
478
- Routing: ${deployment.routingEnabled ? 'Enabled' : 'Disabled'}`,
479
- };
480
- } catch (err: unknown) {
481
- return {
482
- text: zh
483
- ? `❌ 回滚失败: ${err instanceof Error ? err.message : String(err)}`
484
- : `❌ Rollback failed: ${err instanceof Error ? err.message : String(err)}`,
485
- };
486
- }
487
- }
488
-
489
- // ── Status ─────────────────────────────────────────────────────────────
490
- if (subcommand === 'status') {
491
- const profile = profileArg ? parseProfile(profileArg) : undefined;
492
-
493
- if (profile) {
494
- const deployment = getDeployment(workspaceDir, profile);
495
- if (!deployment) {
496
- return {
497
- text: zh
498
- ? `Profile ${profile}: 无部署记录`
499
- : `Profile ${profile}: No deployment record`,
500
- };
501
- }
502
-
503
- const checkpoint = deployment.activeCheckpointId
504
- ? getCheckpoint(workspaceDir, deployment.activeCheckpointId)
505
- : null;
506
-
507
- return {
508
- text: zh
509
- ? `=== ${profile} 部署状态 ===
510
- Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
511
- Deployable: ${checkpoint?.deployable ? (zh ? '是' : 'Yes') : (zh ? '否' : 'No')}
512
- 路由启用: ${deployment.routingEnabled ? (zh ? '是' : 'Yes') : (zh ? '否' : 'No')}
513
- 上次更新: ${new Date(deployment.updatedAt).toLocaleString()}
514
- ${deployment.previousCheckpointId ? `回滚目标: ${deployment.previousCheckpointId.substring(0, 8)}...` : ''}
515
- ${deployment.note ? `备注: ${deployment.note}` : ''}`
516
- : `=== ${profile} Deployment Status ===
517
- Checkpoint: ${deployment.activeCheckpointId?.substring(0, 8) || 'none'}...
518
- Deployable: ${checkpoint?.deployable ? 'Yes' : 'No'}
519
- Routing Enabled: ${deployment.routingEnabled ? 'Yes' : 'No'}
520
- Last Updated: ${new Date(deployment.updatedAt).toLocaleString()}
521
- ${deployment.previousCheckpointId ? `Rollback target: ${deployment.previousCheckpointId.substring(0, 8)}...` : ''}
522
- ${deployment.note ? `Note: ${deployment.note}` : ''}`,
523
- };
524
- }
525
-
526
- // Show all profiles
527
- const profiles: WorkerProfile[] = ['local-reader', 'local-editor'];
528
- let text = zh ? '=== 部署状态 ===\n' : '=== Deployment Status ===\n';
529
-
530
- for (const p of profiles) {
531
- const d = getDeployment(workspaceDir, p);
532
- const routing = isRoutingEnabledForProfile(workspaceDir, p);
533
- text += `\n${p}:
534
- ${d
535
- ? `Checkpoint: ${d.activeCheckpointId?.substring(0, 8) || 'none'}... | Routing: ${routing ? (zh ? '启用' : 'Enabled') : (zh ? '禁用' : 'Disabled')}`
536
- : (zh ? ' 无部署记录' : ' No deployment')}`;
537
- }
538
-
539
- return { text };
540
- }
541
-
542
- // ── Show Promotion ────────────────────────────────────────────────────
543
- if (subcommand === 'show-promotion') {
544
- const checkpointId = parts[1] || checkpointIdArg;
545
- if (!checkpointId) {
546
- return { text: zh ? '错误: 需要 checkpointId' : 'Error: checkpointId required' };
547
- }
548
-
549
- const promotion = getPromotionRecord(workspaceDir, checkpointId);
550
- if (!promotion) {
551
- return {
552
- text: zh
553
- ? `未找到晋升记录: ${checkpointId}`
554
- : `No promotion record found: ${checkpointId}`,
555
- };
556
- }
557
-
558
- return {
559
- text: zh
560
- ? `=== 晋升记录 ===
561
- Checkpoint: ${promotion.checkpointId.substring(0, 8)}...
562
- 状态: ${formatPromotionState(promotion.state, zh)}
563
- Profile: ${promotion.targetProfile}
564
- Family: ${promotion.targetModelFamily}
565
- Delta: ${promotion.reducedPromptDelta >= 0 ? '+' : ''}${promotion.reducedPromptDelta.toFixed(4)}
566
-
567
- 约束指标:
568
- Arbiter 拒绝率: ${promotion.constraintMetrics.arbiterRejectRate.toFixed(4)}
569
- 可执行性拒绝率: ${promotion.constraintMetrics.executabilityRejectRate.toFixed(4)}
570
- 质量分数: ${promotion.constraintMetrics.reviewedSubsetQuality.toFixed(4)}
571
-
572
- 基线:
573
- Arbiter 拒绝率: ${promotion.baselineMetrics.arbiterRejectRate.toFixed(4)}
574
- 可执行性拒绝率: ${promotion.baselineMetrics.executabilityRejectRate.toFixed(4)}
575
- 质量分数: ${promotion.baselineMetrics.reviewedSubsetQuality.toFixed(4)}
576
-
577
- 审核通过: ${promotion.orchestratorReviewPassed ? (zh ? '是' : 'Yes') : (zh ? '否' : 'No')}
578
- ${promotion.reviewNote ? `审核备注: ${promotion.reviewNote}` : ''}
579
- 创建: ${new Date(promotion.createdAt).toLocaleString()}
580
- 状态变更: ${new Date(promotion.stateChangedAt).toLocaleString()}
581
- ${promotion.shadowStartedAt ? `Shadow 开始: ${new Date(promotion.shadowStartedAt).toLocaleString()}` : ''}
582
- ${promotion.promotableAt ? `可晋升时间: ${new Date(promotion.promotableAt).toLocaleString()}` : ''}
583
- ${promotion.previousPromotionId ? `上一个晋升: ${promotion.previousPromotionId.substring(0, 8)}...` : ''}`
584
- : `=== Promotion Record ===
585
- Checkpoint: ${promotion.checkpointId.substring(0, 8)}...
586
- State: ${formatPromotionState(promotion.state, zh)}
587
- Profile: ${promotion.targetProfile}
588
- Family: ${promotion.targetModelFamily}
589
- Delta: ${promotion.reducedPromptDelta >= 0 ? '+' : ''}${promotion.reducedPromptDelta.toFixed(4)}
590
-
591
- Constraint Metrics:
592
- Arbiter Reject Rate: ${promotion.constraintMetrics.arbiterRejectRate.toFixed(4)}
593
- Executability Reject Rate: ${promotion.constraintMetrics.executabilityRejectRate.toFixed(4)}
594
- Quality Score: ${promotion.constraintMetrics.reviewedSubsetQuality.toFixed(4)}
595
-
596
- Baseline:
597
- Arbiter Reject Rate: ${promotion.baselineMetrics.arbiterRejectRate.toFixed(4)}
598
- Executability Reject Rate: ${promotion.baselineMetrics.executabilityRejectRate.toFixed(4)}
599
- Quality Score: ${promotion.baselineMetrics.reviewedSubsetQuality.toFixed(4)}
600
-
601
- Review Passed: ${promotion.orchestratorReviewPassed ? 'Yes' : 'No'}
602
- ${promotion.reviewNote ? `Review Note: ${promotion.reviewNote}` : ''}
603
- Created: ${new Date(promotion.createdAt).toLocaleString()}
604
- State Changed: ${new Date(promotion.stateChangedAt).toLocaleString()}
605
- ${promotion.shadowStartedAt ? `Shadow Started: ${new Date(promotion.shadowStartedAt).toLocaleString()}` : ''}
606
- ${promotion.promotableAt ? `Promotable At: ${new Date(promotion.promotableAt).toLocaleString()}` : ''}
607
- ${promotion.previousPromotionId ? `Previous Promotion: ${promotion.previousPromotionId.substring(0, 8)}...` : ''}`,
608
- };
609
- }
610
-
611
- // ── List by State ─────────────────────────────────────────────────────
612
- if (subcommand === 'list-by-state') {
613
- const [, stateArg] = parts;
614
- if (!stateArg) {
615
- return { text: zh ? '错误: 需要状态参数' : 'Error: state argument required' };
616
- }
617
-
618
- const validStates: PromotionState[] = ['rejected', 'candidate_only', 'shadow_ready', 'promotable'];
619
- if (!validStates.includes(stateArg as PromotionState)) {
620
- return {
621
- text: zh
622
- ? `无效状态: ${stateArg}。有效值: ${validStates.join(', ')}`
623
- : `Invalid state: ${stateArg}. Valid values: ${validStates.join(', ')}`,
624
- };
625
- }
626
-
627
- const promotions = listPromotionsByState(workspaceDir, stateArg as PromotionState);
628
- if (promotions.length === 0) {
629
- return {
630
- text: zh
631
- ? `没有 ${stateArg} 状态的检查点`
632
- : `No checkpoints with state ${stateArg}`,
633
- };
634
- }
635
-
636
- const lines = promotions.map((p) =>
637
- `${p.checkpointId.substring(0, 8)}... | ${p.targetProfile} | ${p.targetModelFamily} | Delta: ${p.reducedPromptDelta >= 0 ? '+' : ''}${p.reducedPromptDelta.toFixed(4)}`
638
- );
639
-
640
- return {
641
- text: zh
642
- ? `${stateArg} 检查点 (${promotions.length}):
643
- ${lines.join('\n')}`
644
- : `${stateArg} checkpoints (${promotions.length}):
645
- ${lines.join('\n')}`,
646
- };
647
- }
648
-
649
- // ── Route ───────────────────────────────────────────────────────────────
650
- // Execute a routing decision for a task. Records shadow observation if routing to shadow.
651
- if (subcommand === 'route') {
652
- // Parse routing input arguments
653
- const intentArg = parts.find((p) => p.startsWith('--intent='))?.slice('--intent='.length) ?? '';
654
- const descriptionArg = parts.find((p) => p.startsWith('--description='))?.slice('--description='.length) ?? '';
655
- const toolsArg = parts.find((p) => p.startsWith('--tools='))?.slice('--tools='.length) ?? '';
656
- const filesArg = parts.find((p) => p.startsWith('--files='))?.slice('--files='.length) ?? '';
657
- const outputArg = parts.find((p) => p.startsWith('--output='))?.slice('--output='.length) ?? '';
658
- // Note: --risk= flag is deprecated (risk gating removed from routing)
659
- const complexityArg = parts.find((p) => p.startsWith('--complexity='))?.slice('--complexity='.length) ?? '';
660
-
661
- const routingInput: RoutingInput = {
662
- taskIntent: intentArg || undefined,
663
- taskDescription: descriptionArg || undefined,
664
- requestedTools: toolsArg ? toolsArg.split(',').map((t) => t.trim()) : undefined,
665
- requestedFiles: filesArg ? filesArg.split(',').map((f) => f.trim()) : undefined,
666
- expectedOutputShape: outputArg || undefined,
667
- complexityHints: complexityArg ? complexityArg.split(',').map((c) => c.trim()) : undefined,
668
- targetProfile: parseProfile(profileArg),
669
- };
670
-
671
- const decision = classifyTask(routingInput, workspaceDir);
672
-
673
- const shadowNote = decision.shadowObservationId
674
- ? `\n\n${zh ? '📝 Shadow 观察已记录' : '📝 Shadow observation recorded'}: ${decision.shadowObservationId}`
675
- : '';
676
-
677
- return {
678
- text: zh
679
- ? `=== 路由决策 ===
680
- 决策: ${decision.decision === 'route_local' ? '路由到本地' : '保持在主代理'}
681
- 目标配置: ${decision.targetProfile ?? 'N/A'}
682
- 分类: ${decision.classification}
683
- 原因: ${decision.reason}
684
- ${decision.blockers.length > 0 ? `阻塞因素:\n${decision.blockers.map((b) => ' - ' + b).join('\n')}` : ''}
685
- ${decision.activeCheckpointId ? `活动 Checkpoint: ${decision.activeCheckpointId.substring(0, 8)}...` : ''}
686
- ${decision.activeCheckpointState ? `Checkpoint 状态: ${decision.activeCheckpointState}` : ''}
687
- ${decision.decision === 'route_local' && decision.activeCheckpointState === 'shadow_ready' ? (zh ? '⚠️ 正在路由到 shadow 检查点' : '⚠️ Routing to shadow checkpoint') : ''}
688
- ${shadowNote}
689
-
690
- 下一步:
691
- ${decision.decision === 'route_local'
692
- ? (zh ? ' 任务将路由到本地配置文件。任务完成后使用 /nocturnal-rollout complete-shadow 完成 shadow 观察。' : ' Task will be routed to local profile. After task completes, use /nocturnal-rollout complete-shadow to complete the shadow observation.')
693
- : (zh ? ' 任务必须保留在主代理。' : ' Task must stay on main agent.')}`
694
- : `=== Routing Decision ===
695
- Decision: ${decision.decision === 'route_local' ? 'ROUTE_LOCAL' : 'STAY_MAIN'}
696
- Target Profile: ${decision.targetProfile ?? 'N/A'}
697
- Classification: ${decision.classification}
698
- Reason: ${decision.reason}
699
- ${decision.blockers.length > 0 ? `Blockers:\n${decision.blockers.map((b) => ' - ' + b).join('\n')}` : ''}
700
- ${decision.activeCheckpointId ? `Active Checkpoint: ${decision.activeCheckpointId.substring(0, 8)}...` : ''}
701
- ${decision.activeCheckpointState ? `Checkpoint State: ${decision.activeCheckpointState}` : ''}
702
- ${decision.decision === 'route_local' && decision.activeCheckpointState === 'shadow_ready' ? '⚠️ Routing to shadow checkpoint' : ''}
703
- ${shadowNote}
704
-
705
- Next steps:
706
- ${decision.decision === 'route_local'
707
- ? ' Task will be routed to local profile. After task completes, use /nocturnal-rollout complete-shadow to complete the shadow observation.'
708
- : ' Task must stay on main agent.'}`,
709
- };
710
- }
711
-
712
- // ── Complete Shadow Observation ─────────────────────────────────────────
713
- if (subcommand === 'complete-shadow') {
714
- const obsIdArg = parts.find((p) => p.startsWith('--observation-id='))?.split('=')[1];
715
- const fingerprintArg = parts.find((p) => p.startsWith('--fingerprint='))?.split('=')[1];
716
- const outcomeArg = parts.find((p) => p.startsWith('--outcome='))?.split('=')[1] ?? 'accepted';
717
- const timedOutArg = parts.includes('--timed-out');
718
- const threwArg = parts.includes('--threw');
719
- const invalidArg = parts.includes('--invalid');
720
- const rejectedArg = parts.includes('--rejected');
721
-
722
- const validOutcomes: ShadowOutcome[] = ['accepted', 'rejected', 'escalated'];
723
- const outcome: ShadowOutcome = validOutcomes.includes(outcomeArg as ShadowOutcome)
724
- ? (outcomeArg as ShadowOutcome)
725
- : 'accepted';
726
-
727
- const failureSignals = {
728
- timedOut: timedOutArg,
729
- threwException: threwArg,
730
- invalidOutput: invalidArg,
731
- profileRejected: rejectedArg,
732
- extra: {},
733
- };
734
-
735
- let observation = null;
736
-
737
- if (obsIdArg) {
738
- observation = completeShadowObservation(workspaceDir, {
739
- observationId: obsIdArg,
740
- outcome,
741
- failureSignals,
742
- });
743
- } else if (fingerprintArg) {
744
- // Look up by task fingerprint (recorded during route)
745
- observation = completeShadowObservationByTask(workspaceDir, fingerprintArg, outcome, failureSignals);
746
- } else {
747
- return {
748
- text: zh
749
- ? '错误: 需要 --observation-id 或 --fingerprint 参数'
750
- : 'Error: --observation-id or --fingerprint is required',
751
- };
752
- }
753
-
754
- if (!observation) {
755
- return {
756
- text: zh
757
- ? `未找到 shadow 观察。检查 ID 或指纹是否正确。`
758
- : `Shadow observation not found. Check the ID or fingerprint.`,
759
- };
760
- }
761
-
762
- return {
763
- text: zh
764
- ? `✅ Shadow 观察已完成
765
- 观察 ID: ${observation.observationId.substring(0, 8)}...
766
- 结果: ${observation.outcome}
767
- Checkpoint: ${observation.checkpointId.substring(0, 8)}...
768
- 完成时间: ${observation.completedAt}`
769
- : `✅ Shadow observation completed
770
- Observation ID: ${observation.observationId.substring(0, 8)}...
771
- Outcome: ${observation.outcome}
772
- Checkpoint: ${observation.checkpointId.substring(0, 8)}...
773
- Completed At: ${observation.completedAt}`,
774
- };
775
- }
776
-
777
- // Unknown subcommand
778
- return {
779
- text: zh
780
- ? `未知子命令: ${subcommand}。运行 /nocturnal-rollout help 查看帮助。`
781
- : `Unknown subcommand: ${subcommand}. Run /nocturnal-rollout help for usage.`,
782
- };
783
- } catch (err: unknown) {
784
- return {
785
- text: zh
786
- ? `❌ 命令失败: ${err instanceof Error ? err.message : String(err)}`
787
- : `❌ Command failed: ${err instanceof Error ? err.message : String(err)}`,
788
- };
789
- }
790
- }