pulseed 0.4.0 → 0.4.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 (335) hide show
  1. package/dist/base/state/state-manager-goal-write.d.ts +22 -0
  2. package/dist/base/state/state-manager-goal-write.d.ts.map +1 -0
  3. package/dist/base/state/state-manager-goal-write.js +74 -0
  4. package/dist/base/state/state-manager-goal-write.js.map +1 -0
  5. package/dist/base/state/state-manager-wal.d.ts +11 -0
  6. package/dist/base/state/state-manager-wal.d.ts.map +1 -0
  7. package/dist/base/state/state-manager-wal.js +89 -0
  8. package/dist/base/state/state-manager-wal.js.map +1 -0
  9. package/dist/base/state/state-manager.d.ts +1 -4
  10. package/dist/base/state/state-manager.d.ts.map +1 -1
  11. package/dist/base/state/state-manager.js +18 -127
  12. package/dist/base/state/state-manager.js.map +1 -1
  13. package/dist/interface/chat/chat-runner.d.ts +1 -1
  14. package/dist/interface/chat/event-subscriber.d.ts +4 -0
  15. package/dist/interface/chat/event-subscriber.d.ts.map +1 -1
  16. package/dist/interface/chat/event-subscriber.js +49 -2
  17. package/dist/interface/chat/event-subscriber.js.map +1 -1
  18. package/dist/interface/chat/tend-command.d.ts +1 -1
  19. package/dist/interface/cli/cli-command-registry.js +1 -1
  20. package/dist/interface/cli/cli-command-registry.js.map +1 -1
  21. package/dist/interface/cli/commands/chat.js +2 -2
  22. package/dist/interface/cli/commands/daemon.d.ts.map +1 -1
  23. package/dist/interface/cli/commands/daemon.js +87 -44
  24. package/dist/interface/cli/commands/daemon.js.map +1 -1
  25. package/dist/interface/cli/commands/schedule.js +2 -2
  26. package/dist/interface/cli/commands/setup/steps-runtime.js +1 -1
  27. package/dist/interface/cli/ensure-api-key.d.ts +4 -1
  28. package/dist/interface/cli/ensure-api-key.d.ts.map +1 -1
  29. package/dist/interface/cli/ensure-api-key.js +52 -15
  30. package/dist/interface/cli/ensure-api-key.js.map +1 -1
  31. package/dist/interface/tui/app.d.ts +1 -1
  32. package/dist/interface/tui/chat/scroll.d.ts +14 -0
  33. package/dist/interface/tui/chat/scroll.d.ts.map +1 -0
  34. package/dist/interface/tui/chat/scroll.js +46 -0
  35. package/dist/interface/tui/chat/scroll.js.map +1 -0
  36. package/dist/interface/tui/chat/suggestions.d.ts +8 -0
  37. package/dist/interface/tui/chat/suggestions.d.ts.map +1 -0
  38. package/dist/interface/tui/chat/suggestions.js +112 -0
  39. package/dist/interface/tui/chat/suggestions.js.map +1 -0
  40. package/dist/interface/tui/chat/types.d.ts +31 -0
  41. package/dist/interface/tui/chat/types.d.ts.map +1 -0
  42. package/dist/interface/tui/chat/types.js +2 -0
  43. package/dist/interface/tui/chat/types.js.map +1 -0
  44. package/dist/interface/tui/chat/viewport.d.ts +3 -0
  45. package/dist/interface/tui/chat/viewport.d.ts.map +1 -0
  46. package/dist/interface/tui/chat/viewport.js +78 -0
  47. package/dist/interface/tui/chat/viewport.js.map +1 -0
  48. package/dist/interface/tui/chat.d.ts +5 -49
  49. package/dist/interface/tui/chat.d.ts.map +1 -1
  50. package/dist/interface/tui/chat.js +7 -236
  51. package/dist/interface/tui/chat.js.map +1 -1
  52. package/dist/interface/tui/entry.js +3 -3
  53. package/dist/interface/tui/use-loop.d.ts +1 -1
  54. package/dist/orchestrator/execution/task/task-lifecycle.d.ts +3 -0
  55. package/dist/orchestrator/execution/task/task-lifecycle.d.ts.map +1 -1
  56. package/dist/orchestrator/execution/task/task-lifecycle.js +3 -0
  57. package/dist/orchestrator/execution/task/task-lifecycle.js.map +1 -1
  58. package/dist/orchestrator/execution/task/task-verifier-rules.d.ts.map +1 -1
  59. package/dist/orchestrator/execution/task/task-verifier-rules.js +34 -2
  60. package/dist/orchestrator/execution/task/task-verifier-rules.js.map +1 -1
  61. package/dist/orchestrator/execution/task/task-verifier-types.d.ts +2 -0
  62. package/dist/orchestrator/execution/task/task-verifier-types.d.ts.map +1 -1
  63. package/dist/orchestrator/loop/checkpoint-manager-loop.d.ts +1 -1
  64. package/dist/orchestrator/loop/checkpoint-manager-loop.d.ts.map +1 -1
  65. package/dist/orchestrator/loop/core-loop/capability.d.ts +22 -0
  66. package/dist/orchestrator/loop/core-loop/capability.d.ts.map +1 -0
  67. package/dist/orchestrator/loop/core-loop/capability.js +151 -0
  68. package/dist/orchestrator/loop/core-loop/capability.js.map +1 -0
  69. package/dist/orchestrator/loop/core-loop/contracts.d.ts +245 -0
  70. package/dist/orchestrator/loop/core-loop/contracts.d.ts.map +1 -0
  71. package/dist/orchestrator/loop/core-loop/contracts.js +40 -0
  72. package/dist/orchestrator/loop/core-loop/contracts.js.map +1 -0
  73. package/dist/orchestrator/loop/core-loop/control.d.ts +27 -0
  74. package/dist/orchestrator/loop/core-loop/control.d.ts.map +1 -0
  75. package/dist/orchestrator/loop/core-loop/control.js +72 -0
  76. package/dist/orchestrator/loop/core-loop/control.js.map +1 -0
  77. package/dist/orchestrator/loop/core-loop/learning.d.ts +31 -0
  78. package/dist/orchestrator/loop/core-loop/learning.d.ts.map +1 -0
  79. package/dist/orchestrator/loop/core-loop/learning.js +92 -0
  80. package/dist/orchestrator/loop/core-loop/learning.js.map +1 -0
  81. package/dist/orchestrator/loop/core-loop/preparation.d.ts +63 -0
  82. package/dist/orchestrator/loop/core-loop/preparation.d.ts.map +1 -0
  83. package/dist/orchestrator/loop/core-loop/preparation.js +362 -0
  84. package/dist/orchestrator/loop/core-loop/preparation.js.map +1 -0
  85. package/dist/orchestrator/loop/core-loop/task-cycle.d.ts +29 -0
  86. package/dist/orchestrator/loop/core-loop/task-cycle.d.ts.map +1 -0
  87. package/dist/orchestrator/loop/core-loop/task-cycle.js +674 -0
  88. package/dist/orchestrator/loop/core-loop/task-cycle.js.map +1 -0
  89. package/dist/orchestrator/loop/core-loop-capability.d.ts +1 -24
  90. package/dist/orchestrator/loop/core-loop-capability.d.ts.map +1 -1
  91. package/dist/orchestrator/loop/core-loop-capability.js +1 -153
  92. package/dist/orchestrator/loop/core-loop-capability.js.map +1 -1
  93. package/dist/orchestrator/loop/core-loop-learning.d.ts +1 -34
  94. package/dist/orchestrator/loop/core-loop-learning.d.ts.map +1 -1
  95. package/dist/orchestrator/loop/core-loop-learning.js +1 -95
  96. package/dist/orchestrator/loop/core-loop-learning.js.map +1 -1
  97. package/dist/orchestrator/loop/core-loop-phases-b.d.ts +1 -31
  98. package/dist/orchestrator/loop/core-loop-phases-b.d.ts.map +1 -1
  99. package/dist/orchestrator/loop/core-loop-phases-b.js +1 -669
  100. package/dist/orchestrator/loop/core-loop-phases-b.js.map +1 -1
  101. package/dist/orchestrator/loop/core-loop-phases-c.d.ts +1 -26
  102. package/dist/orchestrator/loop/core-loop-phases-c.d.ts.map +1 -1
  103. package/dist/orchestrator/loop/core-loop-phases-c.js +1 -71
  104. package/dist/orchestrator/loop/core-loop-phases-c.js.map +1 -1
  105. package/dist/orchestrator/loop/core-loop-phases.d.ts +1 -68
  106. package/dist/orchestrator/loop/core-loop-phases.d.ts.map +1 -1
  107. package/dist/orchestrator/loop/core-loop-phases.js +1 -367
  108. package/dist/orchestrator/loop/core-loop-phases.js.map +1 -1
  109. package/dist/orchestrator/loop/core-loop-types.d.ts +1 -244
  110. package/dist/orchestrator/loop/core-loop-types.d.ts.map +1 -1
  111. package/dist/orchestrator/loop/core-loop-types.js +1 -39
  112. package/dist/orchestrator/loop/core-loop-types.js.map +1 -1
  113. package/dist/orchestrator/loop/core-loop.d.ts +3 -3
  114. package/dist/orchestrator/loop/core-loop.d.ts.map +1 -1
  115. package/dist/orchestrator/loop/core-loop.js +6 -6
  116. package/dist/orchestrator/loop/core-loop.js.map +1 -1
  117. package/dist/orchestrator/loop/loop-report-helper.d.ts +1 -1
  118. package/dist/orchestrator/loop/loop-report-helper.d.ts.map +1 -1
  119. package/dist/orchestrator/loop/parallel-dispatch.d.ts +2 -2
  120. package/dist/orchestrator/loop/parallel-dispatch.d.ts.map +1 -1
  121. package/dist/orchestrator/loop/post-loop-hooks.d.ts +1 -1
  122. package/dist/orchestrator/loop/post-loop-hooks.d.ts.map +1 -1
  123. package/dist/orchestrator/loop/tree-loop-runner.d.ts +1 -1
  124. package/dist/orchestrator/loop/tree-loop-runner.d.ts.map +1 -1
  125. package/dist/orchestrator/loop/tree-loop-runner.js +1 -1
  126. package/dist/orchestrator/loop/tree-loop-runner.js.map +1 -1
  127. package/dist/platform/dream/dream-schedule-suggestions.d.ts +1 -1
  128. package/dist/platform/drive/drive-system.d.ts +8 -0
  129. package/dist/platform/drive/drive-system.d.ts.map +1 -1
  130. package/dist/platform/drive/drive-system.js +39 -22
  131. package/dist/platform/drive/drive-system.js.map +1 -1
  132. package/dist/platform/observation/engine/observe-context.d.ts +4 -0
  133. package/dist/platform/observation/engine/observe-context.d.ts.map +1 -0
  134. package/dist/platform/observation/engine/observe-context.js +26 -0
  135. package/dist/platform/observation/engine/observe-context.js.map +1 -0
  136. package/dist/platform/observation/engine/observe-datasource-stage.d.ts +33 -0
  137. package/dist/platform/observation/engine/observe-datasource-stage.d.ts.map +1 -0
  138. package/dist/platform/observation/engine/observe-datasource-stage.js +66 -0
  139. package/dist/platform/observation/engine/observe-datasource-stage.js.map +1 -0
  140. package/dist/platform/observation/engine/observe-llm-stage.d.ts +25 -0
  141. package/dist/platform/observation/engine/observe-llm-stage.d.ts.map +1 -0
  142. package/dist/platform/observation/engine/observe-llm-stage.js +79 -0
  143. package/dist/platform/observation/engine/observe-llm-stage.js.map +1 -0
  144. package/dist/platform/observation/engine/observe-precheck.d.ts +21 -0
  145. package/dist/platform/observation/engine/observe-precheck.d.ts.map +1 -0
  146. package/dist/platform/observation/engine/observe-precheck.js +51 -0
  147. package/dist/platform/observation/engine/observe-precheck.js.map +1 -0
  148. package/dist/platform/observation/engine/observe-self-report.d.ts +18 -0
  149. package/dist/platform/observation/engine/observe-self-report.d.ts.map +1 -0
  150. package/dist/platform/observation/engine/observe-self-report.js +26 -0
  151. package/dist/platform/observation/engine/observe-self-report.js.map +1 -0
  152. package/dist/platform/observation/engine/observe-tool-stage.d.ts +21 -0
  153. package/dist/platform/observation/engine/observe-tool-stage.d.ts.map +1 -0
  154. package/dist/platform/observation/engine/observe-tool-stage.js +49 -0
  155. package/dist/platform/observation/engine/observe-tool-stage.js.map +1 -0
  156. package/dist/platform/observation/observation-engine.d.ts.map +1 -1
  157. package/dist/platform/observation/observation-engine.js +67 -246
  158. package/dist/platform/observation/observation-engine.js.map +1 -1
  159. package/dist/prompt/context-assembler.d.ts +61 -13
  160. package/dist/prompt/context-assembler.d.ts.map +1 -1
  161. package/dist/prompt/context-assembler.js +18 -3
  162. package/dist/prompt/context-assembler.js.map +1 -1
  163. package/dist/runtime/approval-broker.d.ts.map +1 -1
  164. package/dist/runtime/approval-broker.js +1 -0
  165. package/dist/runtime/approval-broker.js.map +1 -1
  166. package/dist/runtime/command-dispatcher.d.ts +35 -0
  167. package/dist/runtime/command-dispatcher.d.ts.map +1 -0
  168. package/dist/runtime/command-dispatcher.js +145 -0
  169. package/dist/runtime/command-dispatcher.js.map +1 -0
  170. package/dist/runtime/daemon/client.d.ts +67 -0
  171. package/dist/runtime/daemon/client.d.ts.map +1 -0
  172. package/dist/runtime/daemon/client.js +330 -0
  173. package/dist/runtime/daemon/client.js.map +1 -0
  174. package/dist/runtime/daemon/health.d.ts +31 -0
  175. package/dist/runtime/daemon/health.d.ts.map +1 -0
  176. package/dist/runtime/daemon/health.js +113 -0
  177. package/dist/runtime/daemon/health.js.map +1 -0
  178. package/dist/runtime/daemon/index.d.ts +9 -0
  179. package/dist/runtime/daemon/index.d.ts.map +1 -0
  180. package/dist/runtime/daemon/index.js +8 -0
  181. package/dist/runtime/daemon/index.js.map +1 -0
  182. package/dist/runtime/daemon/maintenance.d.ts +47 -0
  183. package/dist/runtime/daemon/maintenance.d.ts.map +1 -0
  184. package/dist/runtime/daemon/maintenance.js +230 -0
  185. package/dist/runtime/daemon/maintenance.js.map +1 -0
  186. package/dist/runtime/daemon/persistence.d.ts +20 -0
  187. package/dist/runtime/daemon/persistence.d.ts.map +1 -0
  188. package/dist/runtime/daemon/persistence.js +112 -0
  189. package/dist/runtime/daemon/persistence.js.map +1 -0
  190. package/dist/runtime/daemon/runner-lifecycle.d.ts +29 -0
  191. package/dist/runtime/daemon/runner-lifecycle.d.ts.map +1 -0
  192. package/dist/runtime/daemon/runner-lifecycle.js +56 -0
  193. package/dist/runtime/daemon/runner-lifecycle.js.map +1 -0
  194. package/dist/runtime/daemon/runner.d.ts +229 -0
  195. package/dist/runtime/daemon/runner.d.ts.map +1 -0
  196. package/dist/runtime/daemon/runner.js +875 -0
  197. package/dist/runtime/daemon/runner.js.map +1 -0
  198. package/dist/runtime/daemon/runtime-ownership.d.ts +30 -0
  199. package/dist/runtime/daemon/runtime-ownership.d.ts.map +1 -0
  200. package/dist/runtime/daemon/runtime-ownership.js +132 -0
  201. package/dist/runtime/daemon/runtime-ownership.js.map +1 -0
  202. package/dist/runtime/daemon/signals.d.ts +17 -0
  203. package/dist/runtime/daemon/signals.d.ts.map +1 -0
  204. package/dist/runtime/daemon/signals.js +31 -0
  205. package/dist/runtime/daemon/signals.js.map +1 -0
  206. package/dist/runtime/daemon/types.d.ts +8 -0
  207. package/dist/runtime/daemon/types.d.ts.map +1 -0
  208. package/dist/runtime/daemon/types.js +2 -0
  209. package/dist/runtime/daemon/types.js.map +1 -0
  210. package/dist/runtime/daemon-client.d.ts +1 -55
  211. package/dist/runtime/daemon-client.d.ts.map +1 -1
  212. package/dist/runtime/daemon-client.js +1 -297
  213. package/dist/runtime/daemon-client.js.map +1 -1
  214. package/dist/runtime/daemon-health.d.ts +1 -30
  215. package/dist/runtime/daemon-health.d.ts.map +1 -1
  216. package/dist/runtime/daemon-health.js +1 -112
  217. package/dist/runtime/daemon-health.js.map +1 -1
  218. package/dist/runtime/daemon-runner-lifecycle.d.ts +2 -0
  219. package/dist/runtime/daemon-runner-lifecycle.d.ts.map +1 -0
  220. package/dist/runtime/daemon-runner-lifecycle.js +2 -0
  221. package/dist/runtime/daemon-runner-lifecycle.js.map +1 -0
  222. package/dist/runtime/daemon-runner.d.ts +1 -231
  223. package/dist/runtime/daemon-runner.d.ts.map +1 -1
  224. package/dist/runtime/daemon-runner.js +1 -1042
  225. package/dist/runtime/daemon-runner.js.map +1 -1
  226. package/dist/runtime/daemon-runtime-ownership.d.ts +2 -0
  227. package/dist/runtime/daemon-runtime-ownership.d.ts.map +1 -0
  228. package/dist/runtime/daemon-runtime-ownership.js +2 -0
  229. package/dist/runtime/daemon-runtime-ownership.js.map +1 -0
  230. package/dist/runtime/daemon-signals.d.ts +1 -16
  231. package/dist/runtime/daemon-signals.d.ts.map +1 -1
  232. package/dist/runtime/daemon-signals.js +1 -30
  233. package/dist/runtime/daemon-signals.js.map +1 -1
  234. package/dist/runtime/event/dispatcher.d.ts +34 -0
  235. package/dist/runtime/event/dispatcher.d.ts.map +1 -0
  236. package/dist/runtime/event/dispatcher.js +124 -0
  237. package/dist/runtime/event/dispatcher.js.map +1 -0
  238. package/dist/runtime/event/index.d.ts +5 -0
  239. package/dist/runtime/event/index.d.ts.map +1 -0
  240. package/dist/runtime/event/index.js +5 -0
  241. package/dist/runtime/event/index.js.map +1 -0
  242. package/dist/runtime/event/server-snapshot-reader.d.ts +31 -0
  243. package/dist/runtime/event/server-snapshot-reader.d.ts.map +1 -0
  244. package/dist/runtime/event/server-snapshot-reader.js +94 -0
  245. package/dist/runtime/event/server-snapshot-reader.js.map +1 -0
  246. package/dist/runtime/event/server-sse.d.ts +25 -0
  247. package/dist/runtime/event/server-sse.d.ts.map +1 -0
  248. package/dist/runtime/event/server-sse.js +149 -0
  249. package/dist/runtime/event/server-sse.js.map +1 -0
  250. package/dist/runtime/event/server.d.ts +114 -0
  251. package/dist/runtime/event/server.d.ts.map +1 -0
  252. package/dist/runtime/event/server.js +651 -0
  253. package/dist/runtime/event/server.js.map +1 -0
  254. package/dist/runtime/event-dispatcher.d.ts +2 -0
  255. package/dist/runtime/event-dispatcher.d.ts.map +1 -0
  256. package/dist/runtime/event-dispatcher.js +2 -0
  257. package/dist/runtime/event-dispatcher.js.map +1 -0
  258. package/dist/runtime/event-server-snapshot-reader.d.ts +2 -0
  259. package/dist/runtime/event-server-snapshot-reader.d.ts.map +1 -0
  260. package/dist/runtime/event-server-snapshot-reader.js +2 -0
  261. package/dist/runtime/event-server-snapshot-reader.js.map +1 -0
  262. package/dist/runtime/event-server-sse.d.ts +2 -0
  263. package/dist/runtime/event-server-sse.d.ts.map +1 -0
  264. package/dist/runtime/event-server-sse.js +2 -0
  265. package/dist/runtime/event-server-sse.js.map +1 -0
  266. package/dist/runtime/event-server.d.ts +1 -91
  267. package/dist/runtime/event-server.d.ts.map +1 -1
  268. package/dist/runtime/event-server.js +1 -698
  269. package/dist/runtime/event-server.js.map +1 -1
  270. package/dist/runtime/executor/loop-supervisor.d.ts +9 -5
  271. package/dist/runtime/executor/loop-supervisor.d.ts.map +1 -1
  272. package/dist/runtime/executor/loop-supervisor.js +59 -76
  273. package/dist/runtime/executor/loop-supervisor.js.map +1 -1
  274. package/dist/runtime/gateway/http-channel-adapter.d.ts +1 -1
  275. package/dist/runtime/plugin-loader.d.ts +1 -1
  276. package/dist/runtime/queue/index.d.ts +0 -4
  277. package/dist/runtime/queue/index.d.ts.map +1 -1
  278. package/dist/runtime/queue/index.js +0 -2
  279. package/dist/runtime/queue/index.js.map +1 -1
  280. package/dist/runtime/queue/journal-backed-queue.d.ts.map +1 -1
  281. package/dist/runtime/queue/journal-backed-queue.js +2 -0
  282. package/dist/runtime/queue/journal-backed-queue.js.map +1 -1
  283. package/dist/runtime/schedule/engine-layers.d.ts +44 -0
  284. package/dist/runtime/schedule/engine-layers.d.ts.map +1 -0
  285. package/dist/runtime/schedule/engine-layers.js +433 -0
  286. package/dist/runtime/schedule/engine-layers.js.map +1 -0
  287. package/dist/runtime/schedule/engine.d.ts +82 -0
  288. package/dist/runtime/schedule/engine.d.ts.map +1 -0
  289. package/dist/runtime/schedule/engine.js +480 -0
  290. package/dist/runtime/schedule/engine.js.map +1 -0
  291. package/dist/runtime/schedule/index.d.ts +5 -0
  292. package/dist/runtime/schedule/index.d.ts.map +1 -0
  293. package/dist/runtime/schedule/index.js +5 -0
  294. package/dist/runtime/schedule/index.js.map +1 -0
  295. package/dist/runtime/schedule/presets.d.ts +536 -0
  296. package/dist/runtime/schedule/presets.d.ts.map +1 -0
  297. package/dist/runtime/schedule/presets.js +166 -0
  298. package/dist/runtime/schedule/presets.js.map +1 -0
  299. package/dist/runtime/schedule/source.d.ts +65 -0
  300. package/dist/runtime/schedule/source.d.ts.map +1 -0
  301. package/dist/runtime/schedule/source.js +16 -0
  302. package/dist/runtime/schedule/source.js.map +1 -0
  303. package/dist/runtime/schedule-engine-layers.d.ts +1 -43
  304. package/dist/runtime/schedule-engine-layers.d.ts.map +1 -1
  305. package/dist/runtime/schedule-engine-layers.js +1 -432
  306. package/dist/runtime/schedule-engine-layers.js.map +1 -1
  307. package/dist/runtime/schedule-engine.d.ts +1 -81
  308. package/dist/runtime/schedule-engine.d.ts.map +1 -1
  309. package/dist/runtime/schedule-engine.js +1 -479
  310. package/dist/runtime/schedule-engine.js.map +1 -1
  311. package/dist/runtime/schedule-presets.d.ts +1 -535
  312. package/dist/runtime/schedule-presets.d.ts.map +1 -1
  313. package/dist/runtime/schedule-presets.js +1 -165
  314. package/dist/runtime/schedule-presets.js.map +1 -1
  315. package/dist/runtime/schedule-source.d.ts +1 -64
  316. package/dist/runtime/schedule-source.d.ts.map +1 -1
  317. package/dist/runtime/schedule-source.js +1 -15
  318. package/dist/runtime/schedule-source.js.map +1 -1
  319. package/dist/runtime/types/daemon.d.ts.map +1 -1
  320. package/dist/runtime/types/daemon.js +2 -1
  321. package/dist/runtime/types/daemon.js.map +1 -1
  322. package/dist/runtime/watchdog.d.ts +44 -0
  323. package/dist/runtime/watchdog.d.ts.map +1 -0
  324. package/dist/runtime/watchdog.js +185 -0
  325. package/dist/runtime/watchdog.js.map +1 -0
  326. package/dist/tools/builtin/index.d.ts +1 -1
  327. package/dist/tools/schedule/CreateScheduleTool/CreateScheduleTool.d.ts +1 -1
  328. package/dist/tools/schedule/CreateScheduleTool/CreateScheduleTool.js +1 -1
  329. package/dist/tools/schedule/GetScheduleTool/GetScheduleTool.d.ts +1 -1
  330. package/dist/tools/schedule/ListSchedulesTool/ListSchedulesTool.d.ts +1 -1
  331. package/dist/tools/schedule/PauseScheduleTool/PauseScheduleTool.d.ts +1 -1
  332. package/dist/tools/schedule/RemoveScheduleTool/RemoveScheduleTool.d.ts +1 -1
  333. package/dist/tools/schedule/ResumeScheduleTool/ResumeScheduleTool.d.ts +1 -1
  334. package/dist/tools/schedule/UpdateScheduleTool/UpdateScheduleTool.d.ts +1 -1
  335. package/package.json +1 -1
@@ -1,1043 +1,2 @@
1
- import * as fsp from "node:fs/promises";
2
- import * as path from "node:path";
3
- import { writeJsonFileAtomic, readJsonFileOrNull } from "../base/utils/json-io.js";
4
- import { EventServer } from "./event-server.js";
5
- import { DaemonConfigSchema, DaemonStateSchema } from "../base/types/daemon.js";
6
- import { getInternalIdentityPrefix } from "../base/config/identity-loader.js";
7
- import { z } from "zod";
8
- import { generateCronEntry } from "./daemon-signals.js";
9
- import { rotateDaemonLog, calculateAdaptiveInterval as calcAdaptiveInterval } from "./daemon-health.js";
10
- import { HttpChannelAdapter } from "./gateway/index.js";
11
- import { createEnvelope } from "./types/envelope.js";
12
- import { LoopSupervisor } from "./executor/index.js";
13
- import { PulSeedEventSchema } from "../base/types/drive.js";
14
- import { ApprovalStore, OutboxStore, RuntimeHealthStore, createRuntimeStorePaths } from "./store/index.js";
15
- import { LeaderLockManager } from "./leader-lock-manager.js";
16
- import { GoalLeaseManager } from "./goal-lease-manager.js";
17
- import { JournalBackedQueue } from "./queue/journal-backed-queue.js";
18
- import { QueueClaimSweeper } from "./queue/queue-claim-sweeper.js";
19
- import { ApprovalBroker } from "./approval-broker.js";
20
- // Re-exports for callers that imported these from daemon-runner
21
- export { generateCronEntry } from "./daemon-signals.js";
22
- export { rotateDaemonLog, calculateAdaptiveInterval } from "./daemon-health.js";
23
- export class DaemonRunner {
24
- coreLoop;
25
- driveSystem;
26
- stateManager;
27
- pidManager;
28
- logger;
29
- config;
30
- running = false;
31
- shuttingDown = false;
32
- state;
33
- baseDir;
34
- logDir;
35
- logPath;
36
- shutdownHandler = null;
37
- eventServer;
38
- approvalFn;
39
- sleepAbortController = null;
40
- currentGoalIds = [];
41
- currentLoopIndex = 0;
42
- lastProactiveTickAt = 0;
43
- llmClient;
44
- reportingEngine;
45
- cronScheduler;
46
- scheduleEngine;
47
- consecutiveIdleCycles = 0;
48
- gateway;
49
- eventBus;
50
- commandBus;
51
- supervisor = null;
52
- cronScheduleInterval = null;
53
- shutdownResolve = null;
54
- deps;
55
- runtimeRoot = null;
56
- approvalStore = null;
57
- outboxStore = null;
58
- runtimeHealthStore = null;
59
- leaderLockManager = null;
60
- goalLeaseManager = null;
61
- journalQueue = null;
62
- queueClaimSweeper = null;
63
- approvalBroker = null;
64
- constructor(deps) {
65
- this.deps = deps;
66
- this.coreLoop = deps.coreLoop;
67
- this.driveSystem = deps.driveSystem;
68
- this.stateManager = deps.stateManager;
69
- this.pidManager = deps.pidManager;
70
- this.logger = deps.logger;
71
- this.eventServer = deps.eventServer;
72
- this.llmClient = deps.llmClient;
73
- this.reportingEngine = deps.reportingEngine;
74
- this.cronScheduler = deps.cronScheduler;
75
- this.scheduleEngine = deps.scheduleEngine;
76
- this.gateway = deps.gateway;
77
- this.eventBus = deps.eventBus;
78
- this.commandBus = deps.commandBus;
79
- this.supervisor = deps.supervisor ?? null;
80
- this.lastProactiveTickAt = Date.now();
81
- // Parse config with defaults via DaemonConfigSchema.parse()
82
- this.config = DaemonConfigSchema.parse(deps.config ?? {});
83
- // Resolve base directory from stateManager
84
- this.baseDir = this.stateManager.getBaseDir();
85
- // Pre-compute log paths used by rotateLog
86
- this.logDir = path.join(this.baseDir, this.config.log_dir);
87
- this.logPath = path.join(this.logDir, "pulseed.log");
88
- if (this.config.runtime_journal_v2) {
89
- this.runtimeRoot = this.resolveRuntimeRoot();
90
- const runtimePaths = createRuntimeStorePaths(this.runtimeRoot);
91
- this.approvalStore = new ApprovalStore(runtimePaths);
92
- this.outboxStore = new OutboxStore(runtimePaths);
93
- this.runtimeHealthStore = new RuntimeHealthStore(runtimePaths);
94
- this.leaderLockManager = new LeaderLockManager(this.runtimeRoot);
95
- this.goalLeaseManager = new GoalLeaseManager(this.runtimeRoot);
96
- this.approvalBroker = new ApprovalBroker({
97
- store: this.approvalStore,
98
- logger: this.logger,
99
- });
100
- this.journalQueue = new JournalBackedQueue({
101
- journalPath: path.join(this.runtimeRoot, "queue.json"),
102
- });
103
- this.queueClaimSweeper = new QueueClaimSweeper({
104
- queue: this.journalQueue,
105
- });
106
- }
107
- // Initialize daemon state
108
- this.state = DaemonStateSchema.parse({
109
- pid: process.pid,
110
- started_at: new Date().toISOString(),
111
- last_loop_at: null,
112
- loop_count: 0,
113
- active_goals: [],
114
- status: "stopped",
115
- crash_count: 0,
116
- last_error: null,
117
- });
118
- }
119
- resolveRuntimeRoot() {
120
- const configuredRoot = this.config.runtime_root;
121
- if (!configuredRoot || configuredRoot.trim() === "") {
122
- return path.join(this.baseDir, "runtime");
123
- }
124
- return path.isAbsolute(configuredRoot)
125
- ? configuredRoot
126
- : path.resolve(this.baseDir, configuredRoot);
127
- }
128
- // ─── Public API ───
129
- /**
130
- * Start daemon loop for given goals.
131
- * Throws if daemon is already running.
132
- */
133
- async start(goalIds) {
134
- // 1. Check if already running
135
- if (await this.pidManager.isRunning()) {
136
- const info = await this.pidManager.readPID();
137
- throw new Error(`Daemon is already running (PID ${info?.pid ?? "unknown"}). ` +
138
- `Stop it first or remove the PID file at: ${this.pidManager.getPath()}`);
139
- }
140
- // 2. Rotate log if needed, then check for crash recovery marker
141
- await this.rotateLog();
142
- await this.checkCrashRecovery();
143
- await this.initializeRuntimeFoundation();
144
- // 2b. Publish PID only after startup prerequisites succeed.
145
- await this.pidManager.writePID();
146
- // 2c. Start EventServer (always-on) and file watcher
147
- if (!this.eventServer) {
148
- const esPort = this.config.event_server_port ?? 41700;
149
- this.eventServer = new EventServer(this.driveSystem, {
150
- port: esPort,
151
- stateManager: this.stateManager,
152
- }, this.logger);
153
- }
154
- if (this.approvalBroker) {
155
- this.approvalBroker.setBroadcast((eventType, data) => {
156
- this.eventServer?.broadcast(eventType, data);
157
- });
158
- this.eventServer.setApprovalBroker?.(this.approvalBroker);
159
- }
160
- this.eventServer.setCommandEnvelopeHook?.(async (envelope) => this.handleInboundEnvelope(envelope));
161
- if (this.gateway) {
162
- // Phase A: Route through Gateway → Envelope → writeEvent
163
- const httpAdapter = new HttpChannelAdapter(this.eventServer);
164
- this.gateway.registerAdapter(httpAdapter);
165
- this.gateway.onEnvelope(async (envelope) => this.handleInboundEnvelope(envelope));
166
- // Wire onHighPriority to abort sleep — done via the abortSleep() public method.
167
- // Callers who construct buses should pass: onHighPriority: () => daemon.abortSleep()
168
- // The daemon provides abortSleep() below for this purpose.
169
- await this.gateway.start();
170
- this.logger.info("Gateway started with HTTP adapter", { port: this.eventServer.getPort() });
171
- }
172
- else {
173
- // Legacy path: direct EventServer (no Gateway)
174
- await this.eventServer.start();
175
- this.eventServer.startFileWatcher();
176
- this.logger.info("EventServer started", { port: this.eventServer.getPort() });
177
- }
178
- // Wire approval bridge if not already provided
179
- if (!this.approvalFn && this.eventServer) {
180
- const es = this.eventServer;
181
- this.approvalFn = async (task) => {
182
- const goalId = String(task["goal_id"] ?? "unknown");
183
- const description = String(task["description"] ?? "");
184
- const action = String(task["action"] ?? "");
185
- const taskId = String(task["id"] ?? "");
186
- if (this.reportingEngine) {
187
- try {
188
- await this.reportingEngine.generateNotification("approval_required", {
189
- goalId,
190
- message: description || action || taskId || "Task approval required",
191
- details: [`task_id: ${taskId || "(none)"}`, `action: ${action || "(none)"}`].join("\n"),
192
- });
193
- }
194
- catch (err) {
195
- this.logger.warn("Approval notification dispatch failed", {
196
- goalId,
197
- error: err instanceof Error ? err.message : String(err),
198
- });
199
- }
200
- }
201
- return es.requestApproval(goalId, {
202
- id: taskId,
203
- description,
204
- action,
205
- });
206
- };
207
- }
208
- // Start heartbeat (every 30s)
209
- const heartbeatInterval = setInterval(() => {
210
- if (this.eventServer && this.state.status === "running") {
211
- this.eventServer.broadcast("daemon_status", {
212
- status: this.state.status,
213
- activeGoals: this.state.active_goals,
214
- loopCount: this.state.loop_count,
215
- uptime: Date.now() - new Date(this.state.started_at).getTime(),
216
- });
217
- }
218
- }, 30_000);
219
- this.driveSystem.startWatcher((event) => this.onEventReceived(event));
220
- // 3. Set up signal handlers for graceful shutdown
221
- this.shuttingDown = false;
222
- const shutdownTimeout = this.config.crash_recovery.graceful_shutdown_timeout_ms ?? 30_000;
223
- let forceStopTimer = null;
224
- const shutdown = () => {
225
- if (this.shuttingDown)
226
- return;
227
- this.shuttingDown = true;
228
- this.logger.info("Shutting down gracefully...");
229
- // Abort current sleep so the loop exits promptly
230
- this.sleepAbortController?.abort();
231
- // Start a timeout to force-stop if graceful shutdown takes too long
232
- forceStopTimer = setTimeout(() => {
233
- this.logger.warn(`Graceful shutdown timeout (${shutdownTimeout}ms) exceeded, forcing stop`);
234
- this.running = false;
235
- }, shutdownTimeout);
236
- };
237
- this.shutdownHandler = shutdown;
238
- process.on("SIGTERM", shutdown);
239
- process.on("SIGINT", shutdown);
240
- // 4. Restore state from previous interrupted run
241
- const mergedGoalIds = await this.restoreState(goalIds);
242
- // 5. Save initial daemon state
243
- this.running = true;
244
- this.currentGoalIds = mergedGoalIds;
245
- this.currentLoopIndex = 0;
246
- this.state = DaemonStateSchema.parse({
247
- pid: process.pid,
248
- started_at: new Date().toISOString(),
249
- last_loop_at: null,
250
- loop_count: 0,
251
- active_goals: mergedGoalIds,
252
- status: "running",
253
- crash_count: 0,
254
- last_error: null,
255
- });
256
- await this.saveDaemonState();
257
- // 5b. Write "running" shutdown marker (crash detection on next startup)
258
- await this.writeShutdownMarker({
259
- goal_ids: mergedGoalIds,
260
- loop_index: 0,
261
- timestamp: new Date().toISOString(),
262
- reason: "startup",
263
- state: "running",
264
- });
265
- // 6. Log start
266
- this.logger.info("Daemon started", {
267
- pid: process.pid,
268
- goals: mergedGoalIds,
269
- check_interval_ms: this.config.check_interval_ms,
270
- });
271
- const sweepResult = this.queueClaimSweeper?.sweep();
272
- if (sweepResult && (sweepResult.reclaimed > 0 || sweepResult.deadlettered > 0)) {
273
- this.logger.info("Recovered stale runtime claims on startup", {
274
- reclaimed: sweepResult.reclaimed,
275
- deadlettered: sweepResult.deadlettered,
276
- expiredClaimTokens: sweepResult.expiredClaimTokens,
277
- });
278
- }
279
- this.queueClaimSweeper?.start();
280
- // 7. Create supervisor if not already provided and runtime execution wiring is configured
281
- if (!this.supervisor && (this.eventBus || (this.journalQueue && this.goalLeaseManager))) {
282
- const factory = this.deps.coreLoopFactory ?? (() => this.coreLoop);
283
- this.supervisor = new LoopSupervisor({
284
- coreLoopFactory: factory,
285
- eventBus: this.eventBus,
286
- journalQueue: this.journalQueue ?? undefined,
287
- goalLeaseManager: this.goalLeaseManager ?? undefined,
288
- driveSystem: this.driveSystem,
289
- stateManager: this.stateManager,
290
- logger: this.logger,
291
- onEscalation: (goalId, crashCount, lastError) => {
292
- this.logger.error(`Goal ${goalId} suspended after ${crashCount} crashes: ${lastError}`);
293
- },
294
- }, { iterationsPerCycle: this.config.iterations_per_cycle });
295
- }
296
- await this.saveRuntimeHealthSnapshot(this.supervisor && this.journalQueue && this.goalLeaseManager
297
- ? "execution_ownership_durable"
298
- : "foundation_only", {
299
- gateway: this.gateway || this.eventServer ? "ok" : "degraded",
300
- queue: this.journalQueue ? "ok" : "degraded",
301
- leases: this.goalLeaseManager ? "ok" : "degraded",
302
- approval: this.approvalStore ? "ok" : "degraded",
303
- outbox: this.outboxStore ? "ok" : "degraded",
304
- supervisor: this.supervisor && this.journalQueue && this.goalLeaseManager ? "ok" : "degraded",
305
- });
306
- // 8. Run main loop — supervisor mode when supervisor is injected via deps,
307
- // fallback to sequential runLoop otherwise (preserves backward compat for
308
- // tests that provide eventBus without a supervisor)
309
- let cleanupHandled = false;
310
- try {
311
- if (this.supervisor) {
312
- // Supervisor handles goal execution; cron/schedule must also run in this mode.
313
- await this.supervisor.start(mergedGoalIds);
314
- // Run cron/schedule processing once immediately, then on a periodic interval.
315
- const cronIntervalMs = this.config.check_interval_ms;
316
- await this.processCronTasks();
317
- await this.processScheduleEntries();
318
- this.cronScheduleInterval = setInterval(async () => {
319
- if (this.shuttingDown)
320
- return;
321
- await this.processCronTasks();
322
- await this.processScheduleEntries();
323
- }, cronIntervalMs);
324
- // Block until stop() is called.
325
- await new Promise((resolve) => {
326
- this.shutdownResolve = resolve;
327
- // If already stopped before we get here, resolve immediately.
328
- if (!this.running)
329
- resolve();
330
- });
331
- }
332
- else {
333
- // Fallback: sequential mode
334
- await this.runLoop(mergedGoalIds);
335
- cleanupHandled = true;
336
- }
337
- }
338
- finally {
339
- // Cancel the force-stop timer if it's still pending
340
- if (forceStopTimer !== null) {
341
- clearTimeout(forceStopTimer);
342
- forceStopTimer = null;
343
- }
344
- this.queueClaimSweeper?.stop();
345
- // Remove signal handlers
346
- if (this.shutdownHandler) {
347
- process.removeListener("SIGTERM", this.shutdownHandler);
348
- process.removeListener("SIGINT", this.shutdownHandler);
349
- this.shutdownHandler = null;
350
- }
351
- // Stop file watcher and EventServer
352
- clearInterval(heartbeatInterval);
353
- if (this.cronScheduleInterval !== null) {
354
- clearInterval(this.cronScheduleInterval);
355
- this.cronScheduleInterval = null;
356
- }
357
- await this.supervisor?.shutdown();
358
- this.driveSystem.stopWatcher();
359
- if (this.gateway) {
360
- await this.gateway.stop();
361
- this.logger.info("Gateway stopped");
362
- }
363
- else if (this.eventServer) {
364
- this.eventServer.stopFileWatcher();
365
- await this.eventServer.stop();
366
- this.logger.info("EventServer stopped");
367
- }
368
- if (!cleanupHandled) {
369
- await this.cleanup();
370
- }
371
- }
372
- }
373
- async initializeRuntimeFoundation() {
374
- if (!this.config.runtime_journal_v2)
375
- return;
376
- await Promise.all([
377
- this.approvalStore?.ensureReady(),
378
- this.outboxStore?.ensureReady(),
379
- this.runtimeHealthStore?.ensureReady(),
380
- ]);
381
- await this.saveRuntimeHealthSnapshot("foundation_only", {
382
- gateway: "degraded",
383
- queue: "degraded",
384
- leases: "ok",
385
- approval: "ok",
386
- outbox: "ok",
387
- supervisor: "degraded",
388
- });
389
- this.logger.info("Runtime journal foundation initialized", {
390
- runtime_root: this.runtimeRoot,
391
- queue_path: this.runtimeRoot ? path.join(this.runtimeRoot, "queue.json") : undefined,
392
- });
393
- }
394
- async saveRuntimeHealthSnapshot(phase, components) {
395
- if (!this.config.runtime_journal_v2)
396
- return;
397
- const status = Object.values(components).every((value) => value === "ok") ? "ok" : "degraded";
398
- await this.runtimeHealthStore?.saveSnapshot({
399
- status,
400
- leader: false,
401
- checked_at: Date.now(),
402
- components,
403
- details: {
404
- runtime_journal_v2: true,
405
- runtime_root: this.runtimeRoot,
406
- phase,
407
- },
408
- });
409
- }
410
- /** Expose approvalFn for callers (e.g. cmdStart) to wire into TaskLifecycle */
411
- getApprovalFn() {
412
- return this.approvalFn;
413
- }
414
- /**
415
- * Abort the current sleep cycle immediately.
416
- * Intended for use as the onHighPriority callback when constructing EventBus/CommandBus:
417
- * new EventBus({ onHighPriority: () => daemon.abortSleep() })
418
- */
419
- abortSleep() {
420
- this.sleepAbortController?.abort();
421
- }
422
- /**
423
- * Signal daemon to stop after current iteration completes.
424
- * Saves interrupted_goals so they can be restored on next start.
425
- */
426
- stop() {
427
- this.running = false;
428
- this.shutdownResolve?.();
429
- this.sleepAbortController?.abort();
430
- this.state.status = "stopping";
431
- // Save current active_goals as interrupted_goals for state restoration
432
- this.state.interrupted_goals = [...this.state.active_goals];
433
- // Do NOT persist here — cleanup() will save the final state after the loop exits.
434
- // Calling saveDaemonState() here would race with cleanup()'s save and corrupt the file.
435
- this.logger.info("Stop requested — daemon will stop after current iteration");
436
- }
437
- // ─── Private: Main Loop ───
438
- /**
439
- * Main daemon loop. Runs until this.running is false or a critical error occurs.
440
- */
441
- async runLoop(goalIds) {
442
- while (this.running && !this.shuttingDown) {
443
- try {
444
- // 1. Determine which goals need activation
445
- const activeGoals = await this.determineActiveGoals(goalIds);
446
- if (activeGoals.length === 0) {
447
- this.logger.info("No goals need activation this cycle", {
448
- checked: goalIds.length,
449
- });
450
- }
451
- // 2. Execute loop for each active goal
452
- for (const goalId of activeGoals) {
453
- if (!this.running)
454
- break;
455
- this.logger.info(`Running loop for goal: ${goalId}`);
456
- try {
457
- const iterationsPerCycle = this.config.iterations_per_cycle ?? 1;
458
- const result = await this.coreLoop.run(goalId, { maxIterations: iterationsPerCycle });
459
- this.state.loop_count++;
460
- this.currentLoopIndex = this.state.loop_count;
461
- this.state.last_loop_at = new Date().toISOString();
462
- this.logger.info(`Loop completed for goal: ${goalId}`, {
463
- status: result.finalStatus,
464
- iterations: result.totalIterations,
465
- });
466
- if (this.eventServer) {
467
- const goal = await this.stateManager.loadGoal(goalId).catch(() => null);
468
- this.eventServer.broadcast("iteration_complete", {
469
- goalId,
470
- loopCount: this.state.loop_count,
471
- status: goal?.status ?? "unknown",
472
- });
473
- }
474
- }
475
- catch (err) {
476
- this.handleLoopError(goalId, err);
477
- }
478
- // Bail out of goal iteration if crash limit exceeded
479
- if (!this.running)
480
- break;
481
- }
482
- // 3. Save state
483
- await this.saveDaemonState();
484
- if (this.eventServer) {
485
- this.eventServer.broadcast("daemon_status", {
486
- status: this.state.status,
487
- activeGoals: this.state.active_goals,
488
- loopCount: this.state.loop_count,
489
- lastLoopAt: this.state.last_loop_at,
490
- });
491
- }
492
- // 3b. Process due cron-scheduled tasks
493
- await this.processCronTasks();
494
- // 3b2. Process schedule engine entries
495
- await this.processScheduleEntries();
496
- // 3c. Expire old cron tasks periodically (every 100 cycles)
497
- if (this.state.loop_count > 0 && this.state.loop_count % 100 === 0) {
498
- await this.expireCronTasks();
499
- }
500
- // 4. Proactive tick: fire every cycle (not only when idle) so long-running goals
501
- // do not block proactive actions indefinitely.
502
- if (this.running) {
503
- await this.proactiveTick();
504
- }
505
- // 5. Track idle cycles for adaptive sleep
506
- if (activeGoals.length > 0) {
507
- this.consecutiveIdleCycles = 0;
508
- }
509
- else {
510
- this.consecutiveIdleCycles++;
511
- }
512
- // 6. Wait for next check interval
513
- if (this.running) {
514
- const baseIntervalMs = this.getNextInterval(goalIds);
515
- const maxGapScore = await this.getMaxGapScore(goalIds);
516
- const intervalMs = this.calculateAdaptiveInterval(baseIntervalMs, activeGoals.length, maxGapScore, this.consecutiveIdleCycles);
517
- this.logger.info(`Sleeping for ${intervalMs}ms until next check`);
518
- await this.sleep(intervalMs);
519
- }
520
- }
521
- catch (err) {
522
- await this.handleCriticalError(err);
523
- }
524
- }
525
- // Cleanup after loop exits
526
- await this.cleanup();
527
- }
528
- // ─── Private: Goal Activation ───
529
- /**
530
- * Determine which goals should be activated this cycle.
531
- * Uses DriveSystem.shouldActivate() for each goal, then sorts by priority.
532
- */
533
- async determineActiveGoals(goalIds) {
534
- const eligibleIds = [];
535
- const scores = new Map();
536
- for (const goalId of goalIds) {
537
- if (await this.driveSystem.shouldActivate(goalId)) {
538
- eligibleIds.push(goalId);
539
- // Load goal to get a rough priority signal (gap or drive score not available here)
540
- // Use schedule consecutive_actions as a tiebreaker — more urgent goals first
541
- const schedule = await this.driveSystem.getSchedule(goalId);
542
- // Higher consecutive_actions = more urgent (stalled goal). Use inverse of next_check_at
543
- // as a proxy: goals that are most overdue rank highest.
544
- const nextCheckAt = schedule
545
- ? new Date(schedule.next_check_at).getTime()
546
- : 0;
547
- // Earlier next_check_at means more overdue → assign higher (inverted) score
548
- scores.set(goalId, -nextCheckAt);
549
- }
550
- }
551
- // Sort by priority: most overdue first
552
- return this.driveSystem.prioritizeGoals(eligibleIds, scores);
553
- }
554
- // ─── Private: Interval Calculation ───
555
- /**
556
- * Calculate the next check interval in milliseconds.
557
- * Uses per-goal override from config.goal_intervals if configured,
558
- * otherwise falls back to config.check_interval_ms.
559
- * Returns the minimum interval across all goals (so the daemon checks
560
- * as soon as the earliest goal is due).
561
- */
562
- getNextInterval(goalIds) {
563
- const goalIntervals = this.config.goal_intervals;
564
- if (!goalIntervals || goalIds.length === 0) {
565
- return this.config.check_interval_ms;
566
- }
567
- let minInterval = this.config.check_interval_ms;
568
- for (const goalId of goalIds) {
569
- const override = goalIntervals[goalId];
570
- if (override !== undefined && override < minInterval) {
571
- minInterval = override;
572
- }
573
- }
574
- return minInterval;
575
- }
576
- // ─── Private: Error Handling ───
577
- /**
578
- * Handle a non-critical loop error for a single goal.
579
- * Increments crash_count and stops daemon if max_retries exceeded.
580
- */
581
- handleLoopError(goalId, err) {
582
- this.state.last_error = err instanceof Error ? err.message : String(err);
583
- this.state.crash_count++;
584
- this.logger.error(`Loop error for goal ${goalId}`, {
585
- error: this.state.last_error,
586
- crash_count: this.state.crash_count,
587
- max_retries: this.config.crash_recovery.max_retries,
588
- });
589
- // If crash count exceeds max_retries, stop daemon
590
- if (this.state.crash_count >= this.config.crash_recovery.max_retries) {
591
- this.logger.error(`Max crash retries (${this.config.crash_recovery.max_retries}) exceeded, stopping daemon`);
592
- this.running = false;
593
- }
594
- }
595
- /**
596
- * Handle a critical daemon-level error (outer loop catch).
597
- * Marks state as crashed and stops the loop.
598
- */
599
- async handleCriticalError(err) {
600
- const msg = err instanceof Error ? err.message : String(err);
601
- this.logger.error("Critical daemon error", { error: msg });
602
- this.state.status = "crashed";
603
- this.state.last_error = msg;
604
- await this.saveDaemonState();
605
- this.running = false;
606
- }
607
- // ─── Private: State Persistence ───
608
- /**
609
- * Save daemon state to {baseDir}/daemon-state.json atomically.
610
- */
611
- async saveDaemonState() {
612
- const statePath = path.join(this.baseDir, "daemon-state.json");
613
- try {
614
- await writeJsonFileAtomic(statePath, this.state);
615
- }
616
- catch (err) {
617
- // Non-fatal — log but don't crash the daemon
618
- this.logger.warn("Failed to save daemon state", {
619
- error: err instanceof Error ? err.message : String(err),
620
- });
621
- }
622
- }
623
- /**
624
- * Load daemon state from {baseDir}/daemon-state.json.
625
- * Returns null if the file doesn't exist or fails to parse.
626
- */
627
- async loadDaemonState() {
628
- const statePath = path.join(this.baseDir, "daemon-state.json");
629
- const data = await readJsonFileOrNull(statePath);
630
- if (data === null)
631
- return null;
632
- try {
633
- return DaemonStateSchema.parse(data);
634
- }
635
- catch {
636
- return null;
637
- }
638
- }
639
- /**
640
- * Restore state from a previous interrupted run.
641
- * Merges interrupted_goals from daemon-state.json with the given goalIds (deduped).
642
- * Returns the merged goal ID array.
643
- */
644
- async restoreState(goalIds) {
645
- const saved = await this.loadDaemonState();
646
- if (!saved || !saved.interrupted_goals || saved.interrupted_goals.length === 0) {
647
- return goalIds;
648
- }
649
- const merged = Array.from(new Set([...goalIds, ...saved.interrupted_goals]));
650
- if (merged.length > goalIds.length) {
651
- this.logger.info("Restored interrupted goals from previous run", {
652
- interrupted: saved.interrupted_goals,
653
- merged,
654
- });
655
- }
656
- return merged;
657
- }
658
- // ─── Private: Cleanup ───
659
- /**
660
- * Perform cleanup after the loop exits: update state, remove PID file, log.
661
- * Also writes "clean_shutdown" marker to enable crash-vs-clean detection on next startup.
662
- */
663
- async cleanup() {
664
- // Only set to "stopped" if not already "crashed"
665
- const wasCrashed = this.state.status === "crashed";
666
- if (!wasCrashed) {
667
- this.state.status = "stopped";
668
- if (this.state.interrupted_goals === undefined) {
669
- this.state.interrupted_goals = [...this.state.active_goals];
670
- }
671
- }
672
- await this.saveDaemonState();
673
- await this.pidManager.cleanup();
674
- // Write clean shutdown marker (async, atomic)
675
- const markerPath = path.join(this.baseDir, "shutdown-state.json");
676
- const marker = {
677
- goal_ids: this.currentGoalIds,
678
- loop_index: this.currentLoopIndex,
679
- timestamp: new Date().toISOString(),
680
- reason: wasCrashed ? "max_retries" : "stop",
681
- state: wasCrashed ? "running" : "clean_shutdown",
682
- };
683
- try {
684
- await writeJsonFileAtomic(markerPath, marker);
685
- }
686
- catch (err) {
687
- this.logger.warn("Failed to write shutdown marker", {
688
- error: err instanceof Error ? err.message : String(err),
689
- });
690
- }
691
- this.logger.info("Daemon stopped", {
692
- loop_count: this.state.loop_count,
693
- crash_count: this.state.crash_count,
694
- });
695
- }
696
- // ─── Private: Cron Scheduler ───
697
- /**
698
- * Process due cron-scheduled tasks.
699
- * Logs each task, executes based on type, and marks as fired.
700
- */
701
- async processCronTasks() {
702
- if (!this.cronScheduler)
703
- return;
704
- try {
705
- const dueTasks = await this.cronScheduler.getDueTasks();
706
- for (const task of dueTasks) {
707
- this.logger.info(`Cron task due: ${task.id} (type=${task.type})`, {
708
- cron: task.cron,
709
- type: task.type,
710
- });
711
- const envelope = createEnvelope({
712
- type: "event",
713
- name: "cron_task_due",
714
- source: "cron-scheduler",
715
- priority: "normal",
716
- payload: task,
717
- dedupe_key: `cron-${task.id}`,
718
- });
719
- if (!this.acceptRuntimeEnvelope(envelope)) {
720
- continue;
721
- }
722
- if (this.eventBus) {
723
- // Push to eventBus — markFired happens when the envelope is consumed, not at push time
724
- this.eventBus.push(envelope);
725
- this.logger.info(`Cron task enqueued to eventBus: ${task.id}`);
726
- }
727
- else {
728
- // Fallback: no eventBus — execute and markFired directly (legacy behavior)
729
- try {
730
- // Log the task prompt and type for observability; actual execution
731
- // varies by type but all are currently handled as fire-and-log.
732
- this.logger.info(`Executing cron task: ${task.type}`, {
733
- prompt: task.prompt,
734
- });
735
- await this.cronScheduler.markFired(task.id);
736
- this.logger.info(`Cron task fired: ${task.id}`);
737
- }
738
- catch (err) {
739
- this.logger.warn(`Cron task ${task.id} failed`, {
740
- error: err instanceof Error ? err.message : String(err),
741
- });
742
- // Still mark as fired to avoid retry-storms
743
- await this.cronScheduler.markFired(task.id);
744
- }
745
- }
746
- }
747
- }
748
- catch (err) {
749
- // Non-fatal — cron errors should not crash the daemon
750
- this.logger.warn("Failed to process cron tasks", {
751
- error: err instanceof Error ? err.message : String(err),
752
- });
753
- }
754
- }
755
- /**
756
- * Process due schedule engine entries.
757
- */
758
- async processScheduleEntries() {
759
- if (!this.scheduleEngine)
760
- return;
761
- try {
762
- const results = await this.scheduleEngine.tick();
763
- for (const result of results) {
764
- if (result.status === "error") {
765
- this.logger?.warn?.(`Schedule entry ${result.entry_id} failed: ${result.error_message}`);
766
- }
767
- else {
768
- // Record schedule activation in the runtime journal before any in-memory fanout.
769
- const goalId = result["goal_id"];
770
- if (!goalId) {
771
- this.logger.warn("schedule_activated envelope missing goal_id", { entry_id: result["entry_id"] });
772
- }
773
- const envelope = createEnvelope({
774
- type: "event",
775
- name: "schedule_activated",
776
- source: "schedule-engine",
777
- goal_id: goalId,
778
- priority: "normal",
779
- payload: result,
780
- dedupe_key: result.entry_id,
781
- });
782
- if (!this.acceptRuntimeEnvelope(envelope)) {
783
- continue;
784
- }
785
- if (this.eventBus) {
786
- this.eventBus.push(envelope);
787
- }
788
- }
789
- }
790
- }
791
- catch (error) {
792
- this.logger?.error?.("Failed to process schedule entries", { error: error instanceof Error ? error.message : String(error) });
793
- }
794
- }
795
- /**
796
- * Expire old non-permanent cron tasks.
797
- */
798
- async expireCronTasks() {
799
- if (!this.cronScheduler)
800
- return;
801
- try {
802
- await this.cronScheduler.expireOldTasks();
803
- this.logger.debug("Expired old cron tasks");
804
- }
805
- catch (err) {
806
- this.logger.warn("Failed to expire cron tasks", {
807
- error: err instanceof Error ? err.message : String(err),
808
- });
809
- }
810
- }
811
- // ─── Private: Sleep ───
812
- /**
813
- * Sleep for the given number of milliseconds.
814
- * Can be aborted early via sleepAbortController (e.g. when an event arrives).
815
- */
816
- sleep(ms) {
817
- this.sleepAbortController = new AbortController();
818
- const abortController = this.sleepAbortController;
819
- return new Promise((resolve) => {
820
- const timer = setTimeout(resolve, ms);
821
- abortController.signal.addEventListener("abort", () => {
822
- clearTimeout(timer);
823
- resolve();
824
- });
825
- }).finally(() => {
826
- this.sleepAbortController = null;
827
- });
828
- }
829
- // ─── Private: Event Handling ───
830
- /**
831
- * Called when a file-watcher event arrives from DriveSystem.
832
- * Aborts the current sleep so the loop runs immediately.
833
- */
834
- onEventReceived(event) {
835
- this.logger.info("Event received, triggering immediate loop", {
836
- event_type: event.type,
837
- });
838
- this.sleepAbortController?.abort();
839
- }
840
- acceptRuntimeEnvelope(envelope) {
841
- if (!this.journalQueue)
842
- return true;
843
- const result = this.journalQueue.accept(envelope);
844
- if (result.accepted) {
845
- return true;
846
- }
847
- this.logger.info("Runtime journal skipped envelope", {
848
- id: envelope.id,
849
- name: envelope.name,
850
- type: envelope.type,
851
- duplicate: result.duplicate,
852
- runtime_root: this.runtimeRoot,
853
- });
854
- return false;
855
- }
856
- async handleInboundEnvelope(envelope) {
857
- if (!this.acceptRuntimeEnvelope(envelope)) {
858
- return;
859
- }
860
- if (envelope.type === "command") {
861
- if (this.commandBus) {
862
- this.commandBus.push(envelope);
863
- }
864
- return;
865
- }
866
- if (envelope.type === "event" && this.eventBus) {
867
- this.eventBus.push(envelope);
868
- return;
869
- }
870
- // Fallback: no bus configured — keep legacy driveSystem.writeEvent() behavior
871
- const payload = envelope.payload;
872
- try {
873
- const event = PulSeedEventSchema.parse(payload);
874
- await this.driveSystem.writeEvent(event);
875
- }
876
- catch (err) {
877
- this.logger.error("Gateway: failed to process envelope", {
878
- id: envelope.id,
879
- error: String(err),
880
- });
881
- }
882
- }
883
- // ─── Private: Proactive Tick ───
884
- // Zod schema for the LLM proactive action response
885
- static ProactiveResponseSchema = z.object({
886
- action: z.enum(["suggest_goal", "investigate", "preemptive_check", "sleep"]),
887
- details: z.record(z.string(), z.unknown()).optional(),
888
- });
889
- /**
890
- * Ask the LLM for a proactive action when no goals were activated this cycle.
891
- * Fires only if proactive_mode is enabled and enough time has passed since last tick.
892
- * Errors are caught and logged — they never affect the daemon loop.
893
- */
894
- async proactiveTick() {
895
- if (!this.config.proactive_mode)
896
- return;
897
- if (!this.llmClient)
898
- return;
899
- if (Date.now() - this.lastProactiveTickAt < this.config.proactive_interval_ms)
900
- return;
901
- try {
902
- // Build a brief summary of all tracked goals from daemon state
903
- const goalSummaries = this.state.active_goals.length > 0
904
- ? this.state.active_goals.map((id) => `- ${id}`).join("\n")
905
- : "(no active goals)";
906
- const prompt = `${getInternalIdentityPrefix("proactive engine")} Given the current state of all goals:\n${goalSummaries}\n\nDecide what action to take:\n- "suggest_goal": A new goal should be created (provide title + description)\n- "investigate": Something needs investigation (provide what and why)\n- "preemptive_check": Run a pre-emptive observation (provide goal_id)\n- "sleep": Nothing needs attention right now\n\nRespond with JSON: { "action": "...", "details": { ... } }`;
907
- const response = await this.llmClient.sendMessage([{ role: "user", content: prompt }], { model_tier: "light" });
908
- const parsed = DaemonRunner.ProactiveResponseSchema.safeParse(this.llmClient.parseJSON(response.content, DaemonRunner.ProactiveResponseSchema));
909
- if (!parsed.success) {
910
- this.logger.warn("Proactive tick: failed to parse LLM response", {
911
- raw: response.content,
912
- error: parsed.error.message,
913
- });
914
- this.lastProactiveTickAt = Date.now();
915
- return;
916
- }
917
- const { action, details } = parsed.data;
918
- if (action === "sleep") {
919
- this.logger.debug("Proactive tick: LLM decided to sleep");
920
- }
921
- else {
922
- this.logger.info(`Proactive tick: action=${action}`, { details });
923
- }
924
- }
925
- catch (err) {
926
- this.logger.warn("Proactive tick: LLM error (ignored)", {
927
- error: err instanceof Error ? err.message : String(err),
928
- });
929
- }
930
- this.lastProactiveTickAt = Date.now();
931
- }
932
- // ─── Private: Adaptive Sleep ───
933
- /**
934
- * Get the highest gap score across all active goals.
935
- * Falls back to 0 if no gap data is available.
936
- */
937
- async getMaxGapScore(goalIds) {
938
- let max = 0;
939
- for (const goalId of goalIds) {
940
- try {
941
- const schedule = await this.driveSystem.getSchedule(goalId);
942
- if (schedule && typeof schedule["last_gap_score"] === "number") {
943
- const score = schedule["last_gap_score"];
944
- if (score > max)
945
- max = score;
946
- }
947
- }
948
- catch {
949
- // Non-fatal — just use 0 for this goal
950
- }
951
- }
952
- return max;
953
- }
954
- /**
955
- * Thin wrapper delegating to the standalone calculateAdaptiveInterval function.
956
- * Passes this.config.adaptive_sleep as the config parameter.
957
- * Kept as a class method so tests can call daemon.calculateAdaptiveInterval(...).
958
- */
959
- calculateAdaptiveInterval(baseInterval, goalsActivatedThisCycle, maxGapScore, consecutiveIdleCycles) {
960
- return calcAdaptiveInterval(baseInterval, goalsActivatedThisCycle, maxGapScore, consecutiveIdleCycles, this.config.adaptive_sleep);
961
- }
962
- // ─── Private: Shutdown Marker ───
963
- /**
964
- * Write shutdown-state.json to baseDir (async, atomic).
965
- */
966
- async writeShutdownMarker(marker) {
967
- const markerPath = path.join(this.baseDir, "shutdown-state.json");
968
- try {
969
- await writeJsonFileAtomic(markerPath, marker);
970
- }
971
- catch (err) {
972
- this.logger.warn("Failed to write shutdown marker", {
973
- error: err instanceof Error ? err.message : String(err),
974
- });
975
- }
976
- }
977
- /**
978
- * Read shutdown-state.json from baseDir.
979
- * Returns null if file doesn't exist or fails to parse.
980
- */
981
- async readShutdownMarker() {
982
- const markerPath = path.join(this.baseDir, "shutdown-state.json");
983
- return readJsonFileOrNull(markerPath);
984
- }
985
- /**
986
- * Delete shutdown-state.json (after successful resume).
987
- */
988
- async deleteShutdownMarker() {
989
- const markerPath = path.join(this.baseDir, "shutdown-state.json");
990
- try {
991
- await fsp.unlink(markerPath);
992
- }
993
- catch {
994
- // File may not exist — ignore
995
- }
996
- }
997
- /**
998
- * Check for a previous shutdown marker and log recovery information.
999
- * Called at startup before the main loop begins.
1000
- */
1001
- async checkCrashRecovery() {
1002
- const marker = await this.readShutdownMarker();
1003
- if (!marker)
1004
- return;
1005
- if (marker.state === "clean_shutdown") {
1006
- this.logger.info("Resuming from clean shutdown", {
1007
- previous_loop_index: marker.loop_index,
1008
- previous_goals: marker.goal_ids,
1009
- shutdown_at: marker.timestamp,
1010
- });
1011
- }
1012
- else {
1013
- // state === "running" — previous instance did not shut down cleanly
1014
- this.logger.warn("Recovering from crash — previous instance did not shut down cleanly", {
1015
- previous_loop_index: marker.loop_index,
1016
- previous_goals: marker.goal_ids,
1017
- last_seen_at: marker.timestamp,
1018
- });
1019
- }
1020
- // Delete the marker; we'll write a fresh "running" marker in start()
1021
- await this.deleteShutdownMarker();
1022
- }
1023
- // ─── Log Rotation (delegates to daemon-health) ───
1024
- /**
1025
- * Rotate the main log file if it exceeds the configured size limit.
1026
- * Delegates to rotateDaemonLog() with explicit config params.
1027
- * Called at daemon startup.
1028
- */
1029
- async rotateLog() {
1030
- const maxSizeBytes = this.config.log_rotation.max_size_mb * 1024 * 1024;
1031
- const maxFiles = this.config.log_rotation.max_files;
1032
- await rotateDaemonLog(this.logPath, this.logDir, maxSizeBytes, maxFiles, this.logger);
1033
- }
1034
- // ─── Static Utilities (delegates to daemon-signals) ───
1035
- /**
1036
- * Generate a crontab entry that runs `pulseed run --goal <goalId>` on a schedule.
1037
- * Delegates to the standalone generateCronEntry() function in daemon-signals.ts.
1038
- */
1039
- static generateCronEntry(goalId, intervalMinutes = 60) {
1040
- return generateCronEntry(goalId, intervalMinutes);
1041
- }
1042
- }
1
+ export * from "./daemon/runner.js";
1043
2
  //# sourceMappingURL=daemon-runner.js.map