@aitne/daemon 0.1.3 → 0.1.6

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 (421) hide show
  1. package/dist/adapters/notification-manager.d.ts +12 -0
  2. package/dist/adapters/notification-manager.d.ts.map +1 -1
  3. package/dist/adapters/notification-manager.js +39 -1
  4. package/dist/adapters/notification-manager.js.map +1 -1
  5. package/dist/adapters/whatsapp-adapter.d.ts.map +1 -1
  6. package/dist/adapters/whatsapp-adapter.js +0 -1
  7. package/dist/adapters/whatsapp-adapter.js.map +1 -1
  8. package/dist/api/integration-route-gate.d.ts +15 -11
  9. package/dist/api/integration-route-gate.d.ts.map +1 -1
  10. package/dist/api/integration-route-gate.js +60 -23
  11. package/dist/api/integration-route-gate.js.map +1 -1
  12. package/dist/api/json-body.d.ts +22 -7
  13. package/dist/api/json-body.d.ts.map +1 -1
  14. package/dist/api/json-body.js +27 -8
  15. package/dist/api/json-body.js.map +1 -1
  16. package/dist/api/routes/agent.d.ts.map +1 -1
  17. package/dist/api/routes/agent.js +25 -0
  18. package/dist/api/routes/agent.js.map +1 -1
  19. package/dist/api/routes/backends.d.ts.map +1 -1
  20. package/dist/api/routes/backends.js +96 -1
  21. package/dist/api/routes/backends.js.map +1 -1
  22. package/dist/api/routes/books.js +1 -1
  23. package/dist/api/routes/books.js.map +1 -1
  24. package/dist/api/routes/commands.d.ts.map +1 -1
  25. package/dist/api/routes/commands.js +16 -13
  26. package/dist/api/routes/commands.js.map +1 -1
  27. package/dist/api/routes/context.d.ts.map +1 -1
  28. package/dist/api/routes/context.js +26 -3
  29. package/dist/api/routes/context.js.map +1 -1
  30. package/dist/api/routes/dashboard.d.ts.map +1 -1
  31. package/dist/api/routes/dashboard.js +103 -5
  32. package/dist/api/routes/dashboard.js.map +1 -1
  33. package/dist/api/routes/fs.d.ts +23 -0
  34. package/dist/api/routes/fs.d.ts.map +1 -0
  35. package/dist/api/routes/fs.js +156 -0
  36. package/dist/api/routes/fs.js.map +1 -0
  37. package/dist/api/routes/fs.logic.d.ts +62 -0
  38. package/dist/api/routes/fs.logic.d.ts.map +1 -0
  39. package/dist/api/routes/fs.logic.js +137 -0
  40. package/dist/api/routes/fs.logic.js.map +1 -0
  41. package/dist/api/routes/github.d.ts.map +1 -1
  42. package/dist/api/routes/github.js +38 -5
  43. package/dist/api/routes/github.js.map +1 -1
  44. package/dist/api/routes/health.d.ts.map +1 -1
  45. package/dist/api/routes/health.js +4 -2
  46. package/dist/api/routes/health.js.map +1 -1
  47. package/dist/api/routes/integrations.d.ts +35 -6
  48. package/dist/api/routes/integrations.d.ts.map +1 -1
  49. package/dist/api/routes/integrations.js +192 -15
  50. package/dist/api/routes/integrations.js.map +1 -1
  51. package/dist/api/routes/mail.d.ts.map +1 -1
  52. package/dist/api/routes/mail.js +112 -46
  53. package/dist/api/routes/mail.js.map +1 -1
  54. package/dist/api/routes/metrics.d.ts +1 -0
  55. package/dist/api/routes/metrics.d.ts.map +1 -1
  56. package/dist/api/routes/metrics.js +24 -0
  57. package/dist/api/routes/metrics.js.map +1 -1
  58. package/dist/api/routes/observations.d.ts.map +1 -1
  59. package/dist/api/routes/observations.js +696 -30
  60. package/dist/api/routes/observations.js.map +1 -1
  61. package/dist/api/routes/setup-migrate.d.ts +9 -1
  62. package/dist/api/routes/setup-migrate.d.ts.map +1 -1
  63. package/dist/api/routes/setup-migrate.js +4 -2
  64. package/dist/api/routes/setup-migrate.js.map +1 -1
  65. package/dist/api/routes/skills.d.ts +9 -1
  66. package/dist/api/routes/skills.d.ts.map +1 -1
  67. package/dist/api/routes/skills.js +77 -17
  68. package/dist/api/routes/skills.js.map +1 -1
  69. package/dist/api/routes/voice.d.ts.map +1 -1
  70. package/dist/api/routes/voice.js +62 -4
  71. package/dist/api/routes/voice.js.map +1 -1
  72. package/dist/api/routes/wiki.d.ts +4 -0
  73. package/dist/api/routes/wiki.d.ts.map +1 -0
  74. package/dist/api/routes/wiki.js +1075 -0
  75. package/dist/api/routes/wiki.js.map +1 -0
  76. package/dist/api/server.d.ts +13 -0
  77. package/dist/api/server.d.ts.map +1 -1
  78. package/dist/api/server.js +27 -1
  79. package/dist/api/server.js.map +1 -1
  80. package/dist/bootstrap/adapters.d.ts +109 -0
  81. package/dist/bootstrap/adapters.d.ts.map +1 -0
  82. package/dist/bootstrap/adapters.js +237 -0
  83. package/dist/bootstrap/adapters.js.map +1 -0
  84. package/dist/bootstrap/catchup.d.ts +23 -0
  85. package/dist/bootstrap/catchup.d.ts.map +1 -0
  86. package/dist/bootstrap/catchup.js +124 -0
  87. package/dist/bootstrap/catchup.js.map +1 -0
  88. package/dist/bootstrap/schedule-helpers.d.ts +18 -0
  89. package/dist/bootstrap/schedule-helpers.d.ts.map +1 -0
  90. package/dist/bootstrap/schedule-helpers.js +96 -0
  91. package/dist/bootstrap/schedule-helpers.js.map +1 -0
  92. package/dist/bootstrap/services.d.ts +60 -0
  93. package/dist/bootstrap/services.d.ts.map +1 -0
  94. package/dist/bootstrap/services.js +209 -0
  95. package/dist/bootstrap/services.js.map +1 -0
  96. package/dist/config.d.ts.map +1 -1
  97. package/dist/config.js +26 -0
  98. package/dist/config.js.map +1 -1
  99. package/dist/core/agent-core.d.ts +25 -0
  100. package/dist/core/agent-core.d.ts.map +1 -1
  101. package/dist/core/agent-core.js.map +1 -1
  102. package/dist/core/backends/backend-router.d.ts +28 -1
  103. package/dist/core/backends/backend-router.d.ts.map +1 -1
  104. package/dist/core/backends/backend-router.js +58 -4
  105. package/dist/core/backends/backend-router.js.map +1 -1
  106. package/dist/core/backends/claude-auth.d.ts +70 -0
  107. package/dist/core/backends/claude-auth.d.ts.map +1 -0
  108. package/dist/core/backends/claude-auth.js +198 -0
  109. package/dist/core/backends/claude-auth.js.map +1 -0
  110. package/dist/core/backends/claude-code-core.d.ts +47 -119
  111. package/dist/core/backends/claude-code-core.d.ts.map +1 -1
  112. package/dist/core/backends/claude-code-core.js +166 -1561
  113. package/dist/core/backends/claude-code-core.js.map +1 -1
  114. package/dist/core/backends/claude-delegated.d.ts +86 -0
  115. package/dist/core/backends/claude-delegated.d.ts.map +1 -0
  116. package/dist/core/backends/claude-delegated.js +801 -0
  117. package/dist/core/backends/claude-delegated.js.map +1 -0
  118. package/dist/core/backends/claude-errors.d.ts +39 -0
  119. package/dist/core/backends/claude-errors.d.ts.map +1 -0
  120. package/dist/core/backends/claude-errors.js +71 -0
  121. package/dist/core/backends/claude-errors.js.map +1 -0
  122. package/dist/core/backends/claude-probe.d.ts +103 -0
  123. package/dist/core/backends/claude-probe.d.ts.map +1 -0
  124. package/dist/core/backends/claude-probe.js +336 -0
  125. package/dist/core/backends/claude-probe.js.map +1 -0
  126. package/dist/core/backends/claude-tool-collection.d.ts +135 -0
  127. package/dist/core/backends/claude-tool-collection.d.ts.map +1 -0
  128. package/dist/core/backends/claude-tool-collection.js +1093 -0
  129. package/dist/core/backends/claude-tool-collection.js.map +1 -0
  130. package/dist/core/backends/codex-core.d.ts.map +1 -1
  131. package/dist/core/backends/codex-core.js +36 -0
  132. package/dist/core/backends/codex-core.js.map +1 -1
  133. package/dist/core/backends/gemini-cli-core.d.ts +45 -5
  134. package/dist/core/backends/gemini-cli-core.d.ts.map +1 -1
  135. package/dist/core/backends/gemini-cli-core.js +146 -36
  136. package/dist/core/backends/gemini-cli-core.js.map +1 -1
  137. package/dist/core/backends/plan-presets.d.ts +3 -1
  138. package/dist/core/backends/plan-presets.d.ts.map +1 -1
  139. package/dist/core/backends/plan-presets.js +42 -2
  140. package/dist/core/backends/plan-presets.js.map +1 -1
  141. package/dist/core/backends/prompt-utils.d.ts +1 -0
  142. package/dist/core/backends/prompt-utils.d.ts.map +1 -1
  143. package/dist/core/backends/prompt-utils.js +60 -3
  144. package/dist/core/backends/prompt-utils.js.map +1 -1
  145. package/dist/core/bang-commands/commands-help.d.ts +5 -0
  146. package/dist/core/bang-commands/commands-help.d.ts.map +1 -0
  147. package/dist/core/bang-commands/commands-help.js +69 -0
  148. package/dist/core/bang-commands/commands-help.js.map +1 -0
  149. package/dist/core/bang-commands/commands-wiki.d.ts +75 -0
  150. package/dist/core/bang-commands/commands-wiki.d.ts.map +1 -0
  151. package/dist/core/bang-commands/commands-wiki.js +574 -0
  152. package/dist/core/bang-commands/commands-wiki.js.map +1 -0
  153. package/dist/core/bang-commands/index.d.ts +4 -2
  154. package/dist/core/bang-commands/index.d.ts.map +1 -1
  155. package/dist/core/bang-commands/index.js +15 -1
  156. package/dist/core/bang-commands/index.js.map +1 -1
  157. package/dist/core/bang-commands/registry.d.ts +47 -4
  158. package/dist/core/bang-commands/registry.d.ts.map +1 -1
  159. package/dist/core/bang-commands/registry.js +85 -15
  160. package/dist/core/bang-commands/registry.js.map +1 -1
  161. package/dist/core/context-builder.d.ts +53 -12
  162. package/dist/core/context-builder.d.ts.map +1 -1
  163. package/dist/core/context-builder.js +240 -92
  164. package/dist/core/context-builder.js.map +1 -1
  165. package/dist/core/daemon-api-cli.d.ts.map +1 -1
  166. package/dist/core/daemon-api-cli.js +50 -2
  167. package/dist/core/daemon-api-cli.js.map +1 -1
  168. package/dist/core/dispatcher-date-utils.d.ts +49 -0
  169. package/dist/core/dispatcher-date-utils.d.ts.map +1 -0
  170. package/dist/core/dispatcher-date-utils.js +132 -0
  171. package/dist/core/dispatcher-date-utils.js.map +1 -0
  172. package/dist/core/dispatcher-error-handling.d.ts +159 -0
  173. package/dist/core/dispatcher-error-handling.d.ts.map +1 -0
  174. package/dist/core/dispatcher-error-handling.js +393 -0
  175. package/dist/core/dispatcher-error-handling.js.map +1 -0
  176. package/dist/core/dispatcher-hourly-check.d.ts +150 -0
  177. package/dist/core/dispatcher-hourly-check.d.ts.map +1 -0
  178. package/dist/core/dispatcher-hourly-check.js +665 -0
  179. package/dist/core/dispatcher-hourly-check.js.map +1 -0
  180. package/dist/core/dispatcher-message-handler.d.ts +170 -0
  181. package/dist/core/dispatcher-message-handler.d.ts.map +1 -0
  182. package/dist/core/dispatcher-message-handler.js +1064 -0
  183. package/dist/core/dispatcher-message-handler.js.map +1 -0
  184. package/dist/core/dispatcher-morning-routine.d.ts +169 -0
  185. package/dist/core/dispatcher-morning-routine.d.ts.map +1 -0
  186. package/dist/core/dispatcher-morning-routine.js +449 -0
  187. package/dist/core/dispatcher-morning-routine.js.map +1 -0
  188. package/dist/core/dispatcher-prompt.d.ts +107 -0
  189. package/dist/core/dispatcher-prompt.d.ts.map +1 -0
  190. package/dist/core/dispatcher-prompt.js +227 -0
  191. package/dist/core/dispatcher-prompt.js.map +1 -0
  192. package/dist/core/dispatcher-repository-helpers.d.ts +39 -0
  193. package/dist/core/dispatcher-repository-helpers.d.ts.map +1 -0
  194. package/dist/core/dispatcher-repository-helpers.js +86 -0
  195. package/dist/core/dispatcher-repository-helpers.js.map +1 -0
  196. package/dist/core/dispatcher-result-processor.d.ts +168 -0
  197. package/dist/core/dispatcher-result-processor.d.ts.map +1 -0
  198. package/dist/core/dispatcher-result-processor.js +533 -0
  199. package/dist/core/dispatcher-result-processor.js.map +1 -0
  200. package/dist/core/dispatcher-scheduled-tasks.d.ts +406 -0
  201. package/dist/core/dispatcher-scheduled-tasks.d.ts.map +1 -0
  202. package/dist/core/dispatcher-scheduled-tasks.js +1032 -0
  203. package/dist/core/dispatcher-scheduled-tasks.js.map +1 -0
  204. package/dist/core/dispatcher-types.d.ts +411 -0
  205. package/dist/core/dispatcher-types.d.ts.map +1 -0
  206. package/dist/core/dispatcher-types.js +106 -0
  207. package/dist/core/dispatcher-types.js.map +1 -0
  208. package/dist/core/dispatcher.d.ts +122 -610
  209. package/dist/core/dispatcher.d.ts.map +1 -1
  210. package/dist/core/dispatcher.js +365 -3521
  211. package/dist/core/dispatcher.js.map +1 -1
  212. package/dist/core/integration-health.d.ts +18 -10
  213. package/dist/core/integration-health.d.ts.map +1 -1
  214. package/dist/core/integration-health.js +31 -1
  215. package/dist/core/integration-health.js.map +1 -1
  216. package/dist/core/integration-lifecycle.d.ts +65 -0
  217. package/dist/core/integration-lifecycle.d.ts.map +1 -1
  218. package/dist/core/integration-lifecycle.js +163 -14
  219. package/dist/core/integration-lifecycle.js.map +1 -1
  220. package/dist/core/integration-main-backend.d.ts +40 -0
  221. package/dist/core/integration-main-backend.d.ts.map +1 -1
  222. package/dist/core/integration-main-backend.js +89 -2
  223. package/dist/core/integration-main-backend.js.map +1 -1
  224. package/dist/core/management-md.d.ts +51 -17
  225. package/dist/core/management-md.d.ts.map +1 -1
  226. package/dist/core/management-md.js +233 -56
  227. package/dist/core/management-md.js.map +1 -1
  228. package/dist/core/metrics.d.ts +127 -0
  229. package/dist/core/metrics.d.ts.map +1 -1
  230. package/dist/core/metrics.js +256 -1
  231. package/dist/core/metrics.js.map +1 -1
  232. package/dist/core/output-language-policy.d.ts +74 -0
  233. package/dist/core/output-language-policy.d.ts.map +1 -0
  234. package/dist/core/output-language-policy.js +194 -0
  235. package/dist/core/output-language-policy.js.map +1 -0
  236. package/dist/core/prompts.d.ts +3 -1
  237. package/dist/core/prompts.d.ts.map +1 -1
  238. package/dist/core/prompts.js +161 -3
  239. package/dist/core/prompts.js.map +1 -1
  240. package/dist/core/repository-management-docs.d.ts +24 -0
  241. package/dist/core/repository-management-docs.d.ts.map +1 -1
  242. package/dist/core/repository-management-docs.js +210 -26
  243. package/dist/core/repository-management-docs.js.map +1 -1
  244. package/dist/core/roadmap-validate.js +13 -1
  245. package/dist/core/roadmap-validate.js.map +1 -1
  246. package/dist/core/routine-acquisition-plan.d.ts +182 -0
  247. package/dist/core/routine-acquisition-plan.d.ts.map +1 -0
  248. package/dist/core/routine-acquisition-plan.js +367 -0
  249. package/dist/core/routine-acquisition-plan.js.map +1 -0
  250. package/dist/core/routine-fetch-window-retry.d.ts +109 -0
  251. package/dist/core/routine-fetch-window-retry.d.ts.map +1 -0
  252. package/dist/core/routine-fetch-window-retry.js +210 -0
  253. package/dist/core/routine-fetch-window-retry.js.map +1 -0
  254. package/dist/core/routine-fetch-window-runner.d.ts +427 -0
  255. package/dist/core/routine-fetch-window-runner.d.ts.map +1 -0
  256. package/dist/core/routine-fetch-window-runner.js +1591 -0
  257. package/dist/core/routine-fetch-window-runner.js.map +1 -0
  258. package/dist/core/routine-windows.d.ts +171 -0
  259. package/dist/core/routine-windows.d.ts.map +1 -0
  260. package/dist/core/routine-windows.js +377 -0
  261. package/dist/core/routine-windows.js.map +1 -0
  262. package/dist/core/scheduler.d.ts +50 -2
  263. package/dist/core/scheduler.d.ts.map +1 -1
  264. package/dist/core/scheduler.js +88 -7
  265. package/dist/core/scheduler.js.map +1 -1
  266. package/dist/core/skill-curation/declarations.d.ts.map +1 -1
  267. package/dist/core/skill-curation/declarations.js +11 -12
  268. package/dist/core/skill-curation/declarations.js.map +1 -1
  269. package/dist/core/skill-source-paths.d.ts +14 -0
  270. package/dist/core/skill-source-paths.d.ts.map +1 -0
  271. package/dist/core/skill-source-paths.js +82 -0
  272. package/dist/core/skill-source-paths.js.map +1 -0
  273. package/dist/core/skills-compiler.d.ts +29 -0
  274. package/dist/core/skills-compiler.d.ts.map +1 -1
  275. package/dist/core/skills-compiler.js +166 -30
  276. package/dist/core/skills-compiler.js.map +1 -1
  277. package/dist/core/skills-manifest.d.ts.map +1 -1
  278. package/dist/core/skills-manifest.js +72 -0
  279. package/dist/core/skills-manifest.js.map +1 -1
  280. package/dist/core/system-reset.d.ts +25 -0
  281. package/dist/core/system-reset.d.ts.map +1 -1
  282. package/dist/core/system-reset.js +72 -2
  283. package/dist/core/system-reset.js.map +1 -1
  284. package/dist/core/wiki/approval-queue.d.ts +31 -0
  285. package/dist/core/wiki/approval-queue.d.ts.map +1 -0
  286. package/dist/core/wiki/approval-queue.js +44 -0
  287. package/dist/core/wiki/approval-queue.js.map +1 -0
  288. package/dist/core/wiki/bridge.d.ts +74 -0
  289. package/dist/core/wiki/bridge.d.ts.map +1 -0
  290. package/dist/core/wiki/bridge.js +405 -0
  291. package/dist/core/wiki/bridge.js.map +1 -0
  292. package/dist/core/wiki/compile-lock.d.ts +42 -0
  293. package/dist/core/wiki/compile-lock.d.ts.map +1 -0
  294. package/dist/core/wiki/compile-lock.js +55 -0
  295. package/dist/core/wiki/compile-lock.js.map +1 -0
  296. package/dist/core/wiki/compile-preview.d.ts +8 -0
  297. package/dist/core/wiki/compile-preview.d.ts.map +1 -0
  298. package/dist/core/wiki/compile-preview.js +200 -0
  299. package/dist/core/wiki/compile-preview.js.map +1 -0
  300. package/dist/core/wiki/cost-estimate.d.ts +30 -0
  301. package/dist/core/wiki/cost-estimate.d.ts.map +1 -0
  302. package/dist/core/wiki/cost-estimate.js +243 -0
  303. package/dist/core/wiki/cost-estimate.js.map +1 -0
  304. package/dist/core/wiki/dispatcher.d.ts +48 -0
  305. package/dist/core/wiki/dispatcher.d.ts.map +1 -0
  306. package/dist/core/wiki/dispatcher.js +92 -0
  307. package/dist/core/wiki/dispatcher.js.map +1 -0
  308. package/dist/core/wiki/git-precompile.d.ts +86 -0
  309. package/dist/core/wiki/git-precompile.d.ts.map +1 -0
  310. package/dist/core/wiki/git-precompile.js +96 -0
  311. package/dist/core/wiki/git-precompile.js.map +1 -0
  312. package/dist/core/wiki/import-migrate.d.ts +38 -0
  313. package/dist/core/wiki/import-migrate.d.ts.map +1 -0
  314. package/dist/core/wiki/import-migrate.js +310 -0
  315. package/dist/core/wiki/import-migrate.js.map +1 -0
  316. package/dist/core/wiki/import-probe.d.ts +76 -0
  317. package/dist/core/wiki/import-probe.d.ts.map +1 -0
  318. package/dist/core/wiki/import-probe.js +245 -0
  319. package/dist/core/wiki/import-probe.js.map +1 -0
  320. package/dist/core/wiki/index-cache.d.ts +39 -0
  321. package/dist/core/wiki/index-cache.d.ts.map +1 -0
  322. package/dist/core/wiki/index-cache.js +152 -0
  323. package/dist/core/wiki/index-cache.js.map +1 -0
  324. package/dist/core/wiki/multi-url-dispatch.d.ts +52 -0
  325. package/dist/core/wiki/multi-url-dispatch.d.ts.map +1 -0
  326. package/dist/core/wiki/multi-url-dispatch.js +72 -0
  327. package/dist/core/wiki/multi-url-dispatch.js.map +1 -0
  328. package/dist/core/wiki/wiki-fts.d.ts +75 -0
  329. package/dist/core/wiki/wiki-fts.d.ts.map +1 -0
  330. package/dist/core/wiki/wiki-fts.js +265 -0
  331. package/dist/core/wiki/wiki-fts.js.map +1 -0
  332. package/dist/core/wiki/workspaces.d.ts +101 -0
  333. package/dist/core/wiki/workspaces.d.ts.map +1 -0
  334. package/dist/core/wiki/workspaces.js +352 -0
  335. package/dist/core/wiki/workspaces.js.map +1 -0
  336. package/dist/core/wiki/write-strategy.d.ts +70 -0
  337. package/dist/core/wiki/write-strategy.d.ts.map +1 -0
  338. package/dist/core/wiki/write-strategy.js +112 -0
  339. package/dist/core/wiki/write-strategy.js.map +1 -0
  340. package/dist/core/workdir.d.ts +8 -1
  341. package/dist/core/workdir.d.ts.map +1 -1
  342. package/dist/core/workdir.js +4 -1
  343. package/dist/core/workdir.js.map +1 -1
  344. package/dist/db/observations.d.ts +45 -2
  345. package/dist/db/observations.d.ts.map +1 -1
  346. package/dist/db/observations.js +112 -14
  347. package/dist/db/observations.js.map +1 -1
  348. package/dist/db/schema.d.ts.map +1 -1
  349. package/dist/db/schema.js +135 -25
  350. package/dist/db/schema.js.map +1 -1
  351. package/dist/db/wiki-store.d.ts +3 -0
  352. package/dist/db/wiki-store.d.ts.map +1 -0
  353. package/dist/db/wiki-store.js +7 -0
  354. package/dist/db/wiki-store.js.map +1 -0
  355. package/dist/index.js +159 -610
  356. package/dist/index.js.map +1 -1
  357. package/dist/messaging/url-extract.d.ts +8 -0
  358. package/dist/messaging/url-extract.d.ts.map +1 -0
  359. package/dist/messaging/url-extract.js +41 -0
  360. package/dist/messaging/url-extract.js.map +1 -0
  361. package/dist/observers/delegated-sync-worker.d.ts +52 -1
  362. package/dist/observers/delegated-sync-worker.d.ts.map +1 -1
  363. package/dist/observers/delegated-sync-worker.js +75 -18
  364. package/dist/observers/delegated-sync-worker.js.map +1 -1
  365. package/dist/observers/imminent-event-scheduler.d.ts +20 -7
  366. package/dist/observers/imminent-event-scheduler.d.ts.map +1 -1
  367. package/dist/observers/imminent-event-scheduler.js +134 -29
  368. package/dist/observers/imminent-event-scheduler.js.map +1 -1
  369. package/dist/observers/mail-poller.d.ts +12 -5
  370. package/dist/observers/mail-poller.d.ts.map +1 -1
  371. package/dist/observers/mail-poller.js +36 -14
  372. package/dist/observers/mail-poller.js.map +1 -1
  373. package/dist/observers/manager.d.ts +37 -5
  374. package/dist/observers/manager.d.ts.map +1 -1
  375. package/dist/observers/manager.js +28 -10
  376. package/dist/observers/manager.js.map +1 -1
  377. package/dist/safety/always-disallowed.d.ts +65 -0
  378. package/dist/safety/always-disallowed.d.ts.map +1 -1
  379. package/dist/safety/always-disallowed.js +106 -10
  380. package/dist/safety/always-disallowed.js.map +1 -1
  381. package/dist/safety/audit.d.ts +46 -1
  382. package/dist/safety/audit.d.ts.map +1 -1
  383. package/dist/safety/audit.js +79 -16
  384. package/dist/safety/audit.js.map +1 -1
  385. package/dist/safety/risk-classifier.d.ts.map +1 -1
  386. package/dist/safety/risk-classifier.js +29 -0
  387. package/dist/safety/risk-classifier.js.map +1 -1
  388. package/dist/services/delegated-backend-invoker.d.ts +1 -51
  389. package/dist/services/delegated-backend-invoker.d.ts.map +1 -1
  390. package/dist/services/delegated-backend-invoker.js +41 -480
  391. package/dist/services/delegated-backend-invoker.js.map +1 -1
  392. package/dist/services/delegated-invoker-audit.d.ts +94 -0
  393. package/dist/services/delegated-invoker-audit.d.ts.map +1 -0
  394. package/dist/services/delegated-invoker-audit.js +238 -0
  395. package/dist/services/delegated-invoker-audit.js.map +1 -0
  396. package/dist/services/delegated-invoker-cache-hits.d.ts +34 -0
  397. package/dist/services/delegated-invoker-cache-hits.d.ts.map +1 -0
  398. package/dist/services/delegated-invoker-cache-hits.js +104 -0
  399. package/dist/services/delegated-invoker-cache-hits.js.map +1 -0
  400. package/dist/services/delegated-invoker-janitors.d.ts +28 -0
  401. package/dist/services/delegated-invoker-janitors.d.ts.map +1 -0
  402. package/dist/services/delegated-invoker-janitors.js +104 -0
  403. package/dist/services/delegated-invoker-janitors.js.map +1 -0
  404. package/dist/services/delegated-invoker-utils.d.ts +42 -0
  405. package/dist/services/delegated-invoker-utils.d.ts.map +1 -0
  406. package/dist/services/delegated-invoker-utils.js +100 -0
  407. package/dist/services/delegated-invoker-utils.js.map +1 -0
  408. package/dist/services/delegated-task-runtime.d.ts +1 -1
  409. package/dist/services/delegated-task-runtime.js +1 -1
  410. package/dist/services/integrations/snapshot-partitions.d.ts +5 -0
  411. package/dist/services/integrations/snapshot-partitions.d.ts.map +1 -1
  412. package/dist/services/integrations/snapshot-partitions.js +12 -0
  413. package/dist/services/integrations/snapshot-partitions.js.map +1 -1
  414. package/dist/services/voice/transcriber-impl.d.ts.map +1 -1
  415. package/dist/services/voice/transcriber-impl.js +7 -8
  416. package/dist/services/voice/transcriber-impl.js.map +1 -1
  417. package/dist/settings/runtime-settings.d.ts +12 -1
  418. package/dist/settings/runtime-settings.d.ts.map +1 -1
  419. package/dist/settings/runtime-settings.js +59 -1
  420. package/dist/settings/runtime-settings.js.map +1 -1
  421. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { serve } from "@hono/node-server";
2
- import { existsSync, readFileSync } from "node:fs";
3
2
  import { join, resolve } from "node:path";
3
+ import { rmSync } from "node:fs";
4
4
  import { randomBytes } from "node:crypto";
5
5
  import { loadConfig, getContextDir, isRoadmapStale, mergeRuntimeSettingsFromDb, runVaultHealthProbe, validateExternalObsidianVaultPath, } from "./config.js";
6
6
  import { getDegradedMode, isSetupCompleted, isUserPaused, isDegraded as readDegradedMode, } from "./db/runtime-state.js";
@@ -37,20 +37,14 @@ import { DiscordAdapter } from "./adapters/discord.js";
37
37
  import { SlackAdapter } from "./adapters/slack-adapter.js";
38
38
  import { TelegramAdapter } from "./adapters/telegram-adapter.js";
39
39
  import { DashboardAdapter } from "./adapters/dashboard-adapter.js";
40
- import { WhatsAppAdapter } from "./adapters/whatsapp-adapter.js";
41
- import { CalendarService } from "./services/calendar.js";
42
- import { AppleCalendarService } from "./services/apple-calendar/index.js";
43
- import { GmailService } from "./services/gmail.js";
44
- import { detectGoogleCredentialType } from "./services/google-auth.js";
45
40
  import { ObsidianService } from "./services/obsidian.js";
46
- import { NotionService } from "./services/notion.js";
47
- import { GitHubService } from "./services/github.js";
48
41
  import { createServiceRegistry } from "./services/service-registry.js";
49
42
  import { SignalDetector } from "./core/signal-detector.js";
50
43
  import { EventDispatcher } from "./core/dispatcher.js";
51
44
  import { ClaudeCodeCore } from "./core/backends/claude-code-core.js";
52
45
  import { BackendRouter } from "./core/backends/backend-router.js";
53
46
  import { ensureBackendMaterialized, syncAllUserSkills, buildConfiguredServices, refreshDmSessionWorkdirs, validateDelegatedStartup, } from "./core/workdir.js";
47
+ import { setWikiWorkspaceTokenResolver } from "./core/skills-compiler.js";
54
48
  import { CodexCore } from "./core/backends/codex-core.js";
55
49
  import { GeminiCliCore } from "./core/backends/gemini-cli-core.js";
56
50
  import { PriceFetcher } from "./core/backends/price-fetcher.js";
@@ -59,6 +53,8 @@ import { AuthHealthMonitor, AUTH_PROBE_NOTIFICATION_CATEGORY } from "./core/back
59
53
  import { AuthRecovery } from "./core/backends/auth-recovery.js";
60
54
  import { ContextBuilder } from "./core/context-builder.js";
61
55
  import { getTaskFlow, initTaskFlows } from "./core/prompts.js";
56
+ import { listWikiWorkspaces, readDefaultWikiWorkspace, readWikiWorkspaceByName, } from "./core/wiki/workspaces.js";
57
+ import { backfillWikiFulltext } from "./core/wiki/wiki-fts.js";
62
58
  import { ensureSkeletonFiles, resolveTemplatesRoot } from "./core/skeleton.js";
63
59
  import { normalizeGitWatchedRepos, queueGitProjectUpdate, queueMissingGitProjectInits, seedGitProjectDocTemplates, } from "./core/git-project-docs.js";
64
60
  import { reconcileTemplateAssets, recordInstructionAssetStatus, recordSkillAssetStatus, } from "./core/release-assets.js";
@@ -69,7 +65,7 @@ import { NotificationManager } from "./adapters/notification-manager.js";
69
65
  import { recordProactiveForwardDeliveries } from "./core/channel-timeline.js";
70
66
  import { continueDashboardSession as continueDashboardSessionFromHistory, endDashboardSession as endDashboardSessionFromChannel, markContextChanged, } from "./core/dashboard-session-controls.js";
71
67
  import { AuditLogger } from "./safety/audit.js";
72
- import { bootstrapManagementMd, migrateLegacyManagementMd, startManagementMdWatcher, } from "./core/management-md.js";
68
+ import { bootstrapManagementMd, startManagementMdWatcher, } from "./core/management-md.js";
73
69
  import { bootstrapManagementRegistry, startManagementRegistryWatcher, } from "./core/management-registry.js";
74
70
  import { bootstrapManagedTaskSeq } from "./db/managed-tasks-store.js";
75
71
  import { startDocsIndexer, } from "./core/docs/indexer.js";
@@ -79,11 +75,10 @@ import { DocsQAAdapter } from "./adapters/docs-qa-adapter.js";
79
75
  import { CompositeDashboardStream } from "./adapters/composite-dashboard-stream.js";
80
76
  import { createApp } from "./api/server.js";
81
77
  import { EventBroadcaster } from "./api/routes/sse.js";
82
- import { APP_NAME, createEvent, EventPriority, formatSqliteDatetime, getAgentDayBoundsUtc, getAgentDayDateStr, getAgentDayProgressMinutes, getBackendIds, nowInTimezone, parseSqliteUtcMs, } from "@aitne/shared";
78
+ import { APP_NAME, EventPriority, getBackendIds, } from "@aitne/shared";
83
79
  import { getOwnerChannel } from "./messaging/owner-channels.js";
84
80
  import { SUPPORTED_MESSAGING_PLATFORMS, } from "./messaging/constants.js";
85
81
  import { AgentWriteTracker } from "./safety/agent-write-tracker.js";
86
- import { discardStalePendingSchedules, hasActionInWindow, recoverOrphanedRunningSchedules, } from "./core/schedule-maintenance.js";
87
82
  import { ContextWriteGate, InMemoryTodayWriteLockManager, MigrationLock, getTodayWriteLockTimeoutMs, } from "./core/today-write-lock.js";
88
83
  import { applyPromptContextStaleness, } from "./core/context-staleness.js";
89
84
  import { InMemoryRoadmapWriteLockManager, getRoadmapWriteLockTimeoutMs, } from "./core/roadmap-write-lock.js";
@@ -103,13 +98,15 @@ import { parseImapAccountSecret } from "./services/mail/imap/app-password.js";
103
98
  import { ICloudImapProvider } from "./services/mail/imap/icloud-provider.js";
104
99
  import { YahooImapProvider } from "./services/mail/imap/yahoo-provider.js";
105
100
  import { GmailProvider } from "./services/mail/gmail/gmail-provider.js";
106
- import { ensureLegacyGmailRow } from "./services/mail/gmail/legacy-row.js";
107
- import { syncLegacyGmailAccountState } from "./services/mail/gmail/legacy-row.js";
108
101
  import { MailPoller } from "./observers/mail-poller.js";
109
102
  import { MailReconciliationJob } from "./observers/mail-reconciliation.js";
110
103
  import { SecretBroker } from "./secrets/secret-broker.js";
111
104
  import { captureOriginalShellEnv, syncBackendApiKeyToEnv, } from "./secrets/backend-api-key-env.js";
112
105
  import { createLogger, toSafeErrorMessage } from "./logging.js";
106
+ import { runCatchup, runPostMessagingCatchup, } from "./bootstrap/catchup.js";
107
+ import { hasFreshAgentDayTodayMd, readSkillCurationCadence, } from "./bootstrap/schedule-helpers.js";
108
+ import { createAdapterReloaders, whatsappQrResponseFromAdapter, } from "./bootstrap/adapters.js";
109
+ import { createInitialSecretState, createServiceReloaders, } from "./bootstrap/services.js";
113
110
  const logger = createLogger("daemon", {
114
111
  transport: {
115
112
  target: "pino-pretty",
@@ -146,6 +143,32 @@ async function startup() {
146
143
  // ── 3. Database ──
147
144
  const db = createDatabase(config);
148
145
  applySchema(db);
146
+ setWikiWorkspaceTokenResolver((processKey, workspaceName) => {
147
+ if (!processKey.startsWith("wiki."))
148
+ return null;
149
+ // Match `context-builder.ts`'s per-event lookup: prefer the named
150
+ // workspace, fall back to the default. Without this, multi-workspace
151
+ // installs would render the `<wiki_workspace>` XML against the target
152
+ // workspace while skill prose (`{{vault_path}}` etc.) referenced the
153
+ // default — the agent would then operate on the wrong vault.
154
+ const workspace = (workspaceName ? readWikiWorkspaceByName(db, workspaceName) : null)
155
+ ?? readDefaultWikiWorkspace(db);
156
+ if (!workspace)
157
+ return null;
158
+ return {
159
+ vault_path: workspace.root_path,
160
+ language: workspace.language,
161
+ workspace_name: workspace.name,
162
+ schema_version: String(workspace.schema_version),
163
+ };
164
+ });
165
+ // WIKI_BUILDER_DESIGN.md §P4.A — boot-time FTS backfill. The
166
+ // `fts_wiki` virtual table is content-less and lives outside the
167
+ // mail-style trigger chain (the source is the filesystem, not a SQL
168
+ // table), so a fresh DB or a workspace seeded before P4 landed needs
169
+ // a one-shot rebuild. Per-workspace gate inside `backfillWikiFulltext`
170
+ // keeps this near-free in steady state.
171
+ backfillWikiFulltext(db, listWikiWorkspaces(db));
149
172
  // §12 ("managed_tasks.id collision after restore from backup") — ensure
150
173
  // `managed_task_seq.next_id` is greater than the max existing mt id so a
151
174
  // backup-restored DB cannot collide with the seq counter on the next
@@ -227,7 +250,6 @@ async function startup() {
227
250
  let managementMdWatcher = null;
228
251
  let managementRegistryWatcher = null;
229
252
  try {
230
- migrateLegacyManagementMd(config.dataDir);
231
253
  await bootstrapManagementMd(config.dataDir, db, config.workspaceDir, {
232
254
  externalObsidianVaultPath: config.externalObsidianVaultPath,
233
255
  externalObsidianWatch: config.externalObsidianWatch,
@@ -407,185 +429,34 @@ async function startup() {
407
429
  logger.warn({ err, platform }, "Failed to deliver welcome DM after auto-pairing");
408
430
  }
409
431
  }
410
- let discordAdapter = null;
411
- let slackAdapter = null;
412
- let telegramAdapter = null;
413
- let whatsappAdapter = null;
414
- async function reloadDiscordAdapter(startNow) {
415
- const botToken = await secretBroker.getDiscordBotToken();
416
- const configured = !!botToken;
417
- messageHub.setPlatformConfigured("discord", configured);
418
- if (discordAdapter) {
419
- try {
420
- await discordAdapter.stop();
421
- }
422
- catch (err) {
423
- logger.warn({ err }, "Failed to stop Discord adapter during reload");
424
- }
425
- messageHub.unregister("discord");
426
- discordAdapter = null;
427
- }
428
- if (!botToken) {
429
- return;
430
- }
431
- discordAdapter = new DiscordAdapter({
432
- botToken,
433
- ownerUserId: config.discordOwnerUserId,
434
- onMessage: (event) => void eventBus.put(event),
435
- onOwnerDetected: (userId) => recordDetectedOwner("discord", userId),
436
- attachmentStore,
437
- });
438
- messageHub.register(discordAdapter);
439
- if (startNow) {
440
- // Mark as "connecting" while the websocket handshake is in flight so
441
- // /health doesn't briefly report "ok" before the adapter actually
442
- // comes up (register() defaults to "ok" to keep the notification
443
- // pipeline usable in unit tests; this override is the real state).
444
- messageHub.setPlatformRuntimeStatus("discord", { runtimeState: "connecting", error: null });
445
- try {
446
- await discordAdapter.start();
447
- messageHub.setPlatformRuntimeStatus("discord", { runtimeState: "ok", error: null });
448
- }
449
- catch (err) {
450
- const message = toSafeErrorMessage(err, "Discord adapter failed to start");
451
- messageHub.setPlatformRuntimeStatus("discord", { runtimeState: "error", error: message });
452
- logger.error({ err }, "Failed to start Discord adapter during reload");
453
- }
454
- }
455
- }
456
- async function reloadSlackAdapter(startNow) {
457
- const [botToken, appToken] = await Promise.all([
458
- secretBroker.getSlackBotToken(),
459
- secretBroker.getSlackAppToken(),
460
- ]);
461
- const configured = !!(botToken && appToken);
462
- messageHub.setPlatformConfigured("slack", configured);
463
- if (slackAdapter) {
464
- try {
465
- await slackAdapter.stop();
466
- }
467
- catch (err) {
468
- logger.warn({ err }, "Failed to stop Slack adapter during reload");
469
- }
470
- messageHub.unregister("slack");
471
- slackAdapter = null;
472
- }
473
- if (!botToken || !appToken) {
474
- return;
475
- }
476
- slackAdapter = new SlackAdapter({
477
- botToken,
478
- appToken,
479
- ownerUserId: config.slackOwnerUserId,
480
- onMessage: (event) => void eventBus.put(event),
481
- onOwnerDetected: (userId) => recordDetectedOwner("slack", userId),
482
- attachmentStore,
483
- });
484
- messageHub.register(slackAdapter);
485
- if (startNow) {
486
- messageHub.setPlatformRuntimeStatus("slack", { runtimeState: "connecting", error: null });
487
- try {
488
- await slackAdapter.start();
489
- messageHub.setPlatformRuntimeStatus("slack", { runtimeState: "ok", error: null });
490
- }
491
- catch (err) {
492
- const message = toSafeErrorMessage(err, "Slack adapter failed to start");
493
- messageHub.setPlatformRuntimeStatus("slack", { runtimeState: "error", error: message });
494
- logger.error({ err }, "Failed to start Slack adapter during reload");
495
- }
496
- }
497
- }
498
- async function reloadTelegramAdapter(startNow) {
499
- const botToken = await secretBroker.getTelegramBotToken();
500
- const configured = !!botToken;
501
- messageHub.setPlatformConfigured("telegram", configured);
502
- if (telegramAdapter) {
503
- try {
504
- await telegramAdapter.stop();
505
- }
506
- catch (err) {
507
- logger.warn({ err }, "Failed to stop Telegram adapter during reload");
508
- }
509
- messageHub.unregister("telegram");
510
- telegramAdapter = null;
511
- }
512
- if (!botToken) {
513
- return;
514
- }
515
- telegramAdapter = new TelegramAdapter({
516
- botToken,
517
- ownerChatId: config.telegramOwnerChatId,
518
- onMessage: (event) => void eventBus.put(event),
519
- onOwnerDetected: (chatId) => recordDetectedOwner("telegram", chatId),
520
- attachmentStore,
521
- });
522
- messageHub.register(telegramAdapter);
523
- if (startNow) {
524
- messageHub.setPlatformRuntimeStatus("telegram", { runtimeState: "connecting", error: null });
432
+ const adapterState = {
433
+ discord: null,
434
+ slack: null,
435
+ telegram: null,
436
+ whatsapp: null,
437
+ };
438
+ const { reloadDiscordAdapter, reloadSlackAdapter, reloadTelegramAdapter, buildWhatsAppAdapter, teardownWhatsAppAdapter, enableWhatsAppAdapter, } = createAdapterReloaders({
439
+ config,
440
+ secretBroker,
441
+ messageHub,
442
+ eventBus,
443
+ attachmentStore,
444
+ recordDetectedOwner,
445
+ onWhatsAppLoggedOut: async () => {
525
446
  try {
526
- await telegramAdapter.start();
527
- messageHub.setPlatformRuntimeStatus("telegram", { runtimeState: "ok", error: null });
447
+ await messageHub.sendToUser("WhatsApp session logged out — re-run foreground pairing");
528
448
  }
529
449
  catch (err) {
530
- const message = toSafeErrorMessage(err, "Telegram adapter failed to start");
531
- messageHub.setPlatformRuntimeStatus("telegram", { runtimeState: "error", error: message });
532
- logger.error({ err }, "Failed to start Telegram adapter during reload");
450
+ logger.error({ err }, "Failed to deliver WhatsApp logout notification via fallback channel");
533
451
  }
534
- }
535
- }
452
+ },
453
+ state: adapterState,
454
+ });
536
455
  await Promise.all([
537
456
  reloadDiscordAdapter(false),
538
457
  reloadSlackAdapter(false),
539
458
  reloadTelegramAdapter(false),
540
459
  ]);
541
- /**
542
- * Build (or rebuild) the WhatsApp adapter from current config and register
543
- * it with the MessageHub. Idempotent: returns the existing adapter if config
544
- * is unchanged. Throws if whatsappOwnerPhone is missing.
545
- */
546
- function buildWhatsAppAdapter() {
547
- if (!config.whatsappOwnerPhone) {
548
- throw new Error("Cannot enable WhatsApp: PA_WHATSAPP_OWNER_PHONE is not set");
549
- }
550
- const existing = messageHub.getAdapter("whatsapp");
551
- if (existing && whatsappAdapter && existing === whatsappAdapter) {
552
- return whatsappAdapter;
553
- }
554
- const adapter = new WhatsAppAdapter({
555
- ownerPhone: config.whatsappOwnerPhone,
556
- authDir: config.whatsappAuthDir ?? join(config.dataDir, "whatsapp", "auth"),
557
- onMessage: (event) => void eventBus.put(event),
558
- attachmentStore,
559
- onLoggedOut: async () => {
560
- try {
561
- await messageHub.sendToUser("WhatsApp session logged out — re-run foreground pairing");
562
- }
563
- catch (err) {
564
- logger.error({ err }, "Failed to deliver WhatsApp logout notification via fallback channel");
565
- }
566
- },
567
- });
568
- messageHub.register(adapter);
569
- whatsappAdapter = adapter;
570
- return adapter;
571
- }
572
- /**
573
- * Tear down the WhatsApp adapter completely. Used by the dashboard
574
- * `whatsappEnabled=false` toggle so we don't keep a stale Baileys socket
575
- * around. Logs but does not throw on socket close errors.
576
- */
577
- async function teardownWhatsAppAdapter() {
578
- if (!whatsappAdapter)
579
- return;
580
- try {
581
- await whatsappAdapter.stop();
582
- }
583
- catch (err) {
584
- logger.warn({ err }, "Error stopping WhatsApp adapter during teardown");
585
- }
586
- messageHub.unregister("whatsapp");
587
- whatsappAdapter = null;
588
- }
589
460
  if (config.whatsappEnabled) {
590
461
  if (!config.whatsappOwnerPhone) {
591
462
  throw new Error("PA_WHATSAPP_ENABLED=true but PA_WHATSAPP_OWNER_PHONE is not set");
@@ -637,7 +508,7 @@ async function startup() {
637
508
  },
638
509
  startPairing: async (ttlMs = 5 * 60_000) => {
639
510
  assertAdapterReady("telegram");
640
- const adapter = telegramAdapter;
511
+ const adapter = adapterState.telegram;
641
512
  if (!adapter) {
642
513
  throw new Error("Telegram adapter is not initialized. Save the token and retry.");
643
514
  }
@@ -697,12 +568,12 @@ async function startup() {
697
568
  };
698
569
  },
699
570
  getPairingStatus: () => ({
700
- paired: telegramAdapter?.getOwnerChatId() !== null,
701
- ownerChatId: telegramAdapter?.getOwnerChatId() ?? null,
702
- pairingActive: telegramAdapter?.isPairingActive() ?? false,
571
+ paired: adapterState.telegram?.getOwnerChatId() !== null,
572
+ ownerChatId: adapterState.telegram?.getOwnerChatId() ?? null,
573
+ pairingActive: adapterState.telegram?.isPairingActive() ?? false,
703
574
  }),
704
575
  cancelPairing: () => {
705
- telegramAdapter?.cancelPairing();
576
+ adapterState.telegram?.cancelPairing();
706
577
  },
707
578
  };
708
579
  }
@@ -724,7 +595,7 @@ async function startup() {
724
595
  },
725
596
  startPairing: async (ttlMs = 5 * 60_000) => {
726
597
  assertAdapterReady("slack");
727
- const adapter = slackAdapter;
598
+ const adapter = adapterState.slack;
728
599
  if (!adapter) {
729
600
  throw new Error("Slack adapter is not initialized. Save the tokens and retry.");
730
601
  }
@@ -743,12 +614,12 @@ async function startup() {
743
614
  return { phrase, expiresAt };
744
615
  },
745
616
  cancelPairing: () => {
746
- slackAdapter?.cancelPairing();
617
+ adapterState.slack?.cancelPairing();
747
618
  },
748
619
  getPairingStatus: () => ({
749
- paired: slackAdapter?.getOwnerUserId() !== null,
750
- ownerUserId: slackAdapter?.getOwnerUserId() ?? null,
751
- pairingActive: slackAdapter?.isPairingActive() ?? false,
620
+ paired: adapterState.slack?.getOwnerUserId() !== null,
621
+ ownerUserId: adapterState.slack?.getOwnerUserId() ?? null,
622
+ pairingActive: adapterState.slack?.isPairingActive() ?? false,
752
623
  }),
753
624
  };
754
625
  }
@@ -770,7 +641,7 @@ async function startup() {
770
641
  },
771
642
  startPairing: async (ttlMs = 5 * 60_000) => {
772
643
  assertAdapterReady("discord");
773
- const adapter = discordAdapter;
644
+ const adapter = adapterState.discord;
774
645
  if (!adapter) {
775
646
  throw new Error("Discord adapter is not initialized. Save the token and retry.");
776
647
  }
@@ -784,38 +655,15 @@ async function startup() {
784
655
  return { phrase, expiresAt };
785
656
  },
786
657
  cancelPairing: () => {
787
- discordAdapter?.cancelPairing();
658
+ adapterState.discord?.cancelPairing();
788
659
  },
789
660
  getPairingStatus: () => ({
790
- paired: discordAdapter?.getOwnerUserId() !== null,
791
- ownerUserId: discordAdapter?.getOwnerUserId() ?? null,
792
- pairingActive: discordAdapter?.isPairingActive() ?? false,
661
+ paired: adapterState.discord?.getOwnerUserId() !== null,
662
+ ownerUserId: adapterState.discord?.getOwnerUserId() ?? null,
663
+ pairingActive: adapterState.discord?.isPairingActive() ?? false,
793
664
  }),
794
665
  };
795
666
  }
796
- function whatsappQrResponseFromAdapter(adapter, snapshotOverride) {
797
- if (!adapter) {
798
- return {
799
- dataUrl: null,
800
- payload: null,
801
- generatedAt: null,
802
- expiresAt: null,
803
- state: "not_initialized",
804
- error: "WhatsApp adapter not enabled",
805
- };
806
- }
807
- const snapshot = snapshotOverride !== undefined
808
- ? snapshotOverride
809
- : adapter.getQrSnapshot();
810
- return {
811
- dataUrl: snapshot?.dataUrl ?? null,
812
- payload: snapshot?.payload ?? null,
813
- generatedAt: snapshot?.generatedAt ?? null,
814
- expiresAt: snapshot?.expiresAt ?? null,
815
- state: adapter.getStatus(),
816
- error: adapter.getStatusError(),
817
- };
818
- }
819
667
  // Dashboard adapter — always registered (activates on SSE connect)
820
668
  const dashboardAdapter = new DashboardAdapter((event) => void eventBus.put(event));
821
669
  messageHub.register(dashboardAdapter);
@@ -882,96 +730,14 @@ async function startup() {
882
730
  },
883
731
  },
884
732
  });
885
- const secretState = {
886
- googleCredentialsConfigured: false,
887
- googleTokenConfigured: false,
888
- googleCredentialType: null,
889
- notionConfigured: false,
890
- githubConfigured: false,
891
- githubWebhookConfigured: false,
892
- };
893
- async function refreshGoogleSecretState() {
894
- const [credentialsRaw, tokenRaw] = await Promise.all([
895
- secretBroker.getGoogleCredentialsJson(),
896
- secretBroker.getGoogleTokenJson(),
897
- ]);
898
- secretState.googleCredentialsConfigured = !!credentialsRaw;
899
- secretState.googleTokenConfigured = !!tokenRaw;
900
- secretState.googleCredentialType = detectGoogleCredentialType(credentialsRaw);
901
- }
902
- async function reloadGoogleServices() {
903
- await refreshGoogleSecretState();
904
- services.calendar = null;
905
- services.gmail = null;
906
- delete services.errors.googleCalendar;
907
- delete services.errors.gmail;
908
- if (!secretState.googleCredentialsConfigured) {
909
- if (services.mail) {
910
- syncLegacyGmailAccountState(db, services.mail, {
911
- available: false,
912
- error: "Google credentials are not configured.",
913
- });
914
- }
915
- return;
916
- }
917
- // OAuth2 pre-auth: credentials uploaded but the user has not completed the
918
- // browser flow yet (no token in the keychain). Initializing the services
919
- // would fail with a "missing token" error that the dashboard would then
920
- // render as red "Error" under the Google card — but this is the expected
921
- // mid-setup state, not a failure. Skip init and leave services.errors
922
- // unset so /health reports error: null until the user finishes OAuth or a
923
- // real init error occurs.
924
- const oauth2PreAuth = secretState.googleCredentialType === "oauth2"
925
- && !secretState.googleTokenConfigured;
926
- if (oauth2PreAuth) {
927
- if (services.mail) {
928
- syncLegacyGmailAccountState(db, services.mail, {
929
- available: false,
930
- error: "Awaiting Google OAuth authorization.",
931
- });
932
- }
933
- return;
934
- }
935
- const calendarService = new CalendarService(config, secretBroker);
936
- try {
937
- await calendarService.init();
938
- services.calendar = calendarService;
939
- }
940
- catch (err) {
941
- const msg = err.message;
942
- logger.error({ error: msg }, "Calendar service init failed, continuing without it");
943
- services.errors.googleCalendar = msg;
944
- }
945
- const gmailService = new GmailService(secretBroker);
946
- try {
947
- await gmailService.init();
948
- services.gmail = gmailService;
949
- }
950
- catch (err) {
951
- const msg = err.message;
952
- logger.error({ error: msg }, "Gmail service init failed, continuing without it");
953
- services.errors.gmail = msg;
954
- }
955
- // Ensure the shared-Google-OAuth Gmail identity exists as a unified
956
- // mail account (idempotent; returns `exists` on subsequent boots).
957
- if (services.gmail?.available) {
958
- try {
959
- await ensureLegacyGmailRow(db, services.gmail);
960
- }
961
- catch (err) {
962
- logger.error({ err }, "Failed to ensure shared-Google-OAuth Gmail mail_accounts row");
963
- }
964
- if (services.mail) {
965
- syncLegacyGmailAccountState(db, services.mail, { available: true });
966
- }
967
- }
968
- else if (services.mail) {
969
- syncLegacyGmailAccountState(db, services.mail, {
970
- available: false,
971
- error: services.errors.gmail ?? "Gmail is not configured.",
972
- });
973
- }
974
- }
733
+ const secretState = createInitialSecretState();
734
+ const { reloadGoogleServices, reloadAppleCalendarService, reloadNotionService, reloadGitHubService, } = createServiceReloaders({
735
+ db,
736
+ config,
737
+ secretBroker,
738
+ services,
739
+ secretState,
740
+ });
975
741
  // Google Maps (F-08: commute optimization)
976
742
  {
977
743
  const { GoogleMapsService } = await import("./services/google-maps.js");
@@ -1007,78 +773,6 @@ async function startup() {
1007
773
  else if (config.externalObsidianVaultPath && !config.externalObsidianVaultName) {
1008
774
  services.errors.obsidian = "externalObsidianVaultName is required for the Obsidian CLI service (externalObsidianVaultPath alone enables file watching only)";
1009
775
  }
1010
- async function reloadAppleCalendarService() {
1011
- const raw = await secretBroker.getAppleCalendarCredentialsJson();
1012
- services.appleCalendar = null;
1013
- delete services.errors.appleCalendar;
1014
- if (!raw) {
1015
- return;
1016
- }
1017
- const service = new AppleCalendarService(secretBroker);
1018
- try {
1019
- await service.init();
1020
- if (service.available) {
1021
- services.appleCalendar = service;
1022
- }
1023
- else {
1024
- // Surface the underlying iCloud error verbatim — the dashboard
1025
- // shows it on the Connections card so the user can act
1026
- // (`401 Unauthorized` → regenerate password; network error →
1027
- // retry; etc.). Falls back to a generic placeholder only if
1028
- // init() failed without recording a message.
1029
- services.errors.appleCalendar =
1030
- service.initError
1031
- ?? "Apple Calendar credentials present but iCloud discovery did not return a usable calendar — verify the app-specific password.";
1032
- }
1033
- }
1034
- catch (err) {
1035
- const msg = err.message;
1036
- logger.error({ error: msg }, "Apple Calendar service init failed, continuing without it");
1037
- services.errors.appleCalendar = msg;
1038
- }
1039
- }
1040
- async function reloadNotionService() {
1041
- const apiKey = await secretBroker.getNotionApiKey();
1042
- secretState.notionConfigured = !!apiKey;
1043
- services.notion = null;
1044
- delete services.errors.notion;
1045
- if (!apiKey) {
1046
- return;
1047
- }
1048
- const notionService = new NotionService(config, secretBroker);
1049
- try {
1050
- await notionService.init();
1051
- services.notion = notionService;
1052
- }
1053
- catch (err) {
1054
- const msg = err.message;
1055
- logger.error({ error: msg }, "Notion service init failed, continuing without it");
1056
- services.errors.notion = msg;
1057
- }
1058
- }
1059
- async function reloadGitHubService() {
1060
- const [token, webhookSecret] = await Promise.all([
1061
- secretBroker.getGitHubToken(),
1062
- secretBroker.getGitHubWebhookSecret(),
1063
- ]);
1064
- secretState.githubConfigured = !!token;
1065
- secretState.githubWebhookConfigured = !!webhookSecret;
1066
- services.github = null;
1067
- delete services.errors.github;
1068
- if (!token) {
1069
- return;
1070
- }
1071
- const githubService = new GitHubService(token, webhookSecret);
1072
- try {
1073
- await githubService.init();
1074
- services.github = githubService;
1075
- }
1076
- catch (err) {
1077
- const msg = err.message;
1078
- logger.error({ error: msg }, "GitHub service init failed, continuing without it");
1079
- services.errors.github = msg;
1080
- }
1081
- }
1082
776
  await Promise.all([
1083
777
  reloadGoogleServices(),
1084
778
  reloadAppleCalendarService(),
@@ -1088,7 +782,7 @@ async function startup() {
1088
782
  /** Build integration status snapshot for /api/health */
1089
783
  const getIntegrationStatus = () => {
1090
784
  const whatsappState = config.whatsappEnabled
1091
- ? (whatsappAdapter?.getStatus() ?? "disabled")
785
+ ? (adapterState.whatsapp?.getStatus() ?? "disabled")
1092
786
  : "not_configured";
1093
787
  return {
1094
788
  google: {
@@ -1828,7 +1522,7 @@ async function startup() {
1828
1522
  // the newly created backend-specific dir (e.g. .codex/skills/) also
1829
1523
  // receives user skills. The dispatcher's syncAllUserSkills ran BEFORE
1830
1524
  // router.execute(), so the fallback dir didn't exist at that point.
1831
- (sessionDir, backendId, eventType, processKey) => {
1525
+ (sessionDir, backendId, eventType, processKey, wikiWorkspaceName) => {
1832
1526
  // services is the mutable ServiceRegistry — reading .calendar here
1833
1527
  // picks up the current availability (e.g. after mid-flight OAuth).
1834
1528
  // GitHub presence is read fresh from the unified `repositories` table
@@ -1838,7 +1532,7 @@ async function startup() {
1838
1532
  github: selectGithubRepoSlugs(db).length > 0,
1839
1533
  });
1840
1534
  const mailAccounts = services.mail?.listActiveAccounts() ?? [];
1841
- ensureBackendMaterialized(config.workspaceDir, sessionDir, backendId, eventType, processKey, cfgServices, mailAccounts, readIntegrations(db), config.character);
1535
+ ensureBackendMaterialized(config.workspaceDir, sessionDir, backendId, eventType, processKey, cfgServices, mailAccounts, readIntegrations(db), config.character, wikiWorkspaceName);
1842
1536
  syncAllUserSkills(sessionDir, join(config.dataDir, "skills"));
1843
1537
  });
1844
1538
  // Startup validation — warn if any delegated-mode variant files are
@@ -1965,6 +1659,13 @@ async function startup() {
1965
1659
  // safe (DOCS_QA_B7_DESIGN.md §9 F3.4 / §11.4).
1966
1660
  dispatcher.setDashboardStream(new CompositeDashboardStream([dashboardAdapter, docsQAAdapter]));
1967
1661
  dispatcher.setAttachmentStore(attachmentStore);
1662
+ // Real-time routine progress for the dashboard. `dispatchSafe` emits
1663
+ // `kind: "routine_started"` / `"routine_completed"` on the default
1664
+ // SSE `event` channel — same pipe as `kind: "main_backend_changed"`
1665
+ // — so the setup wizard's "今日の状況を生成中…" indicator can render
1666
+ // without waiting on `/api/events` REST polling. Failure containment
1667
+ // lives in `broadcastRoutineProgress` (swallows + warns).
1668
+ dispatcher.setEventBroadcaster(eventBroadcaster);
1968
1669
  // Local-Whisper voice transcription for inbound audio attachments.
1969
1670
  // See docs/design/appendices/voice-transcription.md.
1970
1671
  //
@@ -2071,17 +1772,7 @@ async function startup() {
2071
1772
  // (same logic as runCatchup, ensures schedule generation after first auth)
2072
1773
  const contextDir = getContextDir(config);
2073
1774
  const todayMdPath = join(contextDir, "today.md");
2074
- let needsMorning = false;
2075
- if (existsSync(todayMdPath)) {
2076
- const firstLine = readFileSync(todayMdPath, "utf-8").split("\n")[0];
2077
- const today = getAgentDayDateStr(config.timezone || undefined, config.dayBoundaryHour);
2078
- if (!firstLine.includes(today)) {
2079
- needsMorning = true;
2080
- }
2081
- }
2082
- else {
2083
- needsMorning = true;
2084
- }
1775
+ const needsMorning = !hasFreshAgentDayTodayMd(todayMdPath, config.timezone || undefined, config.dayBoundaryHour);
2085
1776
  if (needsMorning) {
2086
1777
  // Morning routine's post-completion hook will also check roadmap staleness.
2087
1778
  logger.info("Google services ready — today.md stale, queueing morning_routine wake");
@@ -2433,7 +2124,7 @@ async function startup() {
2433
2124
  auditLogger,
2434
2125
  validateAttachmentTurnToken: (token) => dispatcher.validateAttachmentTurnToken(token),
2435
2126
  whatsappControls: {
2436
- isInitialized: () => whatsappAdapter !== null,
2127
+ isInitialized: () => adapterState.whatsapp !== null,
2437
2128
  enable: async () => {
2438
2129
  const adapter = buildWhatsAppAdapter();
2439
2130
  if (adapter.getStatus() === "disabled") {
@@ -2444,19 +2135,58 @@ async function startup() {
2444
2135
  await teardownWhatsAppAdapter();
2445
2136
  },
2446
2137
  requestQr: async () => {
2447
- if (!whatsappAdapter) {
2448
- await whatsappControls_enable();
2138
+ if (!adapterState.whatsapp) {
2139
+ await enableWhatsAppAdapter();
2449
2140
  }
2450
- await whatsappAdapter.requestQR();
2141
+ await adapterState.whatsapp.requestQR();
2451
2142
  },
2452
2143
  waitForQr: async (timeoutMs = 10_000) => {
2453
- if (!whatsappAdapter) {
2454
- await whatsappControls_enable();
2144
+ if (!adapterState.whatsapp) {
2145
+ await enableWhatsAppAdapter();
2146
+ }
2147
+ const snapshot = await adapterState.whatsapp.waitForQr(timeoutMs);
2148
+ return whatsappQrResponseFromAdapter(adapterState.whatsapp, snapshot);
2149
+ },
2150
+ getQrResponse: () => whatsappQrResponseFromAdapter(adapterState.whatsapp),
2151
+ reset: async (timeoutMs = 10_000) => {
2152
+ // 1. Stop + unregister any live adapter so no Baileys callbacks fire
2153
+ // against the auth dir while we delete it.
2154
+ await teardownWhatsAppAdapter();
2155
+ // 2. Wipe Baileys' multi-file auth state. Without this, a fresh
2156
+ // enable() would re-load the stale creds and never emit a QR —
2157
+ // which is exactly the "前のデータが残っているのか pair できない"
2158
+ // failure mode the dashboard's "Reset connection" button exists
2159
+ // to recover from. Recursive + force so a partially-written dir
2160
+ // (e.g. only qr.txt present, no creds.json yet) still wipes
2161
+ // cleanly. `force: true` also no-ops if the dir is already gone.
2162
+ const authDir = config.whatsappAuthDir ?? join(config.dataDir, "whatsapp", "auth");
2163
+ try {
2164
+ rmSync(authDir, { recursive: true, force: true });
2165
+ }
2166
+ catch (err) {
2167
+ logger.warn({ err, authDir }, "Failed to wipe WhatsApp auth directory during reset");
2168
+ }
2169
+ // 3. Drop the cached owner_channels mapping so a freshly-paired
2170
+ // session re-discovers the channel id from the inbound auth
2171
+ // handshake rather than reusing the prior LID alias.
2172
+ try {
2173
+ db
2174
+ .prepare("DELETE FROM owner_channels WHERE platform = ?")
2175
+ .run("whatsapp");
2176
+ }
2177
+ catch (err) {
2178
+ logger.warn({ err }, "Failed to clear WhatsApp owner_channels row during reset");
2455
2179
  }
2456
- const snapshot = await whatsappAdapter.waitForQr(timeoutMs);
2457
- return whatsappQrResponseFromAdapter(whatsappAdapter, snapshot);
2180
+ // 4. If the integration is still enabled, rebuild the adapter and
2181
+ // await the first scannable QR so the dashboard can render it
2182
+ // in the same request that triggered the reset.
2183
+ if (!config.whatsappEnabled) {
2184
+ return whatsappQrResponseFromAdapter(null);
2185
+ }
2186
+ await enableWhatsAppAdapter();
2187
+ const snapshot = await adapterState.whatsapp.waitForQr(timeoutMs);
2188
+ return whatsappQrResponseFromAdapter(adapterState.whatsapp, snapshot);
2458
2189
  },
2459
- getQrResponse: () => whatsappQrResponseFromAdapter(whatsappAdapter),
2460
2190
  },
2461
2191
  messagingControls: {
2462
2192
  telegram: buildTelegramControls(),
@@ -2464,13 +2194,6 @@ async function startup() {
2464
2194
  discord: buildDiscordControls(),
2465
2195
  },
2466
2196
  });
2467
- // Local helper avoiding circular reference inside the object literal above.
2468
- async function whatsappControls_enable() {
2469
- const adapter = buildWhatsAppAdapter();
2470
- if (adapter.getStatus() === "disabled") {
2471
- await adapter.start();
2472
- }
2473
- }
2474
2197
  // Mount /api/docs/* (DOCS_QA_DESIGN.md §10.4 + DOCS_QA_B7_DESIGN.md
2475
2198
  // §S5–S6) after createApp so the indexer handle and the QA SSE
2476
2199
  // adapter can be threaded in without extending ApiDependencies. The
@@ -2486,6 +2209,22 @@ async function startup() {
2486
2209
  fetch: app.fetch,
2487
2210
  hostname: "127.0.0.1",
2488
2211
  port: config.apiPort,
2212
+ // @hono/node-server's getRequestListener defaults to replacing
2213
+ // `globalThis.Request` / `globalThis.Response` with its own lazy
2214
+ // wrapper classes (named `_Request` / `_Response`) for response-
2215
+ // body materialization performance. The wrappers are prototype-
2216
+ // chained to the native classes — fine for Hono's own response
2217
+ // path — but they break `instanceof Response` checks for objects
2218
+ // returned by native `fetch()`, because a native Response's
2219
+ // prototype chain doesn't include `_Response.prototype`. This
2220
+ // makes `@huggingface/transformers`'s `toCacheResponse` check
2221
+ // (`response instanceof Response && response.status === 200`)
2222
+ // evaluate to false on fresh fetches, which silently skips
2223
+ // `cache.put` and then throws "Unable to get model file path or
2224
+ // buffer." Disabling the override keeps the native globals
2225
+ // intact and costs us nothing — we don't construct Hono's
2226
+ // `Response` instances directly anywhere in the daemon.
2227
+ overrideGlobalObjects: false,
2489
2228
  });
2490
2229
  logger.info({ port: config.apiPort }, "API server listening");
2491
2230
  void dispatcher.run(); // Start consuming dashboard events as soon as the API is live
@@ -2706,202 +2445,12 @@ async function startup() {
2706
2445
  }
2707
2446
  logger.info(`${APP_NAME} Daemon ready`);
2708
2447
  }
2709
- async function runCatchup(db, dispatcher, config) {
2710
- // Setup gate — on first boot (before rules/management.md exists) we must
2711
- // NOT run the morning routine. Without user/profile.md / rules/management.md
2712
- // the generated today.md is meaningless AND it fires
2713
- // onPromptContextChanged markActiveDmSessionsStale, which destroys the
2714
- // dashboard setup conversation on the user's next turn. Autonomous
2715
- // catchup will run on the first normal boot *after* setup completes.
2716
- const gateReason = dispatcher.isAutonomousAllowed();
2717
- if (gateReason !== null) {
2718
- logger.info({ reason: gateReason }, "Skipping startup catchup — autonomous work paused for setup");
2719
- return {
2720
- postMessagingRoadmapRefresh: false,
2721
- postMessagingRoutines: [],
2722
- postMessagingHourlyCheck: false,
2723
- };
2724
- }
2725
- const now = new Date();
2726
- const tz = config.timezone || undefined;
2727
- const contextDir = getContextDir(config);
2728
- const todayMdPath = join(contextDir, "today.md");
2729
- const { start: agentDayStartUtc, end: agentDayEndUtc } = getAgentDayBoundsUtc(tz, config.dayBoundaryHour, now);
2730
- const skippedPending = discardStalePendingSchedules(db, agentDayStartUtc);
2731
- if (skippedPending > 0) {
2732
- logger.warn({ count: skippedPending }, "Discarded stale pending schedules at startup");
2733
- }
2734
- const runningRecovery = recoverOrphanedRunningSchedules(db, agentDayStartUtc);
2735
- if (runningRecovery.skipped > 0 || runningRecovery.failed > 0) {
2736
- logger.warn(runningRecovery, "Recovered orphaned running schedules without replay");
2737
- }
2738
- // Check if morning routine is needed
2739
- let needsMorning = false;
2740
- if (existsSync(todayMdPath)) {
2741
- const firstLine = readFileSync(todayMdPath, "utf-8").split("\n")[0];
2742
- const today = getAgentDayDateStr(tz, config.dayBoundaryHour, now);
2743
- if (!firstLine.includes(today)) {
2744
- needsMorning = true;
2745
- }
2746
- }
2747
- else {
2748
- needsMorning = true;
2749
- }
2750
- const dueCatchupRoutines = getDueCatchupRoutines(db, config, agentDayStartUtc, agentDayEndUtc, now);
2751
- const needsHourlyCheckCatchup = shouldCatchUpHourlyCheck(db, config, now);
2752
- let ranMorningCatchup = false;
2753
- if (needsMorning) {
2754
- try {
2755
- await dispatcher.summarizeDmSessions();
2756
- }
2757
- catch (err) {
2758
- logger.error({ err }, "DM summarization catchup failed before morning routine");
2759
- }
2760
- logger.info("Stale today.md detected, running morning_routine catchup inline");
2761
- await dispatcher.processInline({
2762
- ...createEvent({
2763
- type: "routine.morning_routine",
2764
- source: "catchup",
2765
- priority: EventPriority.HIGH,
2766
- data: {
2767
- postCatchupRoutines: dueCatchupRoutines,
2768
- postCatchupHourlyCheck: needsHourlyCheckCatchup,
2769
- deferPostMorningCatchupsUntilStartupReady: true,
2770
- },
2771
- }),
2772
- routine: "morning_routine",
2773
- });
2774
- ranMorningCatchup = true;
2775
- if (!hasFreshAgentDayTodayMd(todayMdPath, tz, config.dayBoundaryHour)) {
2776
- logger.warn("Startup morning catchup did not produce a fresh today.md — deferring remaining catchup work");
2777
- return {
2778
- postMessagingRoadmapRefresh: false,
2779
- postMessagingRoutines: [],
2780
- postMessagingHourlyCheck: false,
2781
- };
2782
- }
2783
- return {
2784
- postMessagingRoadmapRefresh: isRoadmapStale(contextDir),
2785
- postMessagingRoutines: dueCatchupRoutines,
2786
- postMessagingHourlyCheck: needsHourlyCheckCatchup,
2787
- };
2788
- }
2789
- if (!ranMorningCatchup && isRoadmapStale(contextDir)) {
2790
- logger.info("Roadmap stale at startup, running roadmap_refresh catchup inline");
2791
- await processRoutineCatchup(dispatcher, "roadmap_refresh");
2792
- }
2793
- return {
2794
- postMessagingRoadmapRefresh: false,
2795
- postMessagingRoutines: dueCatchupRoutines,
2796
- postMessagingHourlyCheck: needsHourlyCheckCatchup,
2797
- };
2798
- }
2799
- async function runPostMessagingCatchup(dispatcher, catchup) {
2800
- if (catchup.postMessagingRoadmapRefresh) {
2801
- logger.info("Running roadmap_refresh catchup after messaging startup");
2802
- await processRoutineCatchup(dispatcher, "roadmap_refresh");
2803
- }
2804
- for (const routine of catchup.postMessagingRoutines) {
2805
- logger.info({ routine }, "Running same-day routine catchup after messaging startup");
2806
- await processRoutineCatchup(dispatcher, routine);
2807
- }
2808
- if (catchup.postMessagingHourlyCheck) {
2809
- logger.info("Triggering hourly_check catchup after messaging startup");
2810
- await dispatcher.triggerHourlyCheck("catchup_startup", { force: false });
2811
- }
2812
- }
2813
- async function processRoutineCatchup(dispatcher, routine) {
2814
- await dispatcher.processInline({
2815
- ...createEvent({
2816
- type: `routine.${routine}`,
2817
- source: "catchup",
2818
- priority: routine === "hourly_check" ? EventPriority.NORMAL : EventPriority.HIGH,
2819
- }),
2820
- routine,
2821
- });
2822
- }
2823
- function getDueCatchupRoutines(db, config, agentDayStartUtc, agentDayEndUtc, now) {
2824
- const tz = config.timezone || undefined;
2825
- const progressMinutes = getAgentDayProgressMinutes(tz, config.dayBoundaryHour, now);
2826
- const dueAt18 = getProgressMinutesForHour(18, config.dayBoundaryHour);
2827
- if (progressMinutes < dueAt18) {
2828
- return [];
2829
- }
2830
- const routines = [];
2831
- const agentDayStartMs = parseSqliteUtcMs(agentDayStartUtc);
2832
- const agentDayLocal = nowInTimezone(tz, new Date(agentDayStartMs));
2833
- const tomorrowLocal = nowInTimezone(tz, new Date(agentDayStartMs + 24 * 60 * 60 * 1000));
2834
- if (!hasActionInWindow(db, "routine.evening_review", agentDayStartUtc, agentDayEndUtc)) {
2835
- routines.push("evening_review");
2836
- }
2837
- if (agentDayLocal.dayOfWeek === 5 &&
2838
- !hasActionInWindow(db, "routine.weekly_review", agentDayStartUtc, agentDayEndUtc)) {
2839
- routines.push("weekly_review");
2840
- }
2841
- if (tomorrowLocal.day === 1 &&
2842
- !hasActionInWindow(db, "routine.monthly_review", agentDayStartUtc, agentDayEndUtc)) {
2843
- routines.push("monthly_review");
2844
- }
2845
- return routines;
2846
- }
2847
- function shouldCatchUpHourlyCheck(db, config, now) {
2848
- if (!config.hourlyCheckEnabled) {
2849
- return false;
2850
- }
2851
- const tz = config.timezone || undefined;
2852
- const local = nowInTimezone(tz, now);
2853
- if (local.hours < config.hourlyCheckActiveStartHour ||
2854
- local.hours >= config.hourlyCheckActiveEndHour ||
2855
- local.hours === config.dayBoundaryHour) {
2856
- return false;
2857
- }
2858
- // Slot anchors to `activeStartHour`, mirroring shouldFireHourlyTickAt
2859
- // in scheduler.ts so the catch-up function picks the same slot the
2860
- // cron callback would have fired at. The earlier branch already
2861
- // returned false when local.hours < activeStartHour, so the offset is
2862
- // always non-negative here.
2863
- const anchorMinutes = config.hourlyCheckActiveStartHour * 60;
2864
- const offsetFromAnchor = local.hours * 60 + local.minutes - anchorMinutes;
2865
- const slotOffsetFromAnchor = Math.floor(offsetFromAnchor / config.hourlyCheckIntervalMinutes) *
2866
- config.hourlyCheckIntervalMinutes;
2867
- const slotMinutesSinceMidnight = anchorMinutes + slotOffsetFromAnchor;
2868
- const dayStartUtc = getAgentDayBoundsUtc(tz, 0, now).start;
2869
- const slotStartMs = parseSqliteUtcMs(dayStartUtc) + slotMinutesSinceMidnight * 60 * 1000;
2870
- const slotStartUtc = formatSqliteDatetime(new Date(slotStartMs));
2871
- return !hasActionInWindow(db, "routine.hourly_check", slotStartUtc, formatSqliteDatetime(now));
2872
- }
2873
- function getProgressMinutesForHour(hour, dayBoundaryHour) {
2874
- const scheduledMinutes = hour * 60;
2875
- const boundaryMinutes = dayBoundaryHour * 60;
2876
- return scheduledMinutes >= boundaryMinutes
2877
- ? scheduledMinutes - boundaryMinutes
2878
- : 24 * 60 - boundaryMinutes + scheduledMinutes;
2879
- }
2880
- function hasFreshAgentDayTodayMd(todayMdPath, timezone, dayBoundaryHour) {
2881
- if (!existsSync(todayMdPath)) {
2882
- return false;
2883
- }
2884
- const firstLine = readFileSync(todayMdPath, "utf-8").split("\n")[0] ?? "";
2885
- const today = getAgentDayDateStr(timezone, dayBoundaryHour);
2886
- return firstLine.includes(today);
2887
- }
2888
- // P22 — read the operator's chosen cadence for skill curation runs.
2889
- // Mirrors the helper in `core/scheduler.ts` so the dispatcher hook here can
2890
- // resolve cadence at runtime without crossing module boundaries.
2891
- function readSkillCurationCadence(db) {
2892
- const row = db
2893
- .prepare(`SELECT value_json FROM runtime_state WHERE key = 'skill_curation.config'`)
2894
- .get();
2895
- if (!row)
2896
- return "weekly";
2897
- try {
2898
- const v = JSON.parse(row.value_json);
2899
- return v.cadence ?? "weekly";
2900
- }
2901
- catch {
2902
- return "weekly";
2903
- }
2904
- }
2448
+ // Catchup (`runCatchup` / `runPostMessagingCatchup`) and the pure schedule
2449
+ // predicates (`getDueCatchupRoutines`, `shouldCatchUpHourlyCheck`,
2450
+ // `getProgressMinutesForHour`, `hasFreshAgentDayTodayMd`,
2451
+ // `readSkillCurationCadence`) live in `./bootstrap/` see
2452
+ // `docs/design/appendices/file-split-plan.md` §10. Imports are at the top
2453
+ // of this file.
2905
2454
  // ── Global safety net ──
2906
2455
  // Catch unhandled rejections from fire-and-forget patterns (void async calls)
2907
2456
  // so they are logged before Node.js 22+ terminates the process.