principles-disciple 1.8.1 → 1.8.3

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 (508) hide show
  1. package/ADVANCED_CONFIG_ZH.md +97 -0
  2. package/AGENT_INSTALL.md +173 -0
  3. package/AGENT_INSTALL_EN.md +173 -0
  4. package/INSTALL.md +256 -0
  5. package/SKILL.md +63 -0
  6. package/docs/COMMAND_REFERENCE.md +76 -0
  7. package/docs/COMMAND_REFERENCE_EN.md +79 -0
  8. package/esbuild.config.js +75 -0
  9. package/openclaw.plugin.json +4 -4
  10. package/package.json +11 -13
  11. package/scripts/build-web.mjs +46 -0
  12. package/scripts/install-dependencies.cjs +47 -0
  13. package/scripts/sync-plugin.mjs +802 -0
  14. package/scripts/verify-build.mjs +109 -0
  15. package/src/agents/nocturnal-dreamer.md +152 -0
  16. package/src/agents/nocturnal-philosopher.md +138 -0
  17. package/src/agents/nocturnal-reflector.md +126 -0
  18. package/src/agents/nocturnal-scribe.md +164 -0
  19. package/src/commands/capabilities.ts +85 -0
  20. package/{dist/commands/context.js → src/commands/context.ts} +78 -38
  21. package/src/commands/evolution-status.ts +146 -0
  22. package/src/commands/export.ts +111 -0
  23. package/src/commands/focus.ts +533 -0
  24. package/src/commands/nocturnal-review.ts +311 -0
  25. package/src/commands/nocturnal-rollout.ts +763 -0
  26. package/src/commands/nocturnal-train.ts +1002 -0
  27. package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
  28. package/src/commands/principle-rollback.ts +27 -0
  29. package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
  30. package/src/commands/samples.ts +60 -0
  31. package/src/commands/strategy.ts +38 -0
  32. package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
  33. package/src/commands/workflow-debug.ts +128 -0
  34. package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
  35. package/src/config/errors.ts +163 -0
  36. package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
  37. package/src/constants/diagnostician.ts +66 -0
  38. package/src/constants/tools.ts +62 -0
  39. package/src/core/adaptive-thresholds.ts +476 -0
  40. package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
  41. package/{dist/core/config.js → src/core/config.ts} +158 -46
  42. package/src/core/control-ui-db.ts +435 -0
  43. package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
  44. package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
  45. package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
  46. package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
  47. package/src/core/empathy-keyword-matcher.ts +327 -0
  48. package/src/core/empathy-types.ts +218 -0
  49. package/src/core/event-log.ts +544 -0
  50. package/src/core/evolution-engine.ts +612 -0
  51. package/src/core/evolution-logger.ts +353 -0
  52. package/src/core/evolution-migration.ts +77 -0
  53. package/src/core/evolution-reducer.ts +731 -0
  54. package/src/core/evolution-types.ts +456 -0
  55. package/src/core/external-training-contract.ts +527 -0
  56. package/src/core/focus-history.ts +1458 -0
  57. package/src/core/hygiene/tracker.ts +117 -0
  58. package/{dist/core/init.js → src/core/init.ts} +39 -26
  59. package/src/core/local-worker-routing.ts +617 -0
  60. package/{dist/core/migration.js → src/core/migration.ts} +18 -11
  61. package/src/core/model-deployment-registry.ts +722 -0
  62. package/src/core/model-training-registry.ts +813 -0
  63. package/src/core/nocturnal-arbiter.ts +706 -0
  64. package/src/core/nocturnal-candidate-scoring.ts +392 -0
  65. package/src/core/nocturnal-compliance.ts +1075 -0
  66. package/src/core/nocturnal-dataset.ts +668 -0
  67. package/src/core/nocturnal-executability.ts +428 -0
  68. package/src/core/nocturnal-export.ts +390 -0
  69. package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
  70. package/src/core/nocturnal-trajectory-extractor.ts +484 -0
  71. package/src/core/nocturnal-trinity.ts +1384 -0
  72. package/src/core/pain.ts +122 -0
  73. package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
  74. package/{dist/core/paths.js → src/core/paths.ts} +13 -4
  75. package/src/core/principle-training-state.ts +450 -0
  76. package/src/core/profile.ts +226 -0
  77. package/src/core/promotion-gate.ts +822 -0
  78. package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
  79. package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +175 -62
  80. package/src/core/shadow-observation-registry.ts +534 -0
  81. package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
  82. package/src/core/thinking-models.ts +217 -0
  83. package/src/core/training-program.ts +630 -0
  84. package/src/core/trajectory-types.ts +243 -0
  85. package/src/core/trajectory.ts +1673 -0
  86. package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
  87. package/src/hooks/bash-risk.ts +171 -0
  88. package/src/hooks/edit-verification.ts +295 -0
  89. package/src/hooks/gate-block-helper.ts +160 -0
  90. package/src/hooks/gate.ts +210 -0
  91. package/src/hooks/gfi-gate.ts +177 -0
  92. package/src/hooks/lifecycle.ts +326 -0
  93. package/{dist/hooks/llm.js → src/hooks/llm.ts} +160 -80
  94. package/src/hooks/message-sanitize.ts +45 -0
  95. package/src/hooks/pain.ts +384 -0
  96. package/src/hooks/progressive-trust-gate.ts +174 -0
  97. package/src/hooks/prompt.ts +920 -0
  98. package/src/hooks/subagent.ts +207 -0
  99. package/src/hooks/thinking-checkpoint.ts +73 -0
  100. package/src/hooks/trajectory-collector.ts +290 -0
  101. package/src/http/principles-console-route.ts +716 -0
  102. package/src/i18n/commands.ts +117 -0
  103. package/src/index.ts +694 -0
  104. package/src/service/central-database.ts +831 -0
  105. package/src/service/control-ui-query-service.ts +888 -0
  106. package/src/service/evolution-query-service.ts +405 -0
  107. package/src/service/evolution-worker.ts +1646 -0
  108. package/src/service/health-query-service.ts +836 -0
  109. package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +235 -79
  110. package/src/service/nocturnal-service.ts +1015 -0
  111. package/src/service/nocturnal-target-selector.ts +532 -0
  112. package/src/service/phase3-input-filter.ts +237 -0
  113. package/src/service/runtime-summary-service.ts +757 -0
  114. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
  115. package/{dist/service/subagent-workflow/empathy-observer-workflow-manager.js → src/service/subagent-workflow/empathy-observer-workflow-manager.ts} +240 -117
  116. package/src/service/subagent-workflow/index.ts +51 -0
  117. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
  118. package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
  119. package/{dist/service/subagent-workflow/types.d.ts → src/service/subagent-workflow/types.ts} +137 -18
  120. package/src/service/subagent-workflow/workflow-store.ts +328 -0
  121. package/src/service/trajectory-service.ts +15 -0
  122. package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
  123. package/src/tools/deep-reflect.ts +349 -0
  124. package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
  125. package/src/types/event-types.ts +453 -0
  126. package/src/types/hygiene-types.ts +31 -0
  127. package/src/types/principle-tree-schema.ts +244 -0
  128. package/src/types/runtime-summary.ts +49 -0
  129. package/src/types.ts +74 -0
  130. package/src/utils/file-lock.ts +391 -0
  131. package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
  132. package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
  133. package/src/utils/io.ts +110 -0
  134. package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
  135. package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
  136. package/src/utils/subagent-probe.ts +94 -0
  137. package/templates/langs/en/skills/ai-sprint-orchestration/EXAMPLES.md +63 -0
  138. package/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +136 -0
  139. package/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +67 -0
  140. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +214 -0
  141. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +107 -0
  142. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +107 -0
  143. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +105 -0
  144. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +108 -0
  145. package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +58 -0
  146. package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +190 -0
  147. package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -0
  148. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +310 -0
  149. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +683 -0
  150. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +604 -0
  151. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +32 -0
  152. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +707 -0
  153. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +3419 -0
  154. package/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +63 -0
  155. package/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +136 -0
  156. package/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +67 -0
  157. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +214 -0
  158. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +107 -0
  159. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +107 -0
  160. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +105 -0
  161. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +108 -0
  162. package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +58 -0
  163. package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +190 -0
  164. package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -0
  165. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +310 -0
  166. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +683 -0
  167. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +604 -0
  168. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +32 -0
  169. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +707 -0
  170. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +3419 -0
  171. package/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +230 -0
  172. package/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +672 -0
  173. package/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +1321 -0
  174. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +1419 -0
  175. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
  176. package/templates/pain_settings.json +2 -1
  177. package/tests/README.md +120 -0
  178. package/tests/build-artifacts.test.ts +111 -0
  179. package/tests/commands/evolution-status.test.ts +222 -0
  180. package/tests/commands/evolver.test.ts +22 -0
  181. package/tests/commands/export.test.ts +78 -0
  182. package/tests/commands/nocturnal-review.test.ts +448 -0
  183. package/tests/commands/nocturnal-train.test.ts +97 -0
  184. package/tests/commands/pain.test.ts +108 -0
  185. package/tests/commands/samples.test.ts +65 -0
  186. package/tests/commands/strategy.test.ts +34 -0
  187. package/tests/commands/thinking-os.test.ts +88 -0
  188. package/tests/core/adaptive-thresholds.test.ts +261 -0
  189. package/tests/core/config-service.test.ts +89 -0
  190. package/tests/core/config.test.ts +90 -0
  191. package/tests/core/control-ui-db.test.ts +75 -0
  192. package/tests/core/core-template-guidance.test.ts +21 -0
  193. package/tests/core/detection-funnel.test.ts +63 -0
  194. package/tests/core/detection-service.test.ts +50 -0
  195. package/tests/core/dictionary-service.test.ts +116 -0
  196. package/tests/core/dictionary.test.ts +168 -0
  197. package/tests/core/empathy-keyword-matcher.test.ts +209 -0
  198. package/tests/core/event-log.test.ts +181 -0
  199. package/tests/core/evolution-e2e.test.ts +58 -0
  200. package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
  201. package/tests/core/evolution-engine.test.ts +562 -0
  202. package/tests/core/evolution-logger.test.ts +148 -0
  203. package/tests/core/evolution-migration.test.ts +50 -0
  204. package/tests/core/evolution-paths.test.ts +21 -0
  205. package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
  206. package/tests/core/evolution-reducer.test.ts +180 -0
  207. package/tests/core/evolution-types-loop.test.ts +48 -0
  208. package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
  209. package/tests/core/external-training-contract.test.ts +463 -0
  210. package/tests/core/focus-history.test.ts +682 -0
  211. package/tests/core/init-flatten.test.ts +69 -0
  212. package/tests/core/init-refactor.test.ts +87 -0
  213. package/tests/core/init-v1.3.test.ts +46 -0
  214. package/tests/core/init.test.ts +190 -0
  215. package/tests/core/local-worker-routing.test.ts +757 -0
  216. package/tests/core/migration.test.ts +84 -0
  217. package/tests/core/model-deployment-registry.test.ts +845 -0
  218. package/tests/core/model-training-registry.test.ts +889 -0
  219. package/tests/core/nocturnal-arbiter.test.ts +494 -0
  220. package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
  221. package/tests/core/nocturnal-compliance.test.ts +646 -0
  222. package/tests/core/nocturnal-dataset.test.ts +892 -0
  223. package/tests/core/nocturnal-executability.test.ts +357 -0
  224. package/tests/core/nocturnal-export.test.ts +462 -0
  225. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
  226. package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
  227. package/tests/core/nocturnal-trinity.test.ts +953 -0
  228. package/tests/core/pain.test.ts +33 -0
  229. package/tests/core/path-resolver.test.ts +57 -0
  230. package/tests/core/paths-refactor.test.ts +42 -0
  231. package/tests/core/phase7-rollout-integration.test.ts +477 -0
  232. package/tests/core/principle-training-state.test.ts +712 -0
  233. package/tests/core/profile.test.ts +56 -0
  234. package/tests/core/promotion-gate.test.ts +556 -0
  235. package/tests/core/risk-calculator.test.ts +168 -0
  236. package/tests/core/session-tracker.test.ts +191 -0
  237. package/tests/core/training-program.test.ts +472 -0
  238. package/tests/core/trajectory.test.ts +265 -0
  239. package/tests/core/workspace-context-factory.test.ts +18 -0
  240. package/tests/core/workspace-context.test.ts +134 -0
  241. package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
  242. package/tests/fixtures/production-compatibility.test.ts +147 -0
  243. package/tests/fixtures/production-mock-generator.ts +282 -0
  244. package/tests/hooks/bash-risk-integration.test.ts +137 -0
  245. package/tests/hooks/bash-risk.test.ts +81 -0
  246. package/tests/hooks/edit-verification.test.ts +678 -0
  247. package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
  248. package/tests/hooks/gate-edit-verification.test.ts +435 -0
  249. package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
  250. package/tests/hooks/gate.test.ts +271 -0
  251. package/tests/hooks/gfi-gate-unit.test.ts +422 -0
  252. package/tests/hooks/gfi-gate.test.ts +669 -0
  253. package/tests/hooks/lifecycle.test.ts +248 -0
  254. package/tests/hooks/llm.test.ts +308 -0
  255. package/tests/hooks/message-sanitize.test.ts +36 -0
  256. package/tests/hooks/pain.test.ts +141 -0
  257. package/tests/hooks/progressive-trust-gate.test.ts +277 -0
  258. package/tests/hooks/prompt.test.ts +1411 -0
  259. package/tests/hooks/subagent.test.ts +467 -0
  260. package/tests/hooks/thinking-gate.test.ts +313 -0
  261. package/tests/http/principles-console-route.test.ts +140 -0
  262. package/tests/hygiene-tracker.test.ts +77 -0
  263. package/tests/index.integration.test.ts +179 -0
  264. package/tests/index.shadow-routing.integration.test.ts +140 -0
  265. package/tests/index.test.ts +9 -0
  266. package/tests/integration/empathy-workflow-integration.test.ts +627 -0
  267. package/tests/service/control-ui-query-service.test.ts +121 -0
  268. package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
  269. package/tests/service/evolution-worker.test.ts +585 -0
  270. package/tests/service/nocturnal-runtime.test.ts +470 -0
  271. package/tests/service/nocturnal-service.test.ts +577 -0
  272. package/tests/service/nocturnal-target-selector.test.ts +615 -0
  273. package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
  274. package/tests/service/phase3-input-filter.test.ts +289 -0
  275. package/tests/service/runtime-summary-service.test.ts +919 -0
  276. package/tests/task-compliance.test.ts +166 -0
  277. package/tests/test-utils.ts +48 -0
  278. package/tests/tools/critique-prompt.test.ts +260 -0
  279. package/tests/tools/deep-reflect.test.ts +232 -0
  280. package/tests/tools/model-index.test.ts +246 -0
  281. package/tests/ui/app.test.tsx +114 -0
  282. package/tests/utils/file-lock.test.ts +407 -0
  283. package/tests/utils/hashing.test.ts +32 -0
  284. package/tests/utils/io.test.ts +39 -0
  285. package/tests/utils/nlp.test.ts +53 -0
  286. package/tests/utils/plugin-logger.test.ts +156 -0
  287. package/tsconfig.json +16 -0
  288. package/tsconfig.tsbuildinfo +1 -0
  289. package/ui/src/App.tsx +45 -0
  290. package/ui/src/api.ts +216 -0
  291. package/ui/src/charts.tsx +586 -0
  292. package/ui/src/components/ErrorState.tsx +6 -0
  293. package/ui/src/components/Loading.tsx +13 -0
  294. package/ui/src/components/ProtectedRoute.tsx +12 -0
  295. package/ui/src/components/Shell.tsx +91 -0
  296. package/ui/src/components/WorkspaceConfig.tsx +146 -0
  297. package/ui/src/components/index.ts +5 -0
  298. package/ui/src/context/auth.tsx +80 -0
  299. package/ui/src/context/theme.tsx +66 -0
  300. package/ui/src/hooks/useAutoRefresh.ts +39 -0
  301. package/ui/src/i18n/ui.ts +363 -0
  302. package/ui/src/main.tsx +16 -0
  303. package/ui/src/pages/EvolutionPage.tsx +352 -0
  304. package/ui/src/pages/FeedbackPage.tsx +140 -0
  305. package/ui/src/pages/GateMonitorPage.tsx +136 -0
  306. package/ui/src/pages/LoginPage.tsx +88 -0
  307. package/ui/src/pages/OverviewPage.tsx +238 -0
  308. package/ui/src/pages/SamplesPage.tsx +174 -0
  309. package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
  310. package/ui/src/styles.css +1661 -0
  311. package/ui/src/types.ts +368 -0
  312. package/ui/src/utils/format.ts +15 -0
  313. package/vitest.config.ts +23 -0
  314. package/dist/commands/capabilities.d.ts +0 -3
  315. package/dist/commands/capabilities.js +0 -73
  316. package/dist/commands/context.d.ts +0 -5
  317. package/dist/commands/evolution-status.d.ts +0 -4
  318. package/dist/commands/evolution-status.js +0 -117
  319. package/dist/commands/evolver.d.ts +0 -9
  320. package/dist/commands/evolver.js +0 -26
  321. package/dist/commands/export.d.ts +0 -2
  322. package/dist/commands/export.js +0 -98
  323. package/dist/commands/focus.d.ts +0 -14
  324. package/dist/commands/focus.js +0 -457
  325. package/dist/commands/nocturnal-review.d.ts +0 -24
  326. package/dist/commands/nocturnal-review.js +0 -265
  327. package/dist/commands/nocturnal-rollout.d.ts +0 -27
  328. package/dist/commands/nocturnal-rollout.js +0 -671
  329. package/dist/commands/nocturnal-train.d.ts +0 -25
  330. package/dist/commands/nocturnal-train.js +0 -919
  331. package/dist/commands/pain.d.ts +0 -5
  332. package/dist/commands/principle-rollback.d.ts +0 -4
  333. package/dist/commands/principle-rollback.js +0 -22
  334. package/dist/commands/rollback.d.ts +0 -19
  335. package/dist/commands/samples.d.ts +0 -2
  336. package/dist/commands/samples.js +0 -55
  337. package/dist/commands/strategy.d.ts +0 -3
  338. package/dist/commands/strategy.js +0 -29
  339. package/dist/commands/thinking-os.d.ts +0 -2
  340. package/dist/config/defaults/runtime.d.ts +0 -40
  341. package/dist/config/errors.d.ts +0 -84
  342. package/dist/config/errors.js +0 -94
  343. package/dist/config/index.js +0 -7
  344. package/dist/constants/diagnostician.d.ts +0 -12
  345. package/dist/constants/diagnostician.js +0 -56
  346. package/dist/constants/tools.d.ts +0 -17
  347. package/dist/constants/tools.js +0 -54
  348. package/dist/core/adaptive-thresholds.d.ts +0 -186
  349. package/dist/core/adaptive-thresholds.js +0 -300
  350. package/dist/core/config-service.d.ts +0 -15
  351. package/dist/core/config.d.ts +0 -129
  352. package/dist/core/control-ui-db.d.ts +0 -95
  353. package/dist/core/control-ui-db.js +0 -292
  354. package/dist/core/detection-funnel.d.ts +0 -33
  355. package/dist/core/detection-service.d.ts +0 -15
  356. package/dist/core/dictionary-service.d.ts +0 -15
  357. package/dist/core/dictionary.d.ts +0 -38
  358. package/dist/core/event-log.d.ts +0 -82
  359. package/dist/core/event-log.js +0 -463
  360. package/dist/core/evolution-engine.d.ts +0 -118
  361. package/dist/core/evolution-engine.js +0 -464
  362. package/dist/core/evolution-logger.d.ts +0 -137
  363. package/dist/core/evolution-logger.js +0 -256
  364. package/dist/core/evolution-migration.d.ts +0 -5
  365. package/dist/core/evolution-migration.js +0 -65
  366. package/dist/core/evolution-reducer.d.ts +0 -98
  367. package/dist/core/evolution-reducer.js +0 -465
  368. package/dist/core/evolution-types.d.ts +0 -287
  369. package/dist/core/evolution-types.js +0 -78
  370. package/dist/core/external-training-contract.d.ts +0 -276
  371. package/dist/core/external-training-contract.js +0 -269
  372. package/dist/core/focus-history.d.ts +0 -210
  373. package/dist/core/focus-history.js +0 -1185
  374. package/dist/core/hygiene/tracker.d.ts +0 -22
  375. package/dist/core/hygiene/tracker.js +0 -106
  376. package/dist/core/init.d.ts +0 -12
  377. package/dist/core/local-worker-routing.d.ts +0 -175
  378. package/dist/core/local-worker-routing.js +0 -525
  379. package/dist/core/migration.d.ts +0 -6
  380. package/dist/core/model-deployment-registry.d.ts +0 -218
  381. package/dist/core/model-deployment-registry.js +0 -503
  382. package/dist/core/model-training-registry.d.ts +0 -295
  383. package/dist/core/model-training-registry.js +0 -475
  384. package/dist/core/nocturnal-arbiter.d.ts +0 -159
  385. package/dist/core/nocturnal-arbiter.js +0 -534
  386. package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
  387. package/dist/core/nocturnal-candidate-scoring.js +0 -266
  388. package/dist/core/nocturnal-compliance.d.ts +0 -175
  389. package/dist/core/nocturnal-compliance.js +0 -824
  390. package/dist/core/nocturnal-dataset.d.ts +0 -224
  391. package/dist/core/nocturnal-dataset.js +0 -443
  392. package/dist/core/nocturnal-executability.d.ts +0 -85
  393. package/dist/core/nocturnal-executability.js +0 -331
  394. package/dist/core/nocturnal-export.d.ts +0 -124
  395. package/dist/core/nocturnal-export.js +0 -275
  396. package/dist/core/nocturnal-paths.d.ts +0 -124
  397. package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
  398. package/dist/core/nocturnal-trajectory-extractor.js +0 -307
  399. package/dist/core/nocturnal-trinity.d.ts +0 -311
  400. package/dist/core/nocturnal-trinity.js +0 -880
  401. package/dist/core/pain.d.ts +0 -4
  402. package/dist/core/pain.js +0 -70
  403. package/dist/core/path-resolver.d.ts +0 -46
  404. package/dist/core/paths.d.ts +0 -65
  405. package/dist/core/principle-training-state.d.ts +0 -121
  406. package/dist/core/principle-training-state.js +0 -321
  407. package/dist/core/profile.d.ts +0 -62
  408. package/dist/core/profile.js +0 -210
  409. package/dist/core/promotion-gate.d.ts +0 -238
  410. package/dist/core/promotion-gate.js +0 -529
  411. package/dist/core/risk-calculator.d.ts +0 -22
  412. package/dist/core/session-tracker.d.ts +0 -101
  413. package/dist/core/shadow-observation-registry.d.ts +0 -217
  414. package/dist/core/shadow-observation-registry.js +0 -308
  415. package/dist/core/system-logger.d.ts +0 -8
  416. package/dist/core/thinking-models.d.ts +0 -38
  417. package/dist/core/thinking-models.js +0 -170
  418. package/dist/core/training-program.d.ts +0 -233
  419. package/dist/core/training-program.js +0 -433
  420. package/dist/core/trajectory.d.ts +0 -411
  421. package/dist/core/trajectory.js +0 -1307
  422. package/dist/core/workspace-context.d.ts +0 -71
  423. package/dist/hooks/bash-risk.d.ts +0 -57
  424. package/dist/hooks/bash-risk.js +0 -137
  425. package/dist/hooks/edit-verification.d.ts +0 -62
  426. package/dist/hooks/edit-verification.js +0 -256
  427. package/dist/hooks/gate-block-helper.d.ts +0 -44
  428. package/dist/hooks/gate-block-helper.js +0 -119
  429. package/dist/hooks/gate.d.ts +0 -24
  430. package/dist/hooks/gate.js +0 -173
  431. package/dist/hooks/gfi-gate.d.ts +0 -40
  432. package/dist/hooks/gfi-gate.js +0 -113
  433. package/dist/hooks/lifecycle.d.ts +0 -5
  434. package/dist/hooks/lifecycle.js +0 -284
  435. package/dist/hooks/llm.d.ts +0 -13
  436. package/dist/hooks/message-sanitize.d.ts +0 -3
  437. package/dist/hooks/message-sanitize.js +0 -37
  438. package/dist/hooks/pain.d.ts +0 -5
  439. package/dist/hooks/pain.js +0 -301
  440. package/dist/hooks/progressive-trust-gate.d.ts +0 -52
  441. package/dist/hooks/progressive-trust-gate.js +0 -134
  442. package/dist/hooks/prompt.d.ts +0 -49
  443. package/dist/hooks/prompt.js +0 -905
  444. package/dist/hooks/subagent.d.ts +0 -10
  445. package/dist/hooks/subagent.js +0 -387
  446. package/dist/hooks/thinking-checkpoint.d.ts +0 -37
  447. package/dist/hooks/thinking-checkpoint.js +0 -51
  448. package/dist/hooks/trajectory-collector.d.ts +0 -32
  449. package/dist/hooks/trajectory-collector.js +0 -256
  450. package/dist/http/principles-console-route.d.ts +0 -9
  451. package/dist/http/principles-console-route.js +0 -681
  452. package/dist/i18n/commands.d.ts +0 -26
  453. package/dist/i18n/commands.js +0 -116
  454. package/dist/index.d.ts +0 -7
  455. package/dist/index.js +0 -581
  456. package/dist/service/central-database.d.ts +0 -104
  457. package/dist/service/central-database.js +0 -649
  458. package/dist/service/control-ui-query-service.d.ts +0 -221
  459. package/dist/service/control-ui-query-service.js +0 -543
  460. package/dist/service/empathy-observer-manager.d.ts +0 -88
  461. package/dist/service/empathy-observer-manager.js +0 -414
  462. package/dist/service/evolution-query-service.d.ts +0 -155
  463. package/dist/service/evolution-query-service.js +0 -258
  464. package/dist/service/evolution-worker.d.ts +0 -101
  465. package/dist/service/evolution-worker.js +0 -975
  466. package/dist/service/health-query-service.d.ts +0 -170
  467. package/dist/service/health-query-service.js +0 -662
  468. package/dist/service/nocturnal-runtime.d.ts +0 -183
  469. package/dist/service/nocturnal-service.d.ts +0 -163
  470. package/dist/service/nocturnal-service.js +0 -787
  471. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  472. package/dist/service/nocturnal-target-selector.js +0 -315
  473. package/dist/service/phase3-input-filter.d.ts +0 -73
  474. package/dist/service/phase3-input-filter.js +0 -172
  475. package/dist/service/runtime-summary-service.d.ts +0 -122
  476. package/dist/service/runtime-summary-service.js +0 -485
  477. package/dist/service/subagent-workflow/empathy-observer-workflow-manager.d.ts +0 -48
  478. package/dist/service/subagent-workflow/index.d.ts +0 -4
  479. package/dist/service/subagent-workflow/index.js +0 -3
  480. package/dist/service/subagent-workflow/runtime-direct-driver.d.ts +0 -77
  481. package/dist/service/subagent-workflow/runtime-direct-driver.js +0 -75
  482. package/dist/service/subagent-workflow/types.js +0 -11
  483. package/dist/service/subagent-workflow/workflow-store.d.ts +0 -26
  484. package/dist/service/subagent-workflow/workflow-store.js +0 -165
  485. package/dist/service/trajectory-service.d.ts +0 -2
  486. package/dist/service/trajectory-service.js +0 -15
  487. package/dist/tools/critique-prompt.d.ts +0 -14
  488. package/dist/tools/deep-reflect.d.ts +0 -39
  489. package/dist/tools/deep-reflect.js +0 -350
  490. package/dist/tools/model-index.d.ts +0 -9
  491. package/dist/types/event-types.d.ts +0 -306
  492. package/dist/types/event-types.js +0 -106
  493. package/dist/types/hygiene-types.d.ts +0 -20
  494. package/dist/types/hygiene-types.js +0 -12
  495. package/dist/types/runtime-summary.d.ts +0 -47
  496. package/dist/types/runtime-summary.js +0 -1
  497. package/dist/types.d.ts +0 -50
  498. package/dist/types.js +0 -22
  499. package/dist/utils/file-lock.d.ts +0 -71
  500. package/dist/utils/file-lock.js +0 -309
  501. package/dist/utils/glob-match.d.ts +0 -28
  502. package/dist/utils/hashing.d.ts +0 -9
  503. package/dist/utils/io.d.ts +0 -6
  504. package/dist/utils/io.js +0 -106
  505. package/dist/utils/nlp.d.ts +0 -9
  506. package/dist/utils/plugin-logger.d.ts +0 -39
  507. package/dist/utils/subagent-probe.d.ts +0 -34
  508. package/dist/utils/subagent-probe.js +0 -81
@@ -0,0 +1,585 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import {
3
+ EvolutionWorkerService,
4
+ createEvolutionTaskId,
5
+ createPainCandidateFingerprint,
6
+ extractEvolutionTaskId,
7
+ hasRecentDuplicateTask,
8
+ hasEquivalentPromotedRule,
9
+ processPromotion,
10
+ registerEvolutionTaskSession,
11
+ shouldTrackPainCandidate,
12
+ trackPainCandidate,
13
+ } from '../../src/service/evolution-worker.js';
14
+ import { DictionaryService } from '../../src/core/dictionary-service.js';
15
+ import * as sessionTracker from '../../src/core/session-tracker.js';
16
+ import * as eventLog from '../../src/core/event-log.js';
17
+ import * as fs from 'fs';
18
+ import * as os from 'os';
19
+ import * as path from 'path';
20
+ import { evaluatePhase3Inputs } from '../../src/service/phase3-input-filter.js';
21
+ import { safeRmDir } from '../test-utils.js';
22
+
23
+ vi.mock('../../src/core/dictionary-service');
24
+ vi.mock('../../src/core/session-tracker', () => ({
25
+ initPersistence: vi.fn(),
26
+ flushAllSessions: vi.fn(),
27
+ listSessions: vi.fn(() => []), // Returns empty sessions for idle detection
28
+ }));
29
+ vi.mock('../../src/core/event-log', () => ({
30
+ EventLogService: {
31
+ get: vi.fn(() => ({
32
+ recordEvolutionTask: vi.fn(),
33
+ recordRulePromotion: vi.fn(),
34
+ flush: vi.fn(),
35
+ })),
36
+ },
37
+ }));
38
+
39
+ describe('EvolutionWorkerService', () => {
40
+ beforeEach(() => {
41
+ vi.useFakeTimers();
42
+ });
43
+
44
+ afterEach(() => {
45
+ vi.useRealTimers();
46
+ vi.clearAllMocks();
47
+ });
48
+
49
+
50
+ it('should detect recent duplicate tasks by source and preview', () => {
51
+ const now = new Date('2026-03-18T00:30:00.000Z').getTime();
52
+ const queue = [
53
+ {
54
+ id: 'a1',
55
+ score: 50,
56
+ source: 'llm_p_frustration_023',
57
+ reason: 'pain',
58
+ trigger_text_preview: '[EVOLUTION_ACK] 有失败记录',
59
+ timestamp: '2026-03-18T00:10:00.000Z',
60
+ status: 'pending',
61
+ },
62
+ ];
63
+
64
+ expect(hasRecentDuplicateTask(queue as any, 'llm_p_frustration_023', '[EVOLUTION_ACK] 有失败记录', now, 'pain')).toBe(true);
65
+ expect(hasRecentDuplicateTask(queue as any, 'llm_p_frustration_023', 'different preview', now, 'pain')).toBe(false);
66
+ // Different reason should not be considered duplicate
67
+ expect(hasRecentDuplicateTask(queue as any, 'llm_p_frustration_023', '[EVOLUTION_ACK] 有失败记录', now, 'different_reason')).toBe(false);
68
+ });
69
+
70
+ it('should skip promoting duplicate exact-match rules', () => {
71
+ const dictionary = {
72
+ getAllRules: () => ({
73
+ EXISTING: {
74
+ type: 'exact_match',
75
+ phrases: ['Need more evidence'],
76
+ status: 'active',
77
+ },
78
+ }),
79
+ };
80
+
81
+ expect(hasEquivalentPromotedRule(dictionary as any, 'Need more evidence')).toBe(true);
82
+ expect(hasEquivalentPromotedRule(dictionary as any, 'Another phrase')).toBe(false);
83
+ });
84
+
85
+ it('should generate distinct ids for different pain reasons with the same preview', () => {
86
+ const now = new Date('2026-03-20T06:38:32.222Z').getTime();
87
+
88
+ const idA = createEvolutionTaskId(
89
+ 'tool_failure',
90
+ 50,
91
+ '',
92
+ 'Tool edit failed on memory/.scratchpad.md',
93
+ now
94
+ );
95
+ const idB = createEvolutionTaskId(
96
+ 'tool_failure',
97
+ 50,
98
+ '',
99
+ 'Tool edit failed on MEMORY.md',
100
+ now
101
+ );
102
+
103
+ expect(idA).not.toBe(idB);
104
+ });
105
+
106
+ it('should generate distinct pain candidate fingerprints when only the suffix differs', () => {
107
+ const prefix = 'A'.repeat(60);
108
+ const textA = `${prefix} root-cause-one`;
109
+ const textB = `${prefix} root-cause-two`;
110
+
111
+ expect(createPainCandidateFingerprint(textA)).not.toBe(createPainCandidateFingerprint(textB));
112
+ });
113
+
114
+ it('should skip known noise payloads when tracking pain candidates', () => {
115
+ expect(shouldTrackPainCandidate('NO_REPLY')).toBe(false);
116
+ expect(shouldTrackPainCandidate(
117
+ '{"damageDetected":false,"severity":"mild","confidence":0.95,"reason":"observer"}'
118
+ )).toBe(false);
119
+ expect(shouldTrackPainCandidate('Tool edit failed on MEMORY.md')).toBe(true);
120
+ });
121
+
122
+ it('should initialize candidates as pending and keep longer samples', async () => {
123
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pain-candidate-'));
124
+ const candidatePath = path.join(dir, 'pain_candidates.json');
125
+ const longText = `Tool edit failed on MEMORY.md: ${'x'.repeat(1200)}`;
126
+
127
+ try {
128
+ await trackPainCandidate(longText, {
129
+ resolve: () => candidatePath,
130
+ } as any);
131
+
132
+ const data = JSON.parse(fs.readFileSync(candidatePath, 'utf8'));
133
+ const candidate = Object.values(data.candidates)[0] as any;
134
+
135
+ expect(candidate.status).toBe('pending');
136
+ expect(candidate.samples[0].length).toBeGreaterThan(200);
137
+ expect(candidate.samples[0].length).toBeLessThanOrEqual(1000);
138
+ } finally {
139
+ safeRmDir(dir);
140
+ }
141
+ });
142
+
143
+ it('should extract evolution task ids from diagnostician payloads', () => {
144
+ expect(extractEvolutionTaskId('Diagnose systemic pain [ID: ab12cd34]. Source: tool_failure.')).toBe('ab12cd34');
145
+ expect(extractEvolutionTaskId('plain task without id')).toBeNull();
146
+ });
147
+
148
+ it('should register assigned diagnostician session on the matching in-progress task', async () => {
149
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-evolution-session-'));
150
+ const queuePath = path.join(dir, 'evolution_queue.json');
151
+
152
+ fs.writeFileSync(queuePath, JSON.stringify([
153
+ { id: 'task-a', status: 'pending', score: 40, source: 'pain', reason: 'a', timestamp: '2026-03-20T00:00:00.000Z' },
154
+ { id: 'task-b', status: 'in_progress', score: 80, source: 'pain', reason: 'b', timestamp: '2026-03-20T00:00:00.000Z' }
155
+ ], null, 2), 'utf8');
156
+
157
+ try {
158
+ const registered = await registerEvolutionTaskSession(
159
+ () => queuePath,
160
+ 'task-b',
161
+ 'agent:diagnostician:session-1',
162
+ { warn: vi.fn() }
163
+ );
164
+
165
+ expect(registered).toBe(true);
166
+ const saved = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
167
+ expect(saved[1].assigned_session_key).toBe('agent:diagnostician:session-1');
168
+ expect(saved[1].started_at).toBeDefined();
169
+ } finally {
170
+ safeRmDir(dir);
171
+ }
172
+ });
173
+
174
+ it('should promote legacy candidates even when status is missing', async () => {
175
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-promotion-'));
176
+ const candidatePath = path.join(dir, 'pain_candidates.json');
177
+ const addRule = vi.fn();
178
+
179
+ fs.writeFileSync(candidatePath, JSON.stringify({
180
+ candidates: {
181
+ deadbeef: {
182
+ count: 3,
183
+ firstSeen: '2026-03-20T00:00:00.000Z',
184
+ samples: [
185
+ 'Tool edit failed on MEMORY.md. Could not find the exact text in MEMORY.md.',
186
+ 'Tool edit failed on CURRENT_FOCUS.md. Could not find the exact text in CURRENT_FOCUS.md.',
187
+ 'Tool edit failed on TEAM_COMMS.md. Could not find the exact text in TEAM_COMMS.md.',
188
+ ],
189
+ },
190
+ },
191
+ }, null, 2), 'utf8');
192
+
193
+ try {
194
+ await processPromotion({
195
+ workspaceDir: dir,
196
+ resolve: () => candidatePath,
197
+ config: {
198
+ get: (key: string) => {
199
+ if (key === 'thresholds.promotion_count_threshold') return 3;
200
+ if (key === 'scores.default_confusion') return 35;
201
+ return undefined;
202
+ },
203
+ },
204
+ dictionary: {
205
+ addRule,
206
+ getAllRules: () => ({}),
207
+ },
208
+ } as any, { info: vi.fn() }, null);
209
+
210
+ const saved = JSON.parse(fs.readFileSync(candidatePath, 'utf8'));
211
+ expect(addRule).toHaveBeenCalled();
212
+ expect(saved.candidates.deadbeef.status).toBe('promoted');
213
+ } finally {
214
+ safeRmDir(dir);
215
+ }
216
+ });
217
+
218
+ it('should flush the dictionary on its interval', async () => {
219
+ const mockDict = {
220
+ flush: vi.fn()
221
+ };
222
+ vi.mocked(DictionaryService.get).mockReturnValue(mockDict as any);
223
+
224
+ // Use path.resolve for cross-platform compatibility
225
+ const workspaceDir = path.resolve('/mock/workspace');
226
+ const expectedStateDir = path.join(workspaceDir, '.state');
227
+
228
+ const ctx = {
229
+ workspaceDir,
230
+ stateDir: path.resolve('/mock/state'),
231
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }
232
+ };
233
+
234
+ EvolutionWorkerService.start(ctx as any);
235
+
236
+ // Advance by 15 minutes
237
+ await vi.advanceTimersByTimeAsync(15 * 60 * 1000);
238
+
239
+ expect(mockDict.flush).toHaveBeenCalled();
240
+ // Service now uses workspace-specific stateDir, not ctx.stateDir
241
+ expect(sessionTracker.initPersistence).toHaveBeenCalledWith(expectedStateDir);
242
+ expect(sessionTracker.flushAllSessions).toHaveBeenCalled();
243
+
244
+ EvolutionWorkerService.stop(ctx as any);
245
+ });
246
+
247
+ it('should process queue work without persisting a legacy directive file', async () => {
248
+ const mockDict = {
249
+ flush: vi.fn()
250
+ };
251
+ vi.mocked(DictionaryService.get).mockReturnValue(mockDict as any);
252
+
253
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-evolution-worker-'));
254
+ const stateDir = path.join(workspaceDir, '.state');
255
+ fs.mkdirSync(path.join(stateDir, 'sessions'), { recursive: true });
256
+ fs.mkdirSync(path.join(stateDir, 'logs'), { recursive: true });
257
+ fs.writeFileSync(
258
+ path.join(stateDir, 'evolution_queue.json'),
259
+ JSON.stringify([
260
+ { id: 'task-1', score: 90, source: 'tool_failure', reason: 'write failed', timestamp: '2026-03-20T00:00:00.000Z', status: 'pending' },
261
+ ], null, 2),
262
+ 'utf8'
263
+ );
264
+
265
+ const ctx = {
266
+ workspaceDir,
267
+ stateDir,
268
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }
269
+ };
270
+
271
+ try {
272
+ EvolutionWorkerService.start(ctx as any);
273
+
274
+ await vi.advanceTimersByTimeAsync(5000);
275
+
276
+ const queue = JSON.parse(fs.readFileSync(path.join(stateDir, 'evolution_queue.json'), 'utf8'));
277
+ expect(queue[0].status).toBe('in_progress');
278
+ expect(fs.existsSync(path.join(stateDir, 'evolution_directive.json'))).toBe(false);
279
+ } finally {
280
+ EvolutionWorkerService.stop(ctx as any);
281
+ safeRmDir(workspaceDir);
282
+ }
283
+ });
284
+
285
+ describe('sleep_reflection stuck in_progress recovery', () => {
286
+ it('should recover stuck in_progress sleep_reflection tasks older than timeout', async () => {
287
+ const mockDict = {
288
+ flush: vi.fn()
289
+ };
290
+ vi.mocked(DictionaryService.get).mockReturnValue(mockDict as any);
291
+
292
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-sleep-recovery-'));
293
+ const stateDir = path.join(workspaceDir, '.state');
294
+ fs.mkdirSync(path.join(stateDir, 'sessions'), { recursive: true });
295
+ fs.mkdirSync(path.join(stateDir, 'logs'), { recursive: true });
296
+
297
+ // Create a sleep_reflection task that's been in_progress for 2 hours
298
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
299
+ fs.writeFileSync(
300
+ path.join(stateDir, 'evolution_queue.json'),
301
+ JSON.stringify([
302
+ {
303
+ id: 'sleep-stuck',
304
+ taskKind: 'sleep_reflection',
305
+ priority: 'medium',
306
+ score: 50,
307
+ source: 'nocturnal',
308
+ reason: 'Sleep-mode reflection',
309
+ trigger_text_preview: 'Idle workspace detected',
310
+ timestamp: twoHoursAgo,
311
+ enqueued_at: twoHoursAgo,
312
+ started_at: twoHoursAgo,
313
+ status: 'in_progress',
314
+ traceId: 'sleep-stuck',
315
+ retryCount: 0,
316
+ maxRetries: 1,
317
+ },
318
+ ], null, 2),
319
+ 'utf8'
320
+ );
321
+
322
+ const ctx = {
323
+ workspaceDir,
324
+ stateDir,
325
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }
326
+ };
327
+
328
+ try {
329
+ EvolutionWorkerService.start(ctx as any);
330
+ await vi.advanceTimersByTimeAsync(5000);
331
+
332
+ const queue = JSON.parse(fs.readFileSync(path.join(stateDir, 'evolution_queue.json'), 'utf8'));
333
+ expect(queue[0].status).toBe('failed');
334
+ expect(queue[0].resolution).toBe('failed_max_retries');
335
+ expect(queue[0].completed_at).toBeDefined();
336
+ expect(queue[0].lastError).toContain('timed out');
337
+ } finally {
338
+ EvolutionWorkerService.stop(ctx as any);
339
+ safeRmDir(workspaceDir);
340
+ }
341
+ });
342
+
343
+ it('should not recover sleep_reflection tasks within timeout', async () => {
344
+ const mockDict = { flush: vi.fn() };
345
+ vi.mocked(DictionaryService.get).mockReturnValue(mockDict as any);
346
+
347
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-sleep-recent-'));
348
+ const stateDir = path.join(workspaceDir, '.state');
349
+ fs.mkdirSync(path.join(stateDir, 'sessions'), { recursive: true });
350
+ fs.mkdirSync(path.join(stateDir, 'logs'), { recursive: true });
351
+
352
+ // Task started 10 minutes ago — well within 1-hour timeout
353
+ const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
354
+ fs.writeFileSync(
355
+ path.join(stateDir, 'evolution_queue.json'),
356
+ JSON.stringify([
357
+ {
358
+ id: 'sleep-recent',
359
+ taskKind: 'sleep_reflection',
360
+ priority: 'medium',
361
+ score: 50,
362
+ source: 'nocturnal',
363
+ reason: 'Sleep-mode reflection',
364
+ trigger_text_preview: 'Idle workspace detected',
365
+ timestamp: tenMinutesAgo,
366
+ enqueued_at: tenMinutesAgo,
367
+ started_at: tenMinutesAgo,
368
+ status: 'in_progress',
369
+ traceId: 'sleep-recent',
370
+ retryCount: 0,
371
+ maxRetries: 1,
372
+ },
373
+ ], null, 2),
374
+ 'utf8'
375
+ );
376
+
377
+ const ctx = {
378
+ workspaceDir,
379
+ stateDir,
380
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }
381
+ };
382
+
383
+ try {
384
+ EvolutionWorkerService.start(ctx as any);
385
+ await vi.advanceTimersByTimeAsync(5000);
386
+
387
+ const queue = JSON.parse(fs.readFileSync(path.join(stateDir, 'evolution_queue.json'), 'utf8'));
388
+ // Still in_progress — not old enough to recover
389
+ expect(queue[0].status).toBe('in_progress');
390
+ } finally {
391
+ EvolutionWorkerService.stop(ctx as any);
392
+ safeRmDir(workspaceDir);
393
+ }
394
+ });
395
+
396
+ it('should not affect pain_diagnosis in_progress timeout logic', async () => {
397
+ const mockDict = { flush: vi.fn() };
398
+ vi.mocked(DictionaryService.get).mockReturnValue(mockDict as any);
399
+
400
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pain-unchanged-'));
401
+ const stateDir = path.join(workspaceDir, '.state');
402
+ fs.mkdirSync(path.join(stateDir, 'sessions'), { recursive: true });
403
+ fs.mkdirSync(path.join(stateDir, 'logs'), { recursive: true });
404
+
405
+ // pain_diagnosis task that's been in_progress for 2 hours — should be auto-completed
406
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
407
+ fs.writeFileSync(
408
+ path.join(stateDir, 'evolution_queue.json'),
409
+ JSON.stringify([
410
+ {
411
+ id: 'pain-old',
412
+ taskKind: 'pain_diagnosis',
413
+ priority: 'high',
414
+ score: 90,
415
+ source: 'tool_failure',
416
+ reason: 'write failed',
417
+ trigger_text_preview: 'Tool edit failed',
418
+ timestamp: twoHoursAgo,
419
+ enqueued_at: twoHoursAgo,
420
+ started_at: twoHoursAgo,
421
+ status: 'in_progress',
422
+ traceId: 'pain-old',
423
+ retryCount: 0,
424
+ maxRetries: 3,
425
+ },
426
+ ], null, 2),
427
+ 'utf8'
428
+ );
429
+
430
+ const ctx = {
431
+ workspaceDir,
432
+ stateDir,
433
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }
434
+ };
435
+
436
+ try {
437
+ EvolutionWorkerService.start(ctx as any);
438
+ await vi.advanceTimersByTimeAsync(5000);
439
+
440
+ const queue = JSON.parse(fs.readFileSync(path.join(stateDir, 'evolution_queue.json'), 'utf8'));
441
+ // pain_diagnosis uses auto_completed_timeout, NOT failed
442
+ expect(queue[0].status).toBe('completed');
443
+ expect(queue[0].resolution).toBe('auto_completed_timeout');
444
+ } finally {
445
+ EvolutionWorkerService.stop(ctx as any);
446
+ safeRmDir(workspaceDir);
447
+ }
448
+ });
449
+ });
450
+
451
+ describe('Phase 3 Eligibility - Queue Only (Trust Removed)', () => {
452
+ it('makes queue eligible when tasks are valid', () => {
453
+ const result = evaluatePhase3Inputs(
454
+ [{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }]
455
+ );
456
+
457
+ expect(result.phase3ShadowEligible).toBe(true);
458
+ expect(result.queueTruthReady).toBe(true);
459
+ expect(result.evolution.eligible).toHaveLength(1);
460
+ expect(result.evolution.rejected).toHaveLength(0);
461
+ });
462
+
463
+ it('makes queue eligible when directive is stale (production scenario)', () => {
464
+ const result = evaluatePhase3Inputs(
465
+ [{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }]
466
+ );
467
+
468
+ expect(result.phase3ShadowEligible).toBe(true);
469
+ expect(result.queueTruthReady).toBe(true);
470
+ });
471
+
472
+ it('rejects empty queue', () => {
473
+ const result = evaluatePhase3Inputs([]);
474
+
475
+ expect(result.phase3ShadowEligible).toBe(false);
476
+ expect(result.queueTruthReady).toBe(false);
477
+ });
478
+
479
+ it('rejects invalid queue status', () => {
480
+ const result = evaluatePhase3Inputs(
481
+ [{ id: 'task-1', status: 'invalid' }]
482
+ );
483
+
484
+ expect(result.phase3ShadowEligible).toBe(false);
485
+ expect(result.queueTruthReady).toBe(false);
486
+ expect(result.evolution.rejected[0].reasons).toContain('invalid_status');
487
+ });
488
+
489
+ it('eligible requires queue with valid completed tasks', () => {
490
+ const result = evaluatePhase3Inputs(
491
+ [{ id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }]
492
+ );
493
+
494
+ expect(result.phase3ShadowEligible).toBe(true);
495
+ expect(result.queueTruthReady).toBe(true);
496
+ });
497
+
498
+ it('does not accept directive as a parameter (API design)', () => {
499
+ const func = evaluatePhase3Inputs;
500
+ const funcString = func.toString();
501
+
502
+ expect(funcString).not.toMatch(/directive\s*:/);
503
+ expect(funcString).not.toMatch(/directive\s*\)/);
504
+ });
505
+
506
+ it('handles multiple queue items correctly', () => {
507
+ const result = evaluatePhase3Inputs([
508
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' },
509
+ { id: 'task-2', status: 'in_progress', started_at: '2026-03-25T11:00:00.000Z' },
510
+ { id: 'task-3', status: 'pending' }
511
+ ]);
512
+
513
+ expect(result.phase3ShadowEligible).toBe(true);
514
+ expect(result.evolution.eligible).toHaveLength(3);
515
+ expect(result.evolution.rejected).toHaveLength(0);
516
+ });
517
+ });
518
+ });
519
+
520
+ // ── P0-3 / P1: purgeStaleFailedTasks tests ──
521
+
522
+ import { purgeStaleFailedTasks } from '../../src/service/evolution-worker.js';
523
+
524
+ describe('purgeStaleFailedTasks', () => {
525
+ const makeTask = (id: string, status: string, hoursAgo: number) => ({
526
+ id,
527
+ taskKind: 'sleep_reflection' as const,
528
+ status,
529
+ timestamp: new Date(Date.now() - hoursAgo * 60 * 60 * 1000).toISOString(),
530
+ enqueued_at: new Date(Date.now() - hoursAgo * 60 * 60 * 1000).toISOString(),
531
+ source: 'test',
532
+ score: 50,
533
+ reason: 'test',
534
+ retryCount: 1,
535
+ maxRetries: 1,
536
+ lastError: 'Nocturnal reflection failed: no_evaluable_principles',
537
+ resolution: 'failed_max_retries' as const,
538
+ });
539
+
540
+ it('should purge failed tasks older than 24 hours', () => {
541
+ const queue: any[] = [
542
+ makeTask('old-1', 'failed', 30), // 30h old — should be purged
543
+ makeTask('old-2', 'failed', 48), // 48h old — should be purged
544
+ makeTask('recent', 'failed', 12), // 12h old — should be kept
545
+ makeTask('pending', 'pending', 1),
546
+ ];
547
+
548
+ const result = purgeStaleFailedTasks(queue, console as any);
549
+
550
+ expect(result.purged).toBe(2);
551
+ expect(result.remaining).toBe(2);
552
+ expect(queue.length).toBe(2);
553
+ expect(queue.find((t) => t.id === 'old-1')).toBeUndefined();
554
+ expect(queue.find((t) => t.id === 'recent')).toBeDefined();
555
+ expect(queue.find((t) => t.id === 'pending')).toBeDefined();
556
+ });
557
+
558
+ it('should not purge non-failed tasks regardless of age', () => {
559
+ const queue: any[] = [
560
+ makeTask('old-completed', 'completed', 72),
561
+ makeTask('old-pending', 'pending', 72),
562
+ makeTask('old-in-progress', 'in_progress', 72),
563
+ ];
564
+
565
+ const result = purgeStaleFailedTasks(queue, console as any);
566
+
567
+ expect(result.purged).toBe(0);
568
+ expect(result.remaining).toBe(3);
569
+ expect(queue.length).toBe(3);
570
+ });
571
+
572
+ it('should group purge results by failure reason', () => {
573
+ const queue: any[] = [
574
+ { ...makeTask('fail-1', 'failed', 30), lastError: 'Nocturnal reflection failed: no_evaluable_principles' },
575
+ { ...makeTask('fail-2', 'failed', 30), lastError: 'Nocturnal reflection failed: no_evaluable_principles' },
576
+ { ...makeTask('fail-3', 'failed', 30), lastError: 'Nocturnal reflection failed: validation_failed' },
577
+ ];
578
+
579
+ const result = purgeStaleFailedTasks(queue, console as any);
580
+
581
+ expect(result.purged).toBe(3);
582
+ expect(result.byReason['Nocturnal reflection failed: no_evaluable_principles']).toBe(2);
583
+ expect(result.byReason['Nocturnal reflection failed: validation_failed']).toBe(1);
584
+ });
585
+ });