principles-disciple 1.8.0 → 1.8.2

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 (460) 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 +6 -1
  10. package/package.json +13 -15
  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} +185 -63
  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} +166 -139
  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} +263 -36
  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/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
  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/src/service/subagent-workflow/types.ts +378 -0
  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/zh/skills/pd-diagnostician/SKILL.md +70 -1
  138. package/templates/pain_settings.json +2 -1
  139. package/tests/README.md +120 -0
  140. package/tests/build-artifacts.test.ts +111 -0
  141. package/tests/commands/evolution-status.test.ts +222 -0
  142. package/tests/commands/evolver.test.ts +22 -0
  143. package/tests/commands/export.test.ts +78 -0
  144. package/tests/commands/nocturnal-review.test.ts +448 -0
  145. package/tests/commands/nocturnal-train.test.ts +97 -0
  146. package/tests/commands/pain.test.ts +108 -0
  147. package/tests/commands/samples.test.ts +65 -0
  148. package/tests/commands/strategy.test.ts +34 -0
  149. package/tests/commands/thinking-os.test.ts +88 -0
  150. package/tests/core/adaptive-thresholds.test.ts +261 -0
  151. package/tests/core/config-service.test.ts +89 -0
  152. package/tests/core/config.test.ts +90 -0
  153. package/tests/core/control-ui-db.test.ts +75 -0
  154. package/tests/core/core-template-guidance.test.ts +21 -0
  155. package/tests/core/detection-funnel.test.ts +63 -0
  156. package/tests/core/detection-service.test.ts +50 -0
  157. package/tests/core/dictionary-service.test.ts +116 -0
  158. package/tests/core/dictionary.test.ts +168 -0
  159. package/tests/core/empathy-keyword-matcher.test.ts +209 -0
  160. package/tests/core/event-log.test.ts +181 -0
  161. package/tests/core/evolution-e2e.test.ts +58 -0
  162. package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
  163. package/tests/core/evolution-engine.test.ts +562 -0
  164. package/tests/core/evolution-logger.test.ts +148 -0
  165. package/tests/core/evolution-migration.test.ts +50 -0
  166. package/tests/core/evolution-paths.test.ts +21 -0
  167. package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
  168. package/tests/core/evolution-reducer.test.ts +180 -0
  169. package/tests/core/evolution-types-loop.test.ts +48 -0
  170. package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
  171. package/tests/core/external-training-contract.test.ts +463 -0
  172. package/tests/core/focus-history.test.ts +682 -0
  173. package/tests/core/init-flatten.test.ts +69 -0
  174. package/tests/core/init-refactor.test.ts +87 -0
  175. package/tests/core/init-v1.3.test.ts +46 -0
  176. package/tests/core/init.test.ts +190 -0
  177. package/tests/core/local-worker-routing.test.ts +757 -0
  178. package/tests/core/migration.test.ts +84 -0
  179. package/tests/core/model-deployment-registry.test.ts +845 -0
  180. package/tests/core/model-training-registry.test.ts +889 -0
  181. package/tests/core/nocturnal-arbiter.test.ts +494 -0
  182. package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
  183. package/tests/core/nocturnal-compliance.test.ts +646 -0
  184. package/tests/core/nocturnal-dataset.test.ts +892 -0
  185. package/tests/core/nocturnal-executability.test.ts +357 -0
  186. package/tests/core/nocturnal-export.test.ts +462 -0
  187. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
  188. package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
  189. package/tests/core/nocturnal-trinity.test.ts +953 -0
  190. package/tests/core/pain.test.ts +33 -0
  191. package/tests/core/path-resolver.test.ts +57 -0
  192. package/tests/core/paths-refactor.test.ts +42 -0
  193. package/tests/core/phase7-rollout-integration.test.ts +477 -0
  194. package/tests/core/principle-training-state.test.ts +712 -0
  195. package/tests/core/profile.test.ts +56 -0
  196. package/tests/core/promotion-gate.test.ts +556 -0
  197. package/tests/core/risk-calculator.test.ts +168 -0
  198. package/tests/core/session-tracker.test.ts +191 -0
  199. package/tests/core/training-program.test.ts +472 -0
  200. package/tests/core/trajectory.test.ts +265 -0
  201. package/tests/core/workspace-context-factory.test.ts +18 -0
  202. package/tests/core/workspace-context.test.ts +134 -0
  203. package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
  204. package/tests/fixtures/production-compatibility.test.ts +147 -0
  205. package/tests/fixtures/production-mock-generator.ts +282 -0
  206. package/tests/hooks/bash-risk-integration.test.ts +137 -0
  207. package/tests/hooks/bash-risk.test.ts +81 -0
  208. package/tests/hooks/edit-verification.test.ts +678 -0
  209. package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
  210. package/tests/hooks/gate-edit-verification.test.ts +435 -0
  211. package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
  212. package/tests/hooks/gate.test.ts +271 -0
  213. package/tests/hooks/gfi-gate-unit.test.ts +422 -0
  214. package/tests/hooks/gfi-gate.test.ts +669 -0
  215. package/tests/hooks/lifecycle.test.ts +248 -0
  216. package/tests/hooks/llm.test.ts +308 -0
  217. package/tests/hooks/message-sanitize.test.ts +36 -0
  218. package/tests/hooks/pain.test.ts +141 -0
  219. package/tests/hooks/progressive-trust-gate.test.ts +277 -0
  220. package/tests/hooks/prompt.test.ts +1411 -0
  221. package/tests/hooks/subagent.test.ts +467 -0
  222. package/tests/hooks/thinking-gate.test.ts +313 -0
  223. package/tests/http/principles-console-route.test.ts +140 -0
  224. package/tests/hygiene-tracker.test.ts +77 -0
  225. package/tests/index.integration.test.ts +179 -0
  226. package/tests/index.shadow-routing.integration.test.ts +140 -0
  227. package/tests/index.test.ts +9 -0
  228. package/tests/integration/empathy-workflow-integration.test.ts +627 -0
  229. package/tests/service/control-ui-query-service.test.ts +121 -0
  230. package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
  231. package/tests/service/evolution-worker.test.ts +585 -0
  232. package/tests/service/nocturnal-runtime.test.ts +470 -0
  233. package/tests/service/nocturnal-service.test.ts +577 -0
  234. package/tests/service/nocturnal-target-selector.test.ts +615 -0
  235. package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
  236. package/tests/service/phase3-input-filter.test.ts +289 -0
  237. package/tests/service/runtime-summary-service.test.ts +919 -0
  238. package/tests/task-compliance.test.ts +166 -0
  239. package/tests/test-utils.ts +48 -0
  240. package/tests/tools/critique-prompt.test.ts +260 -0
  241. package/tests/tools/deep-reflect.test.ts +232 -0
  242. package/tests/tools/model-index.test.ts +246 -0
  243. package/tests/ui/app.test.tsx +114 -0
  244. package/tests/utils/file-lock.test.ts +407 -0
  245. package/tests/utils/hashing.test.ts +32 -0
  246. package/tests/utils/io.test.ts +39 -0
  247. package/tests/utils/nlp.test.ts +53 -0
  248. package/tests/utils/plugin-logger.test.ts +156 -0
  249. package/tsconfig.json +16 -0
  250. package/tsconfig.tsbuildinfo +1 -0
  251. package/ui/src/App.tsx +45 -0
  252. package/ui/src/api.ts +216 -0
  253. package/ui/src/charts.tsx +586 -0
  254. package/ui/src/components/ErrorState.tsx +6 -0
  255. package/ui/src/components/Loading.tsx +13 -0
  256. package/ui/src/components/ProtectedRoute.tsx +12 -0
  257. package/ui/src/components/Shell.tsx +91 -0
  258. package/ui/src/components/WorkspaceConfig.tsx +146 -0
  259. package/ui/src/components/index.ts +5 -0
  260. package/ui/src/context/auth.tsx +80 -0
  261. package/ui/src/context/theme.tsx +66 -0
  262. package/ui/src/hooks/useAutoRefresh.ts +39 -0
  263. package/ui/src/i18n/ui.ts +363 -0
  264. package/ui/src/main.tsx +16 -0
  265. package/ui/src/pages/EvolutionPage.tsx +352 -0
  266. package/ui/src/pages/FeedbackPage.tsx +140 -0
  267. package/ui/src/pages/GateMonitorPage.tsx +136 -0
  268. package/ui/src/pages/LoginPage.tsx +88 -0
  269. package/ui/src/pages/OverviewPage.tsx +238 -0
  270. package/ui/src/pages/SamplesPage.tsx +174 -0
  271. package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
  272. package/ui/src/styles.css +1661 -0
  273. package/ui/src/types.ts +368 -0
  274. package/ui/src/utils/format.ts +15 -0
  275. package/vitest.config.ts +23 -0
  276. package/dist/commands/capabilities.d.ts +0 -3
  277. package/dist/commands/capabilities.js +0 -73
  278. package/dist/commands/context.d.ts +0 -5
  279. package/dist/commands/evolution-status.d.ts +0 -4
  280. package/dist/commands/evolution-status.js +0 -117
  281. package/dist/commands/evolver.d.ts +0 -9
  282. package/dist/commands/evolver.js +0 -26
  283. package/dist/commands/export.d.ts +0 -2
  284. package/dist/commands/export.js +0 -98
  285. package/dist/commands/focus.d.ts +0 -14
  286. package/dist/commands/focus.js +0 -457
  287. package/dist/commands/nocturnal-review.d.ts +0 -24
  288. package/dist/commands/nocturnal-review.js +0 -265
  289. package/dist/commands/nocturnal-rollout.d.ts +0 -27
  290. package/dist/commands/nocturnal-rollout.js +0 -671
  291. package/dist/commands/nocturnal-train.d.ts +0 -25
  292. package/dist/commands/nocturnal-train.js +0 -919
  293. package/dist/commands/pain.d.ts +0 -5
  294. package/dist/commands/principle-rollback.d.ts +0 -4
  295. package/dist/commands/principle-rollback.js +0 -22
  296. package/dist/commands/rollback.d.ts +0 -19
  297. package/dist/commands/samples.d.ts +0 -2
  298. package/dist/commands/samples.js +0 -55
  299. package/dist/commands/strategy.d.ts +0 -3
  300. package/dist/commands/strategy.js +0 -29
  301. package/dist/commands/thinking-os.d.ts +0 -2
  302. package/dist/config/defaults/runtime.d.ts +0 -40
  303. package/dist/config/errors.d.ts +0 -84
  304. package/dist/config/errors.js +0 -94
  305. package/dist/config/index.js +0 -7
  306. package/dist/constants/diagnostician.d.ts +0 -12
  307. package/dist/constants/diagnostician.js +0 -56
  308. package/dist/constants/tools.d.ts +0 -17
  309. package/dist/constants/tools.js +0 -54
  310. package/dist/core/adaptive-thresholds.d.ts +0 -186
  311. package/dist/core/adaptive-thresholds.js +0 -300
  312. package/dist/core/config-service.d.ts +0 -15
  313. package/dist/core/config.d.ts +0 -127
  314. package/dist/core/control-ui-db.d.ts +0 -95
  315. package/dist/core/control-ui-db.js +0 -292
  316. package/dist/core/detection-funnel.d.ts +0 -33
  317. package/dist/core/detection-service.d.ts +0 -15
  318. package/dist/core/dictionary-service.d.ts +0 -15
  319. package/dist/core/dictionary.d.ts +0 -38
  320. package/dist/core/event-log.d.ts +0 -82
  321. package/dist/core/event-log.js +0 -463
  322. package/dist/core/evolution-engine.d.ts +0 -118
  323. package/dist/core/evolution-engine.js +0 -464
  324. package/dist/core/evolution-logger.d.ts +0 -137
  325. package/dist/core/evolution-logger.js +0 -256
  326. package/dist/core/evolution-migration.d.ts +0 -5
  327. package/dist/core/evolution-migration.js +0 -65
  328. package/dist/core/evolution-reducer.d.ts +0 -98
  329. package/dist/core/evolution-reducer.js +0 -465
  330. package/dist/core/evolution-types.d.ts +0 -287
  331. package/dist/core/evolution-types.js +0 -78
  332. package/dist/core/external-training-contract.d.ts +0 -276
  333. package/dist/core/external-training-contract.js +0 -269
  334. package/dist/core/focus-history.d.ts +0 -210
  335. package/dist/core/focus-history.js +0 -1185
  336. package/dist/core/hygiene/tracker.d.ts +0 -22
  337. package/dist/core/hygiene/tracker.js +0 -106
  338. package/dist/core/init.d.ts +0 -12
  339. package/dist/core/local-worker-routing.d.ts +0 -175
  340. package/dist/core/local-worker-routing.js +0 -525
  341. package/dist/core/migration.d.ts +0 -6
  342. package/dist/core/model-deployment-registry.d.ts +0 -218
  343. package/dist/core/model-deployment-registry.js +0 -503
  344. package/dist/core/model-training-registry.d.ts +0 -295
  345. package/dist/core/model-training-registry.js +0 -475
  346. package/dist/core/nocturnal-arbiter.d.ts +0 -159
  347. package/dist/core/nocturnal-arbiter.js +0 -534
  348. package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
  349. package/dist/core/nocturnal-candidate-scoring.js +0 -266
  350. package/dist/core/nocturnal-compliance.d.ts +0 -175
  351. package/dist/core/nocturnal-compliance.js +0 -824
  352. package/dist/core/nocturnal-dataset.d.ts +0 -224
  353. package/dist/core/nocturnal-dataset.js +0 -443
  354. package/dist/core/nocturnal-executability.d.ts +0 -85
  355. package/dist/core/nocturnal-executability.js +0 -331
  356. package/dist/core/nocturnal-export.d.ts +0 -124
  357. package/dist/core/nocturnal-export.js +0 -275
  358. package/dist/core/nocturnal-paths.d.ts +0 -124
  359. package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
  360. package/dist/core/nocturnal-trajectory-extractor.js +0 -307
  361. package/dist/core/nocturnal-trinity.d.ts +0 -311
  362. package/dist/core/nocturnal-trinity.js +0 -880
  363. package/dist/core/pain.d.ts +0 -4
  364. package/dist/core/pain.js +0 -70
  365. package/dist/core/path-resolver.d.ts +0 -46
  366. package/dist/core/paths.d.ts +0 -65
  367. package/dist/core/principle-training-state.d.ts +0 -121
  368. package/dist/core/principle-training-state.js +0 -321
  369. package/dist/core/profile.d.ts +0 -62
  370. package/dist/core/profile.js +0 -210
  371. package/dist/core/promotion-gate.d.ts +0 -238
  372. package/dist/core/promotion-gate.js +0 -529
  373. package/dist/core/risk-calculator.d.ts +0 -22
  374. package/dist/core/session-tracker.d.ts +0 -99
  375. package/dist/core/shadow-observation-registry.d.ts +0 -217
  376. package/dist/core/shadow-observation-registry.js +0 -308
  377. package/dist/core/system-logger.d.ts +0 -8
  378. package/dist/core/thinking-models.d.ts +0 -38
  379. package/dist/core/thinking-models.js +0 -170
  380. package/dist/core/training-program.d.ts +0 -233
  381. package/dist/core/training-program.js +0 -433
  382. package/dist/core/trajectory.d.ts +0 -411
  383. package/dist/core/trajectory.js +0 -1307
  384. package/dist/core/workspace-context.d.ts +0 -71
  385. package/dist/hooks/bash-risk.d.ts +0 -57
  386. package/dist/hooks/bash-risk.js +0 -137
  387. package/dist/hooks/edit-verification.d.ts +0 -62
  388. package/dist/hooks/edit-verification.js +0 -256
  389. package/dist/hooks/gate-block-helper.d.ts +0 -44
  390. package/dist/hooks/gate-block-helper.js +0 -119
  391. package/dist/hooks/gate.d.ts +0 -24
  392. package/dist/hooks/gate.js +0 -173
  393. package/dist/hooks/gfi-gate.d.ts +0 -40
  394. package/dist/hooks/gfi-gate.js +0 -113
  395. package/dist/hooks/lifecycle.d.ts +0 -5
  396. package/dist/hooks/lifecycle.js +0 -284
  397. package/dist/hooks/llm.d.ts +0 -12
  398. package/dist/hooks/message-sanitize.d.ts +0 -3
  399. package/dist/hooks/message-sanitize.js +0 -37
  400. package/dist/hooks/pain.d.ts +0 -5
  401. package/dist/hooks/pain.js +0 -301
  402. package/dist/hooks/progressive-trust-gate.d.ts +0 -51
  403. package/dist/hooks/progressive-trust-gate.js +0 -89
  404. package/dist/hooks/prompt.d.ts +0 -47
  405. package/dist/hooks/prompt.js +0 -884
  406. package/dist/hooks/subagent.d.ts +0 -10
  407. package/dist/hooks/subagent.js +0 -387
  408. package/dist/hooks/thinking-checkpoint.d.ts +0 -37
  409. package/dist/hooks/thinking-checkpoint.js +0 -51
  410. package/dist/hooks/trajectory-collector.d.ts +0 -32
  411. package/dist/hooks/trajectory-collector.js +0 -256
  412. package/dist/http/principles-console-route.d.ts +0 -9
  413. package/dist/http/principles-console-route.js +0 -567
  414. package/dist/i18n/commands.d.ts +0 -26
  415. package/dist/i18n/commands.js +0 -116
  416. package/dist/index.d.ts +0 -7
  417. package/dist/index.js +0 -581
  418. package/dist/service/central-database.d.ts +0 -104
  419. package/dist/service/central-database.js +0 -649
  420. package/dist/service/control-ui-query-service.d.ts +0 -221
  421. package/dist/service/control-ui-query-service.js +0 -543
  422. package/dist/service/empathy-observer-manager.d.ts +0 -52
  423. package/dist/service/empathy-observer-manager.js +0 -229
  424. package/dist/service/evolution-query-service.d.ts +0 -155
  425. package/dist/service/evolution-query-service.js +0 -258
  426. package/dist/service/evolution-worker.d.ts +0 -101
  427. package/dist/service/evolution-worker.js +0 -974
  428. package/dist/service/nocturnal-runtime.d.ts +0 -183
  429. package/dist/service/nocturnal-service.d.ts +0 -163
  430. package/dist/service/nocturnal-service.js +0 -787
  431. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  432. package/dist/service/nocturnal-target-selector.js +0 -315
  433. package/dist/service/phase3-input-filter.d.ts +0 -73
  434. package/dist/service/phase3-input-filter.js +0 -172
  435. package/dist/service/runtime-summary-service.d.ts +0 -122
  436. package/dist/service/runtime-summary-service.js +0 -485
  437. package/dist/service/trajectory-service.d.ts +0 -2
  438. package/dist/service/trajectory-service.js +0 -15
  439. package/dist/tools/critique-prompt.d.ts +0 -14
  440. package/dist/tools/deep-reflect.d.ts +0 -39
  441. package/dist/tools/deep-reflect.js +0 -350
  442. package/dist/tools/model-index.d.ts +0 -9
  443. package/dist/types/event-types.d.ts +0 -306
  444. package/dist/types/event-types.js +0 -106
  445. package/dist/types/hygiene-types.d.ts +0 -20
  446. package/dist/types/hygiene-types.js +0 -12
  447. package/dist/types/runtime-summary.d.ts +0 -47
  448. package/dist/types/runtime-summary.js +0 -1
  449. package/dist/types.d.ts +0 -50
  450. package/dist/types.js +0 -22
  451. package/dist/utils/file-lock.d.ts +0 -71
  452. package/dist/utils/file-lock.js +0 -309
  453. package/dist/utils/glob-match.d.ts +0 -28
  454. package/dist/utils/hashing.d.ts +0 -9
  455. package/dist/utils/io.d.ts +0 -6
  456. package/dist/utils/io.js +0 -106
  457. package/dist/utils/nlp.d.ts +0 -9
  458. package/dist/utils/plugin-logger.d.ts +0 -39
  459. package/dist/utils/subagent-probe.d.ts +0 -34
  460. package/dist/utils/subagent-probe.js +0 -81
@@ -0,0 +1,627 @@
1
+ /**
2
+ * Integration tests for Empathy Observer Workflow
3
+ *
4
+ * These tests verify the complete chain from hook entry to workflow state/events.
5
+ * Unlike unit tests, these use real file system and SQLite for WorkflowStore.
6
+ *
7
+ * Coverage:
8
+ * 1. EmpathyObserverWorkflowManager startWorkflow triggers workflow helper
9
+ * 2. WorkflowStore records spawn/wait/finalize events
10
+ * 3. Lifecycle cleanup (sweepExpiredWorkflows) works when wired
11
+ */
12
+
13
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
14
+ import * as fs from 'node:fs';
15
+ import * as os from 'node:os';
16
+ import * as path from 'node:path';
17
+ import { EmpathyObserverWorkflowManager, empathyObserverWorkflowSpec } from '../../src/service/subagent-workflow/empathy-observer-workflow-manager.js';
18
+ import { WorkflowStore } from '../../src/service/subagent-workflow/workflow-store.js';
19
+ import { handleBeforePromptBuild } from '../../src/hooks/prompt.js';
20
+ import { WorkspaceContext } from '../../src/core/workspace-context.js';
21
+
22
+ /**
23
+ * Helper to create a mock function that properly reports as AsyncFunction.
24
+ * This is required because isSubagentRuntimeAvailable() checks constructor.name === 'AsyncFunction'.
25
+ */
26
+ function mockAsyncFn<T extends (...args: any[]) => Promise<any>>(impl: (...args: any[]) => any) {
27
+ const fn = vi.fn(impl) as unknown as T;
28
+ Object.defineProperty(fn, 'constructor', {
29
+ value: function AsyncFunction() {},
30
+ writable: true,
31
+ configurable: true,
32
+ });
33
+ return fn;
34
+ }
35
+
36
+ describe('Empathy Workflow Integration (PR2)', () => {
37
+ let tempDir: string;
38
+ let stateDir: string;
39
+ let store: WorkflowStore;
40
+
41
+ const logger = {
42
+ info: vi.fn(),
43
+ warn: vi.fn(),
44
+ error: vi.fn(),
45
+ debug: vi.fn(),
46
+ };
47
+
48
+ beforeEach(() => {
49
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-empathy-integration-'));
50
+ stateDir = path.join(tempDir, '.state');
51
+ fs.mkdirSync(stateDir, { recursive: true });
52
+ });
53
+
54
+ afterEach(() => {
55
+ store?.dispose();
56
+ fs.rmSync(tempDir, { recursive: true, force: true });
57
+ vi.clearAllMocks();
58
+ });
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Test 1: WorkflowStore records spawn/wait/finalize events
62
+ // ---------------------------------------------------------------------------
63
+ describe('WorkflowStore event chain', () => {
64
+ it('records spawned event when workflow starts', async () => {
65
+ store = new WorkflowStore({ workspaceDir: tempDir });
66
+
67
+ const subagent = {
68
+ run: mockAsyncFn(async () => ({ runId: 'run-001' })),
69
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
70
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
71
+ deleteSession: mockAsyncFn(async () => {}),
72
+ };
73
+
74
+ const manager = new EmpathyObserverWorkflowManager({
75
+ workspaceDir: tempDir,
76
+ logger,
77
+ subagent,
78
+ });
79
+
80
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
81
+ parentSessionId: 'session-001',
82
+ taskInput: 'user message',
83
+ });
84
+
85
+ // Verify workflow was created in store
86
+ const workflow = store.getWorkflow(handle.workflowId);
87
+ expect(workflow).not.toBeNull();
88
+ expect(workflow?.state).toBe('active');
89
+ expect(workflow?.parent_session_id).toBe('session-001');
90
+
91
+ // Verify spawned event was recorded
92
+ const events = store.getEvents(handle.workflowId);
93
+ const spawnedEvent = events.find(e => e.event_type === 'spawned');
94
+ expect(spawnedEvent).toBeDefined();
95
+ expect(spawnedEvent?.to_state).toBe('active');
96
+
97
+ manager.dispose();
98
+ });
99
+
100
+ it('records wait_result and finalized events on successful completion', async () => {
101
+ store = new WorkflowStore({ workspaceDir: tempDir });
102
+
103
+ const subagent = {
104
+ run: mockAsyncFn(async () => ({ runId: 'run-002' })),
105
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
106
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
107
+ deleteSession: mockAsyncFn(async () => {}),
108
+ };
109
+
110
+ const manager = new EmpathyObserverWorkflowManager({
111
+ workspaceDir: tempDir,
112
+ logger,
113
+ subagent,
114
+ });
115
+
116
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
117
+ parentSessionId: 'session-002',
118
+ taskInput: 'user message',
119
+ });
120
+
121
+ // Clear the scheduled poll timeout to manually trigger notifyWaitResult
122
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
123
+ if (timeout) {
124
+ clearTimeout(timeout);
125
+ (manager as any).activeWorkflows.delete(handle.workflowId);
126
+ }
127
+
128
+ await manager.notifyWaitResult(handle.workflowId, 'ok');
129
+
130
+ // Verify final state
131
+ const workflow = store.getWorkflow(handle.workflowId);
132
+ expect(workflow?.state).toBe('completed');
133
+
134
+ // Verify event chain: spawned -> wait_result -> finalized
135
+ const events = store.getEvents(handle.workflowId);
136
+ const eventTypes = events.map(e => e.event_type);
137
+
138
+ expect(eventTypes).toContain('spawned');
139
+ expect(eventTypes).toContain('wait_result');
140
+ expect(eventTypes).toContain('finalized');
141
+
142
+ manager.dispose();
143
+ });
144
+ });
145
+
146
+ // ---------------------------------------------------------------------------
147
+ // Test 2: sweepExpiredWorkflows works when wired
148
+ // ---------------------------------------------------------------------------
149
+ describe('Lifecycle cleanup (sweepExpiredWorkflows)', () => {
150
+ it('sweeps workflows older than TTL', async () => {
151
+ store = new WorkflowStore({ workspaceDir: tempDir });
152
+
153
+ const subagent = {
154
+ run: mockAsyncFn(async () => ({ runId: 'run-003' })),
155
+ waitForRun: mockAsyncFn(async () => ({ status: 'timeout' as const })),
156
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: [] })),
157
+ deleteSession: mockAsyncFn(async () => {}),
158
+ };
159
+
160
+ const manager = new EmpathyObserverWorkflowManager({
161
+ workspaceDir: tempDir,
162
+ logger,
163
+ subagent,
164
+ });
165
+
166
+ // Create a workflow
167
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
168
+ parentSessionId: 'session-003',
169
+ taskInput: 'user message',
170
+ });
171
+
172
+ // Clear the scheduled poll timeout
173
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
174
+ if (timeout) {
175
+ clearTimeout(timeout);
176
+ (manager as any).activeWorkflows.delete(handle.workflowId);
177
+ }
178
+
179
+ // Manually set last_observed_at to simulate old workflow
180
+ const oldTime = Date.now() - 10 * 60 * 1000; // 10 minutes ago
181
+ const db = (store as any).db;
182
+ db.prepare('UPDATE subagent_workflows SET last_observed_at = ? WHERE workflow_id = ?').run(oldTime, handle.workflowId);
183
+
184
+ // Run sweep with 5 minute TTL
185
+ const sweptCount = await manager.sweepExpiredWorkflows(5 * 60 * 1000);
186
+
187
+ expect(sweptCount).toBe(1);
188
+
189
+ // Verify workflow is now expired
190
+ const workflow = store.getWorkflow(handle.workflowId);
191
+ expect(workflow?.state).toBe('expired');
192
+
193
+ // Verify swept event was recorded
194
+ const events = store.getEvents(handle.workflowId);
195
+ const sweptEvent = events.find(e => e.event_type === 'swept');
196
+ expect(sweptEvent).toBeDefined();
197
+
198
+ manager.dispose();
199
+ });
200
+ });
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Test 3: Debug summary is accessible
204
+ // ---------------------------------------------------------------------------
205
+ describe('Observability (getWorkflowDebugSummary)', () => {
206
+ it('provides debug summary with recent events', async () => {
207
+ store = new WorkflowStore({ workspaceDir: tempDir });
208
+
209
+ const subagent = {
210
+ run: mockAsyncFn(async () => ({ runId: 'run-004' })),
211
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
212
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
213
+ deleteSession: mockAsyncFn(async () => {}),
214
+ };
215
+
216
+ const manager = new EmpathyObserverWorkflowManager({
217
+ workspaceDir: tempDir,
218
+ logger,
219
+ subagent,
220
+ });
221
+
222
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
223
+ parentSessionId: 'session-004',
224
+ taskInput: 'user message',
225
+ });
226
+
227
+ const summary = await manager.getWorkflowDebugSummary(handle.workflowId);
228
+
229
+ expect(summary).not.toBeNull();
230
+ expect(summary?.workflowId).toBe(handle.workflowId);
231
+ expect(summary?.workflowType).toBe('empathy-observer');
232
+ expect(summary?.transport).toBe('runtime_direct');
233
+ expect(summary?.state).toBe('active');
234
+ expect(summary?.parentSessionId).toBe('session-004');
235
+ expect(summary?.recentEvents.length).toBeGreaterThan(0);
236
+ expect(summary?.recentEvents[0].eventType).toBe('spawned');
237
+
238
+ manager.dispose();
239
+ });
240
+ });
241
+
242
+ // ---------------------------------------------------------------------------
243
+ // Test 4: Workflow isolation
244
+ // ---------------------------------------------------------------------------
245
+ describe('Workflow isolation', () => {
246
+ it('each workflow is recorded independently in WorkflowStore', async () => {
247
+ store = new WorkflowStore({ workspaceDir: tempDir });
248
+
249
+ const subagent = {
250
+ run: mockAsyncFn(async () => ({ runId: 'run-005' })),
251
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
252
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
253
+ deleteSession: mockAsyncFn(async () => {}),
254
+ };
255
+
256
+ const manager = new EmpathyObserverWorkflowManager({
257
+ workspaceDir: tempDir,
258
+ logger,
259
+ subagent,
260
+ });
261
+
262
+ // Start workflow
263
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
264
+ parentSessionId: 'session-005',
265
+ taskInput: 'user message',
266
+ });
267
+
268
+ // Verify workflow is recorded independently
269
+ const workflow = store.getWorkflow(handle.workflowId);
270
+ expect(workflow).not.toBeNull();
271
+ expect(workflow?.workflow_type).toBe('empathy-observer');
272
+
273
+ manager.dispose();
274
+ });
275
+ });
276
+
277
+ // ---------------------------------------------------------------------------
278
+ // Test 5: State transition validation
279
+ // ---------------------------------------------------------------------------
280
+ describe('Workflow state transitions', () => {
281
+ it('transitions: active -> wait_result -> finalizing -> completed', async () => {
282
+ store = new WorkflowStore({ workspaceDir: tempDir });
283
+
284
+ const subagent = {
285
+ run: mockAsyncFn(async () => ({ runId: 'run-006' })),
286
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
287
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
288
+ deleteSession: mockAsyncFn(async () => {}),
289
+ };
290
+
291
+ const manager = new EmpathyObserverWorkflowManager({
292
+ workspaceDir: tempDir,
293
+ logger,
294
+ subagent,
295
+ });
296
+
297
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
298
+ parentSessionId: 'session-006',
299
+ taskInput: 'user message',
300
+ });
301
+
302
+ // Verify initial state
303
+ expect(store.getWorkflow(handle.workflowId)?.state).toBe('active');
304
+
305
+ // Clear timeout and trigger wait result
306
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
307
+ if (timeout) {
308
+ clearTimeout(timeout);
309
+ (manager as any).activeWorkflows.delete(handle.workflowId);
310
+ }
311
+
312
+ await manager.notifyWaitResult(handle.workflowId, 'ok');
313
+
314
+ // Verify final state
315
+ expect(store.getWorkflow(handle.workflowId)?.state).toBe('completed');
316
+
317
+ // Verify state transition events
318
+ const events = store.getEvents(handle.workflowId);
319
+ const stateChanges = events.filter(e => e.event_type === 'state_change' || e.event_type === 'wait_result' || e.event_type === 'finalized');
320
+
321
+ // Should have spawn, wait_result, finalized at minimum
322
+ expect(stateChanges.length).toBeGreaterThanOrEqual(2);
323
+
324
+ manager.dispose();
325
+ });
326
+
327
+ it('transitions to terminal_error on timeout', async () => {
328
+ store = new WorkflowStore({ workspaceDir: tempDir });
329
+
330
+ const subagent = {
331
+ run: mockAsyncFn(async () => ({ runId: 'run-007' })),
332
+ waitForRun: mockAsyncFn(async () => ({ status: 'timeout' as const })),
333
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: [] })),
334
+ deleteSession: mockAsyncFn(async () => {}),
335
+ };
336
+
337
+ const manager = new EmpathyObserverWorkflowManager({
338
+ workspaceDir: tempDir,
339
+ logger,
340
+ subagent,
341
+ });
342
+
343
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
344
+ parentSessionId: 'session-007',
345
+ taskInput: 'user message',
346
+ });
347
+
348
+ // Clear timeout and trigger error result
349
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
350
+ if (timeout) {
351
+ clearTimeout(timeout);
352
+ (manager as any).activeWorkflows.delete(handle.workflowId);
353
+ }
354
+
355
+ await manager.notifyWaitResult(handle.workflowId, 'timeout', 'wait timed out');
356
+
357
+ // Verify terminal state
358
+ expect(store.getWorkflow(handle.workflowId)?.state).toBe('terminal_error');
359
+
360
+ manager.dispose();
361
+ });
362
+ });
363
+
364
+ // ---------------------------------------------------------------------------
365
+ // Test 6: PR2.1 Task 1 - subagent_ended -> notifyLifecycleEvent wiring
366
+ // ---------------------------------------------------------------------------
367
+ describe('PR2.1: subagent_ended lifecycle event wiring', () => {
368
+ it('triggers notifyWaitResult when subagent_ended event received', async () => {
369
+ store = new WorkflowStore({ workspaceDir: tempDir });
370
+
371
+ const subagent = {
372
+ run: mockAsyncFn(async () => ({ runId: 'run-pr21-001' })),
373
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
374
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
375
+ deleteSession: mockAsyncFn(async () => {}),
376
+ };
377
+
378
+ const manager = new EmpathyObserverWorkflowManager({
379
+ workspaceDir: tempDir,
380
+ logger,
381
+ subagent,
382
+ });
383
+
384
+ // Start a helper workflow
385
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
386
+ parentSessionId: 'session-pr21-001',
387
+ taskInput: 'user message',
388
+ });
389
+
390
+ // Get the child session key
391
+ const workflow = store.getWorkflow(handle.workflowId);
392
+ expect(workflow).not.toBeNull();
393
+ const childSessionKey = workflow!.child_session_key;
394
+
395
+ // Verify child session key has expected prefix
396
+ expect(childSessionKey).toMatch(/^agent:main:subagent:workflow-/);
397
+
398
+ // Clear the scheduled poll timeout to prevent auto-wait
399
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
400
+ if (timeout) {
401
+ clearTimeout(timeout);
402
+ (manager as any).activeWorkflows.delete(handle.workflowId);
403
+ }
404
+
405
+ // Verify initial state is active
406
+ expect(store.getWorkflow(handle.workflowId)?.state).toBe('active');
407
+
408
+ // Simulate subagent_ended event by calling notifyLifecycleEvent
409
+ // This triggers notifyWaitResult internally
410
+ await manager.notifyLifecycleEvent(handle.workflowId, 'subagent_ended', {
411
+ outcome: 'ok',
412
+ });
413
+
414
+ // Verify workflow completed through lifecycle event path
415
+ const finalWorkflow = store.getWorkflow(handle.workflowId);
416
+ expect(finalWorkflow?.state).toBe('completed');
417
+
418
+ // Verify event chain includes finalized
419
+ const events = store.getEvents(handle.workflowId);
420
+ const finalizedEvent = events.find(e => e.event_type === 'finalized');
421
+ expect(finalizedEvent).toBeDefined();
422
+
423
+ manager.dispose();
424
+ });
425
+
426
+ it('handles error outcome via lifecycle event', async () => {
427
+ store = new WorkflowStore({ workspaceDir: tempDir });
428
+
429
+ const subagent = {
430
+ run: mockAsyncFn(async () => ({ runId: 'run-pr21-002' })),
431
+ waitForRun: mockAsyncFn(async () => ({ status: 'error' as const, error: 'test error' })),
432
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: [] })),
433
+ deleteSession: mockAsyncFn(async () => {}),
434
+ };
435
+
436
+ const manager = new EmpathyObserverWorkflowManager({
437
+ workspaceDir: tempDir,
438
+ logger,
439
+ subagent,
440
+ });
441
+
442
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
443
+ parentSessionId: 'session-pr21-002',
444
+ taskInput: 'test',
445
+ });
446
+
447
+ // Clear timeout
448
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
449
+ if (timeout) {
450
+ clearTimeout(timeout);
451
+ (manager as any).activeWorkflows.delete(handle.workflowId);
452
+ }
453
+
454
+ // Notify error outcome
455
+ await manager.notifyLifecycleEvent(handle.workflowId, 'subagent_ended', {
456
+ outcome: 'error',
457
+ error: 'subagent failed',
458
+ });
459
+
460
+ // Verify terminal state
461
+ const workflow = store.getWorkflow(handle.workflowId);
462
+ expect(workflow?.state).toBe('terminal_error');
463
+
464
+ manager.dispose();
465
+ });
466
+ });
467
+
468
+ // ---------------------------------------------------------------------------
469
+ // Test 7: PR2.1 Task 2 - sweepExpiredWorkflows integration
470
+ // ---------------------------------------------------------------------------
471
+ describe('PR2.1: sweepExpiredWorkflows integration', () => {
472
+ it('WorkflowStore.getExpiredWorkflows returns workflows past TTL', async () => {
473
+ store = new WorkflowStore({ workspaceDir: tempDir });
474
+
475
+ const subagent = {
476
+ run: mockAsyncFn(async () => ({ runId: 'run-sweep-001' })),
477
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
478
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{}'] })),
479
+ deleteSession: mockAsyncFn(async () => {}),
480
+ };
481
+
482
+ const manager = new EmpathyObserverWorkflowManager({
483
+ workspaceDir: tempDir,
484
+ logger,
485
+ subagent,
486
+ });
487
+
488
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
489
+ parentSessionId: 'session-sweep-001',
490
+ taskInput: 'test',
491
+ });
492
+
493
+ // Clear timeout
494
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
495
+ if (timeout) {
496
+ clearTimeout(timeout);
497
+ (manager as any).activeWorkflows.delete(handle.workflowId);
498
+ }
499
+
500
+ // Manually set last_observed_at to simulate expired workflow
501
+ const db = (store as any).db;
502
+ const oldTime = Date.now() - 10 * 60 * 1000; // 10 minutes ago
503
+ db.prepare('UPDATE subagent_workflows SET last_observed_at = ? WHERE workflow_id = ?').run(oldTime, handle.workflowId);
504
+
505
+ // Verify getExpiredWorkflows finds it
506
+ const expired = store.getExpiredWorkflows(5 * 60 * 1000);
507
+ expect(expired.length).toBe(1);
508
+ expect(expired[0].workflow_id).toBe(handle.workflowId);
509
+
510
+ manager.dispose();
511
+ });
512
+
513
+ it('sweepExpiredWorkflows marks workflows as expired', async () => {
514
+ store = new WorkflowStore({ workspaceDir: tempDir });
515
+
516
+ const subagent = {
517
+ run: mockAsyncFn(async () => ({ runId: 'run-sweep-002' })),
518
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
519
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{}'] })),
520
+ deleteSession: mockAsyncFn(async () => {}),
521
+ };
522
+
523
+ const manager = new EmpathyObserverWorkflowManager({
524
+ workspaceDir: tempDir,
525
+ logger,
526
+ subagent,
527
+ });
528
+
529
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
530
+ parentSessionId: 'session-sweep-002',
531
+ taskInput: 'test',
532
+ });
533
+
534
+ // Clear timeout
535
+ const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
536
+ if (timeout) {
537
+ clearTimeout(timeout);
538
+ (manager as any).activeWorkflows.delete(handle.workflowId);
539
+ }
540
+
541
+ // Manually expire
542
+ const db = (store as any).db;
543
+ const oldTime = Date.now() - 10 * 60 * 1000;
544
+ db.prepare('UPDATE subagent_workflows SET last_observed_at = ? WHERE workflow_id = ?').run(oldTime, handle.workflowId);
545
+
546
+ // Sweep
547
+ const count = await manager.sweepExpiredWorkflows(5 * 60 * 1000);
548
+ expect(count).toBe(1);
549
+
550
+ // Verify state
551
+ const workflow = store.getWorkflow(handle.workflowId);
552
+ expect(workflow?.state).toBe('expired');
553
+
554
+ // Verify swept event
555
+ const events = store.getEvents(handle.workflowId);
556
+ const sweptEvent = events.find(e => e.event_type === 'swept');
557
+ expect(sweptEvent).toBeDefined();
558
+
559
+ manager.dispose();
560
+ });
561
+ });
562
+
563
+ // ---------------------------------------------------------------------------
564
+ // Test 8: PR2.1 Task 3 - getWorkflowDebugSummary slash command
565
+ // ---------------------------------------------------------------------------
566
+ describe('PR2.1: getWorkflowDebugSummary accessibility', () => {
567
+ it('provides complete debug summary with all fields', async () => {
568
+ store = new WorkflowStore({ workspaceDir: tempDir });
569
+
570
+ const subagent = {
571
+ run: mockAsyncFn(async () => ({ runId: 'run-debug-001' })),
572
+ waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
573
+ getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":true,"severity":"moderate"}'] })),
574
+ deleteSession: mockAsyncFn(async () => {}),
575
+ };
576
+
577
+ const manager = new EmpathyObserverWorkflowManager({
578
+ workspaceDir: tempDir,
579
+ logger,
580
+ subagent,
581
+ });
582
+
583
+ const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
584
+ parentSessionId: 'session-debug-001',
585
+ workspaceDir: tempDir,
586
+ taskInput: 'This is a test user message for debugging',
587
+ metadata: { customField: 'customValue' },
588
+ });
589
+
590
+ const summary = await manager.getWorkflowDebugSummary(handle.workflowId);
591
+
592
+ expect(summary).not.toBeNull();
593
+ expect(summary?.workflowId).toBe(handle.workflowId);
594
+ expect(summary?.workflowType).toBe('empathy-observer');
595
+ expect(summary?.transport).toBe('runtime_direct');
596
+ expect(summary?.state).toBe('active');
597
+ expect(summary?.cleanupState).toBe('none');
598
+ expect(summary?.parentSessionId).toBe('session-debug-001');
599
+ expect(summary?.childSessionKey).toContain('workflow-');
600
+ expect(summary?.runId).toBe('run-debug-001');
601
+ expect(summary?.metadata.taskInput).toBe('This is a test user message for debugging');
602
+ expect(summary?.metadata.customField).toBe('customValue');
603
+ expect(summary?.recentEvents.length).toBeGreaterThan(0);
604
+
605
+ // Verify first event is spawned
606
+ expect(summary?.recentEvents[0].eventType).toBe('spawned');
607
+
608
+ manager.dispose();
609
+ });
610
+
611
+ it('returns null for non-existent workflow', async () => {
612
+ store = new WorkflowStore({ workspaceDir: tempDir });
613
+
614
+ const manager = new EmpathyObserverWorkflowManager({
615
+ workspaceDir: tempDir,
616
+ logger,
617
+ subagent: {} as any,
618
+ });
619
+
620
+ const summary = await manager.getWorkflowDebugSummary('non-existent-id');
621
+ expect(summary).toBeNull();
622
+
623
+ manager.dispose();
624
+ });
625
+ });
626
+
627
+ });