principles-disciple 1.8.1 → 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 (470) 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/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 -129
  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 -101
  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 -13
  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 -52
  403. package/dist/hooks/progressive-trust-gate.js +0 -134
  404. package/dist/hooks/prompt.d.ts +0 -49
  405. package/dist/hooks/prompt.js +0 -905
  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 -681
  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 -88
  423. package/dist/service/empathy-observer-manager.js +0 -414
  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 -975
  428. package/dist/service/health-query-service.d.ts +0 -170
  429. package/dist/service/health-query-service.js +0 -662
  430. package/dist/service/nocturnal-runtime.d.ts +0 -183
  431. package/dist/service/nocturnal-service.d.ts +0 -163
  432. package/dist/service/nocturnal-service.js +0 -787
  433. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  434. package/dist/service/nocturnal-target-selector.js +0 -315
  435. package/dist/service/phase3-input-filter.d.ts +0 -73
  436. package/dist/service/phase3-input-filter.js +0 -172
  437. package/dist/service/runtime-summary-service.d.ts +0 -122
  438. package/dist/service/runtime-summary-service.js +0 -485
  439. package/dist/service/subagent-workflow/empathy-observer-workflow-manager.d.ts +0 -48
  440. package/dist/service/subagent-workflow/index.d.ts +0 -4
  441. package/dist/service/subagent-workflow/index.js +0 -3
  442. package/dist/service/subagent-workflow/runtime-direct-driver.d.ts +0 -77
  443. package/dist/service/subagent-workflow/runtime-direct-driver.js +0 -75
  444. package/dist/service/subagent-workflow/types.js +0 -11
  445. package/dist/service/subagent-workflow/workflow-store.d.ts +0 -26
  446. package/dist/service/subagent-workflow/workflow-store.js +0 -165
  447. package/dist/service/trajectory-service.d.ts +0 -2
  448. package/dist/service/trajectory-service.js +0 -15
  449. package/dist/tools/critique-prompt.d.ts +0 -14
  450. package/dist/tools/deep-reflect.d.ts +0 -39
  451. package/dist/tools/deep-reflect.js +0 -350
  452. package/dist/tools/model-index.d.ts +0 -9
  453. package/dist/types/event-types.d.ts +0 -306
  454. package/dist/types/event-types.js +0 -106
  455. package/dist/types/hygiene-types.d.ts +0 -20
  456. package/dist/types/hygiene-types.js +0 -12
  457. package/dist/types/runtime-summary.d.ts +0 -47
  458. package/dist/types/runtime-summary.js +0 -1
  459. package/dist/types.d.ts +0 -50
  460. package/dist/types.js +0 -22
  461. package/dist/utils/file-lock.d.ts +0 -71
  462. package/dist/utils/file-lock.js +0 -309
  463. package/dist/utils/glob-match.d.ts +0 -28
  464. package/dist/utils/hashing.d.ts +0 -9
  465. package/dist/utils/io.d.ts +0 -6
  466. package/dist/utils/io.js +0 -106
  467. package/dist/utils/nlp.d.ts +0 -9
  468. package/dist/utils/plugin-logger.d.ts +0 -39
  469. package/dist/utils/subagent-probe.d.ts +0 -34
  470. package/dist/utils/subagent-probe.js +0 -81
@@ -17,115 +17,176 @@
17
17
  * - cooldown/quota state is persisted in nocturnal-runtime.json
18
18
  * - abandoned sessions (>2h inactive) must not block nocturnal flow
19
19
  */
20
+
20
21
  import * as fs from 'fs';
21
22
  import * as path from 'path';
22
- import { listSessions } from '../core/session-tracker.js';
23
+ import { listSessions, SessionState } from '../core/session-tracker.js';
23
24
  import { withLockAsync } from '../utils/file-lock.js';
25
+
24
26
  // ---------------------------------------------------------------------------
25
- // Session Key Parsing (replicated from openclaw/src/sessions/session-key-utils.ts)
27
+ // System Session Detection
26
28
  // ---------------------------------------------------------------------------
27
- /** Parse an agent-scoped session key. Returns null for non-agent keys. */
28
- function parseAgentSessionKey(sessionKey) {
29
- const raw = (sessionKey ?? '').trim().toLowerCase();
30
- if (!raw)
31
- return null;
32
- const parts = raw.split(':').filter(Boolean);
33
- if (parts.length < 3 || parts[0] !== 'agent')
34
- return null;
35
- const agentId = parts[1]?.trim();
36
- const rest = parts.slice(2).join(':');
37
- if (!agentId || !rest)
38
- return null;
39
- return { agentId, rest };
40
- }
41
- /**
42
- * Returns true if the session was created by a system process (cron, boot, probe, subagent, acp).
43
- * Uses OpenClaw's native session key patterns to avoid false positives.
44
- *
45
- * System patterns:
46
- * - boot- prefix: boot sessions (e.g. boot-2026-04-02_10-43-45)
47
- * - probe- prefix: probe sessions (e.g. probe-glm-4.9-xxx)
48
- * - cron:<...> rest: cron run sessions (agent:<id>:cron:...)
49
- * - subagent: prefix: subagent sessions
50
- * - acp: prefix: acp sessions
51
- *
52
- * Excluded (NOT system sessions):
53
- * - User sessions like agent:main:feishu:user:xxx — third component is channel type, NOT cron/subagent/acp
54
- */
29
+
55
30
  /**
56
31
  * Returns true if the session was created by a system process (cron, boot, probe, subagent, acp).
57
32
  * Uses OpenClaw's native session key patterns to avoid false positives.
58
33
  *
59
34
  * Detection priority (most reliable first):
60
- * 1. trigger field: Most reliable - explicitly set by OpenClaw ("cron", "heartbeat", "subagent")
35
+ * 1. trigger field: Most reliable explicitly set by OpenClaw ("cron", "heartbeat", "subagent")
61
36
  * 2. sessionKey patterns: Secondary confirmation via structured key (agent:main:cron:...)
62
37
  * 3. sessionId prefix: Fallback for boot-, probe- prefixed IDs
63
38
  *
64
- * System patterns:
65
- * - trigger === 'cron' | 'heartbeat' | 'subagent'
66
- * - sessionKey contains: cron:, subagent:, acp:
67
- * - sessionId starts with: boot-, probe-
39
+ * Excluded (NOT system sessions):
40
+ * - User sessions like agent:main:feishu:user:xxx third component is channel type
68
41
  */
69
- function isSystemSession(state) {
42
+ function isSystemSession(state: SessionState): boolean {
70
43
  const { sessionId, sessionKey, trigger } = state;
44
+
71
45
  // Primary: trigger field is explicitly set by OpenClaw - most reliable
72
46
  if (trigger === 'cron' || trigger === 'heartbeat' || trigger === 'subagent') {
73
47
  return true;
74
48
  }
49
+
75
50
  // Secondary: sessionKey pattern matching
76
51
  if (sessionKey) {
77
52
  const raw = sessionKey.toLowerCase();
78
- if (raw.includes('cron:'))
79
- return true;
80
- if (raw.includes('subagent:'))
81
- return true;
82
- if (raw.includes('acp:'))
83
- return true;
53
+ if (raw.includes('cron:')) return true;
54
+ if (raw.includes('subagent:')) return true;
55
+ if (raw.includes('acp:')) return true;
84
56
  }
57
+
85
58
  // Fallback: sessionId prefix patterns (boot-, probe-)
86
- if (sessionId?.startsWith('boot-'))
87
- return true;
88
- if (sessionId?.startsWith('probe-'))
89
- return true;
59
+ if (sessionId?.startsWith('boot-')) return true;
60
+ if (sessionId?.startsWith('probe-')) return true;
61
+
90
62
  return false;
91
63
  }
64
+
92
65
  // ---------------------------------------------------------------------------
93
66
  // Constants
94
67
  // ---------------------------------------------------------------------------
68
+
95
69
  /** File name for nocturnal runtime bookkeeping */
96
70
  export const NOCTURNAL_RUNTIME_FILE = 'nocturnal-runtime.json';
71
+
97
72
  /** Default idle threshold: workspace is considered idle if no activity for this duration (ms) */
98
73
  export const DEFAULT_IDLE_THRESHOLD_MS = 30 * 60 * 1000; // 30 minutes
74
+
99
75
  /** Default cooldown between nocturnal runs (ms) */
100
76
  export const DEFAULT_GLOBAL_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour
77
+
101
78
  /** Default per-principle cooldown (ms) */
102
79
  export const DEFAULT_PRINCIPLE_COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6 hours
80
+
103
81
  /** Default maximum nocturnal runs per quota window */
104
82
  export const DEFAULT_MAX_RUNS_PER_WINDOW = 3;
83
+
105
84
  /** Default quota window size (ms) */
106
85
  export const DEFAULT_QUOTA_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours
86
+
107
87
  /** Abandoned session threshold: sessions inactive for longer than this are ignored (ms) */
108
88
  export const DEFAULT_ABANDONED_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Types
92
+ // ---------------------------------------------------------------------------
93
+
94
+ /**
95
+ * Persisted state for nocturnal runtime bookkeeping.
96
+ * Stored in {stateDir}/nocturnal-runtime.json
97
+ */
98
+ export interface NocturnalRuntimeState {
99
+ /** Last time a nocturnal run was started (ISO string) */
100
+ lastRunAt?: string;
101
+
102
+ /** Last time a nocturnal run completed successfully */
103
+ lastSuccessfulRunAt?: string;
104
+
105
+ /** Cooldown end time for global cooldown (ISO string) */
106
+ globalCooldownUntil?: string;
107
+
108
+ /**
109
+ * Per-principle cooldown map.
110
+ * Key: principleId, Value: ISO string of cooldown end time
111
+ */
112
+ principleCooldowns: Record<string, string>;
113
+
114
+ /**
115
+ * Sliding window of recent run timestamps.
116
+ * Used for quota enforcement.
117
+ */
118
+ recentRunTimestamps: string[];
119
+
120
+ /** Metadata about last run (for debugging) */
121
+ lastRunMeta?: {
122
+ targetPrincipleId?: string;
123
+ sampleCount?: number;
124
+ status: 'success' | 'failed' | 'skipped';
125
+ reason?: string;
126
+ };
127
+ }
128
+
129
+ /** Result of an idle check */
130
+ export interface IdleCheckResult {
131
+ /** Whether the workspace is currently idle */
132
+ isIdle: boolean;
133
+ /** Most recent activity timestamp across all sessions (epoch ms) */
134
+ mostRecentActivityAt: number;
135
+ /** How long since the last activity (ms) */
136
+ idleForMs: number;
137
+ /** Number of active (non-abandoned) user sessions found */
138
+ userActiveSessions: number;
139
+ /** List of abandoned session IDs (inactive > abandoned threshold) */
140
+ abandonedSessionIds: string[];
141
+ /** Whether trajectory guardrail also confirms idle */
142
+ trajectoryGuardrailConfirmsIdle: boolean;
143
+ /** Reason for the idle determination */
144
+ reason: string;
145
+ }
146
+
147
+ /** Result of a cooldown check */
148
+ export interface CooldownCheckResult {
149
+ /** Whether the global cooldown is currently active */
150
+ globalCooldownActive: boolean;
151
+ /** When the global cooldown ends (ISO string), null if not in cooldown */
152
+ globalCooldownUntil: string | null;
153
+ /** Remaining ms until global cooldown expires */
154
+ globalCooldownRemainingMs: number;
155
+ /** Whether the principle-specific cooldown is active */
156
+ principleCooldownActive: boolean;
157
+ /** When the principle cooldown ends (ISO string), null if not in cooldown */
158
+ principleCooldownUntil: string | null;
159
+ /** Remaining ms until principle cooldown expires */
160
+ principleCooldownRemainingMs: number;
161
+ /** Whether the quota has been exhausted */
162
+ quotaExhausted: boolean;
163
+ /** Number of runs remaining in current window */
164
+ runsRemaining: number;
165
+ }
166
+
109
167
  // ---------------------------------------------------------------------------
110
168
  // Default State
111
169
  // ---------------------------------------------------------------------------
112
- function createDefaultState() {
170
+
171
+ function createDefaultState(): NocturnalRuntimeState {
113
172
  return {
114
173
  principleCooldowns: {},
115
174
  recentRunTimestamps: [],
116
175
  };
117
176
  }
177
+
118
178
  // ---------------------------------------------------------------------------
119
179
  // File Operations (with locking)
120
180
  // ---------------------------------------------------------------------------
121
- async function readState(stateDir) {
181
+
182
+ async function readState(stateDir: string): Promise<NocturnalRuntimeState> {
122
183
  const filePath = path.join(stateDir, NOCTURNAL_RUNTIME_FILE);
123
184
  if (!fs.existsSync(filePath)) {
124
185
  return createDefaultState();
125
186
  }
126
187
  try {
127
188
  const raw = fs.readFileSync(filePath, 'utf-8');
128
- const parsed = JSON.parse(raw);
189
+ const parsed = JSON.parse(raw) as NocturnalRuntimeState;
129
190
  // Ensure required fields exist (migration-safe)
130
191
  return {
131
192
  principleCooldowns: parsed.principleCooldowns ?? {},
@@ -135,20 +196,20 @@ async function readState(stateDir) {
135
196
  globalCooldownUntil: parsed.globalCooldownUntil,
136
197
  lastRunMeta: parsed.lastRunMeta,
137
198
  };
138
- }
139
- catch {
199
+ } catch {
140
200
  // Corrupted file — start fresh
141
201
  return createDefaultState();
142
202
  }
143
203
  }
144
- function readStateSync(stateDir) {
204
+
205
+ function readStateSync(stateDir: string): NocturnalRuntimeState {
145
206
  const filePath = path.join(stateDir, NOCTURNAL_RUNTIME_FILE);
146
207
  if (!fs.existsSync(filePath)) {
147
208
  return createDefaultState();
148
209
  }
149
210
  try {
150
211
  const raw = fs.readFileSync(filePath, 'utf-8');
151
- const parsed = JSON.parse(raw);
212
+ const parsed = JSON.parse(raw) as NocturnalRuntimeState;
152
213
  return {
153
214
  principleCooldowns: parsed.principleCooldowns ?? {},
154
215
  recentRunTimestamps: parsed.recentRunTimestamps ?? [],
@@ -157,13 +218,13 @@ function readStateSync(stateDir) {
157
218
  globalCooldownUntil: parsed.globalCooldownUntil,
158
219
  lastRunMeta: parsed.lastRunMeta,
159
220
  };
160
- }
161
- catch (err) {
221
+ } catch (err) {
162
222
  console.warn(`[nocturnal-runtime] State file corrupted, resetting: ${err instanceof Error ? err.message : String(err)}`);
163
223
  return createDefaultState();
164
224
  }
165
225
  }
166
- async function writeState(stateDir, state) {
226
+
227
+ async function writeState(stateDir: string, state: NocturnalRuntimeState): Promise<void> {
167
228
  const filePath = path.join(stateDir, NOCTURNAL_RUNTIME_FILE);
168
229
  const stateDirPath = path.dirname(filePath);
169
230
  if (!fs.existsSync(stateDirPath)) {
@@ -173,9 +234,11 @@ async function writeState(stateDir, state) {
173
234
  fs.writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf-8');
174
235
  });
175
236
  }
237
+
176
238
  // ---------------------------------------------------------------------------
177
239
  // Idle Detection
178
240
  // ---------------------------------------------------------------------------
241
+
179
242
  /**
180
243
  * Check if the workspace is currently idle based on session activity.
181
244
  *
@@ -191,31 +254,45 @@ async function writeState(stateDir, state) {
191
254
  * @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
192
255
  * @returns IdleCheckResult with full diagnostic information
193
256
  */
194
- export function checkWorkspaceIdle(workspaceDir, options = {}, trajectoryLastActivityAt) {
195
- const { idleThresholdMs = DEFAULT_IDLE_THRESHOLD_MS, abandonedThresholdMs = DEFAULT_ABANDONED_THRESHOLD_MS, } = options;
257
+ export function checkWorkspaceIdle(
258
+ workspaceDir: string,
259
+ options: {
260
+ idleThresholdMs?: number;
261
+ abandonedThresholdMs?: number;
262
+ } = {},
263
+ trajectoryLastActivityAt?: number
264
+ ): IdleCheckResult {
265
+ const {
266
+ idleThresholdMs = DEFAULT_IDLE_THRESHOLD_MS,
267
+ abandonedThresholdMs = DEFAULT_ABANDONED_THRESHOLD_MS,
268
+ } = options;
269
+
196
270
  const now = Date.now();
197
271
  const sessions = listSessions(workspaceDir);
272
+
198
273
  // Separate active vs abandoned sessions
199
- const abandonedSessions = [];
274
+ const abandonedSessions: string[] = [];
200
275
  let mostRecentActivityAt = 0;
201
276
  let userActiveSessions = 0;
277
+
202
278
  for (const session of sessions) {
203
279
  // Skip system sessions (cron, boot, probe, subagent, acp) from idle determination
204
- if (isSystemSession(session))
205
- continue;
280
+ if (isSystemSession(session)) continue;
281
+
206
282
  const inactiveFor = now - session.lastActivityAt;
207
283
  if (inactiveFor > abandonedThresholdMs) {
208
284
  abandonedSessions.push(session.sessionId);
209
- }
210
- else {
285
+ } else {
211
286
  userActiveSessions++;
212
287
  if (session.lastActivityAt > mostRecentActivityAt) {
213
288
  mostRecentActivityAt = session.lastActivityAt;
214
289
  }
215
290
  }
216
291
  }
292
+
217
293
  const idleForMs = mostRecentActivityAt > 0 ? now - mostRecentActivityAt : now;
218
294
  const isIdle = mostRecentActivityAt === 0 || idleForMs > idleThresholdMs;
295
+
219
296
  // Trajectory guardrail: only used as a secondary check
220
297
  // If trajectory says there's recent activity but session state says idle,
221
298
  // that's a discrepancy we should note but still trust session state as primary
@@ -225,19 +302,20 @@ export function checkWorkspaceIdle(workspaceDir, options = {}, trajectoryLastAct
225
302
  // Guardrail confirms if trajectory also shows idle or near-idle (>80% of threshold)
226
303
  trajectoryGuardrailConfirmsIdle = trajectoryIdleFor > idleThresholdMs * 0.8;
227
304
  }
228
- let reason;
305
+
306
+ let reason: string;
229
307
  if (mostRecentActivityAt === 0) {
230
308
  reason = 'No active sessions found — workspace is idle';
231
- }
232
- else if (isIdle) {
309
+ } else if (isIdle) {
233
310
  reason = `Most recent activity ${idleForMs}ms ago (>${idleThresholdMs}ms threshold)`;
234
- }
235
- else {
311
+ } else {
236
312
  reason = `Recent activity ${idleForMs}ms ago (<${idleThresholdMs}ms threshold)`;
237
313
  }
314
+
238
315
  if (abandonedSessions.length > 0) {
239
316
  reason += `; ${abandonedSessions.length} abandoned session(s) ignored`;
240
317
  }
318
+
241
319
  return {
242
320
  isIdle,
243
321
  mostRecentActivityAt,
@@ -248,9 +326,11 @@ export function checkWorkspaceIdle(workspaceDir, options = {}, trajectoryLastAct
248
326
  reason,
249
327
  };
250
328
  }
329
+
251
330
  // ---------------------------------------------------------------------------
252
331
  // Cooldown Management
253
332
  // ---------------------------------------------------------------------------
333
+
254
334
  /**
255
335
  * Check if the workspace is currently in a cooldown period.
256
336
  *
@@ -259,14 +339,31 @@ export function checkWorkspaceIdle(workspaceDir, options = {}, trajectoryLastAct
259
339
  * @param options - Cooldown configuration options
260
340
  * @returns CooldownCheckResult
261
341
  */
262
- export function checkCooldown(stateDir, principleId, options = {}) {
263
- const { globalCooldownMs = DEFAULT_GLOBAL_COOLDOWN_MS, principleCooldownMs = DEFAULT_PRINCIPLE_COOLDOWN_MS, maxRunsPerWindow = DEFAULT_MAX_RUNS_PER_WINDOW, quotaWindowMs = DEFAULT_QUOTA_WINDOW_MS, } = options;
342
+ export function checkCooldown(
343
+ stateDir: string,
344
+ principleId?: string,
345
+ options: {
346
+ globalCooldownMs?: number;
347
+ principleCooldownMs?: number;
348
+ maxRunsPerWindow?: number;
349
+ quotaWindowMs?: number;
350
+ } = {}
351
+ ): CooldownCheckResult {
352
+ const {
353
+ globalCooldownMs = DEFAULT_GLOBAL_COOLDOWN_MS,
354
+ principleCooldownMs = DEFAULT_PRINCIPLE_COOLDOWN_MS,
355
+ maxRunsPerWindow = DEFAULT_MAX_RUNS_PER_WINDOW,
356
+ quotaWindowMs = DEFAULT_QUOTA_WINDOW_MS,
357
+ } = options;
358
+
264
359
  const now = Date.now();
265
360
  const state = readStateSync(stateDir);
361
+
266
362
  // Global cooldown check
267
363
  let globalCooldownActive = false;
268
364
  let globalCooldownRemainingMs = 0;
269
- let globalCooldownUntil = null;
365
+ let globalCooldownUntil: string | null = null;
366
+
270
367
  if (state.globalCooldownUntil) {
271
368
  const cooldownEnd = new Date(state.globalCooldownUntil).getTime();
272
369
  if (cooldownEnd > now) {
@@ -275,10 +372,12 @@ export function checkCooldown(stateDir, principleId, options = {}) {
275
372
  globalCooldownUntil = state.globalCooldownUntil;
276
373
  }
277
374
  }
375
+
278
376
  // Principle-specific cooldown check
279
377
  let principleCooldownActive = false;
280
378
  let principleCooldownRemainingMs = 0;
281
- let principleCooldownUntil = null;
379
+ let principleCooldownUntil: string | null = null;
380
+
282
381
  if (principleId && state.principleCooldowns[principleId]) {
283
382
  const cooldownEnd = new Date(state.principleCooldowns[principleId]).getTime();
284
383
  if (cooldownEnd > now) {
@@ -287,13 +386,16 @@ export function checkCooldown(stateDir, principleId, options = {}) {
287
386
  principleCooldownUntil = state.principleCooldowns[principleId];
288
387
  }
289
388
  }
389
+
290
390
  // Quota check: count runs in sliding window
291
391
  const windowStart = now - quotaWindowMs;
292
392
  const recentRuns = state.recentRunTimestamps
293
393
  .map(ts => new Date(ts).getTime())
294
394
  .filter(ts => ts > windowStart);
395
+
295
396
  const quotaExhausted = recentRuns.length >= maxRunsPerWindow;
296
397
  const runsRemaining = Math.max(0, maxRunsPerWindow - recentRuns.length);
398
+
297
399
  return {
298
400
  globalCooldownActive,
299
401
  globalCooldownUntil,
@@ -305,6 +407,7 @@ export function checkCooldown(stateDir, principleId, options = {}) {
305
407
  runsRemaining,
306
408
  };
307
409
  }
410
+
308
411
  /**
309
412
  * Record that a nocturnal run has started.
310
413
  * Updates global cooldown and quota tracking.
@@ -312,27 +415,36 @@ export function checkCooldown(stateDir, principleId, options = {}) {
312
415
  * @param stateDir - State directory
313
416
  * @param principleId - Target principle ID for this run
314
417
  */
315
- export async function recordRunStart(stateDir, principleId) {
418
+ export async function recordRunStart(
419
+ stateDir: string,
420
+ principleId: string
421
+ ): Promise<void> {
316
422
  const state = await readState(stateDir);
317
423
  const now = new Date().toISOString();
424
+
318
425
  state.lastRunAt = now;
319
426
  state.lastRunMeta = {
320
427
  targetPrincipleId: principleId,
321
428
  status: 'skipped', // Will be updated on completion
322
429
  };
430
+
323
431
  // Set global cooldown
324
432
  const cooldownUntil = new Date(Date.now() + DEFAULT_GLOBAL_COOLDOWN_MS).toISOString();
325
433
  state.globalCooldownUntil = cooldownUntil;
434
+
326
435
  // Add to recent runs for quota tracking
327
436
  state.recentRunTimestamps.push(now);
437
+
328
438
  // Prune old timestamps outside the quota window
329
439
  const windowStart = Date.now() - DEFAULT_QUOTA_WINDOW_MS;
330
440
  state.recentRunTimestamps = state.recentRunTimestamps
331
441
  .map(ts => new Date(ts).getTime())
332
442
  .filter(ts => ts > windowStart)
333
443
  .map(ts => new Date(ts).toISOString());
444
+
334
445
  await writeState(stateDir, state);
335
446
  }
447
+
336
448
  /**
337
449
  * Record the outcome of a nocturnal run.
338
450
  *
@@ -340,17 +452,29 @@ export async function recordRunStart(stateDir, principleId) {
340
452
  * @param outcome - 'success', 'failed', or 'skipped'
341
453
  * @param details - Optional details about the run
342
454
  */
343
- export async function recordRunEnd(stateDir, outcome, details) {
455
+ export async function recordRunEnd(
456
+ stateDir: string,
457
+ outcome: 'success' | 'failed' | 'skipped',
458
+ details?: {
459
+ sampleCount?: number;
460
+ reason?: string;
461
+ }
462
+ ): Promise<void> {
344
463
  const state = await readState(stateDir);
345
464
  const now = new Date().toISOString();
465
+
346
466
  if (outcome === 'success') {
347
467
  state.lastSuccessfulRunAt = now;
468
+
348
469
  // Also set per-principle cooldown if we know which principle was targeted
349
470
  if (state.lastRunMeta?.targetPrincipleId) {
350
471
  const pid = state.lastRunMeta.targetPrincipleId;
351
- state.principleCooldowns[pid] = new Date(Date.now() + DEFAULT_PRINCIPLE_COOLDOWN_MS).toISOString();
472
+ state.principleCooldowns[pid] = new Date(
473
+ Date.now() + DEFAULT_PRINCIPLE_COOLDOWN_MS
474
+ ).toISOString();
352
475
  }
353
476
  }
477
+
354
478
  // Update run metadata
355
479
  state.lastRunMeta = {
356
480
  ...state.lastRunMeta,
@@ -358,16 +482,19 @@ export async function recordRunEnd(stateDir, outcome, details) {
358
482
  sampleCount: details?.sampleCount ?? state.lastRunMeta?.sampleCount,
359
483
  reason: details?.reason ?? state.lastRunMeta?.reason,
360
484
  };
485
+
361
486
  // Note: global cooldown remains active (set at run start) - we don't clear it on failure
362
487
  // This prevents rapid retry loops
488
+
363
489
  await writeState(stateDir, state);
364
490
  }
491
+
365
492
  /**
366
493
  * Clear all cooldowns (for testing or admin reset).
367
494
  *
368
495
  * @param stateDir - State directory
369
496
  */
370
- export async function clearAllCooldowns(stateDir) {
497
+ export async function clearAllCooldowns(stateDir: string): Promise<void> {
371
498
  const state = await readState(stateDir);
372
499
  state.globalCooldownUntil = undefined;
373
500
  state.principleCooldowns = {};
@@ -375,15 +502,31 @@ export async function clearAllCooldowns(stateDir) {
375
502
  state.lastRunMeta = undefined;
376
503
  await writeState(stateDir, state);
377
504
  }
505
+
378
506
  /**
379
507
  * Get the current runtime state (for debugging/inspection).
380
508
  *
381
509
  * @param stateDir - State directory
382
510
  * @returns The current NocturnalRuntimeState
383
511
  */
384
- export async function getRuntimeState(stateDir) {
512
+ export async function getRuntimeState(stateDir: string): Promise<NocturnalRuntimeState> {
385
513
  return readState(stateDir);
386
514
  }
515
+
516
+ // ---------------------------------------------------------------------------
517
+ // Convenience: Full Pre-Flight Check
518
+ // ---------------------------------------------------------------------------
519
+
520
+ export interface PreflightCheckResult {
521
+ canRun: boolean;
522
+ idle: IdleCheckResult;
523
+ cooldown: CooldownCheckResult;
524
+ /**
525
+ * Human-readable reasons why run is blocked (if canRun is false)
526
+ */
527
+ blockers: string[];
528
+ }
529
+
387
530
  /**
388
531
  * Combined pre-flight check for whether a nocturnal run should proceed.
389
532
  * Integrates idle + cooldown + quota checks.
@@ -394,26 +537,39 @@ export async function getRuntimeState(stateDir) {
394
537
  * @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
395
538
  * @param idleCheckOverride - Optional override for idle check result (for testing)
396
539
  */
397
- export function checkPreflight(workspaceDir, stateDir, principleId, trajectoryLastActivityAt, idleCheckOverride) {
540
+ export function checkPreflight(
541
+ workspaceDir: string,
542
+ stateDir: string,
543
+ principleId?: string,
544
+ trajectoryLastActivityAt?: number,
545
+ idleCheckOverride?: IdleCheckResult
546
+ ): PreflightCheckResult {
398
547
  const idle = idleCheckOverride ?? checkWorkspaceIdle(workspaceDir, {}, trajectoryLastActivityAt);
399
548
  const cooldown = checkCooldown(stateDir, principleId);
400
- const blockers = [];
549
+
550
+ const blockers: string[] = [];
551
+
401
552
  if (!idle.isIdle) {
402
553
  blockers.push(`Workspace not idle (active for ${idle.idleForMs}ms, threshold=${DEFAULT_IDLE_THRESHOLD_MS}ms)`);
403
554
  }
555
+
404
556
  if (cooldown.globalCooldownActive) {
405
557
  blockers.push(`Global cooldown active until ${cooldown.globalCooldownUntil}`);
406
558
  }
559
+
407
560
  if (cooldown.principleCooldownActive) {
408
561
  blockers.push(`Principle cooldown active until ${cooldown.principleCooldownUntil}`);
409
562
  }
563
+
410
564
  if (cooldown.quotaExhausted) {
411
565
  blockers.push(`Quota exhausted (${DEFAULT_MAX_RUNS_PER_WINDOW} runs per ${DEFAULT_QUOTA_WINDOW_MS / 3600000}h window)`);
412
566
  }
567
+
413
568
  if (idle.abandonedSessionIds.length > 0 && idle.userActiveSessions === 0) {
414
569
  // Only block if ALL sessions are abandoned (meaning workspace truly has no activity)
415
570
  // If some sessions are active, we trust the session-based idle check
416
571
  }
572
+
417
573
  return {
418
574
  canRun: blockers.length === 0,
419
575
  idle,