@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
@@ -0,0 +1,1032 @@
1
+ /**
2
+ * `ScheduledTaskRunner` — owns every non-message dispatch path that
3
+ * routes through the dispatcher's main `dispatch` switch:
4
+ * - `scheduled.task` (generic + repository run + git project doc +
5
+ * today_refresh + morning-routine retry);
6
+ * - `routine.morning_routine` retries (the wake-task fast path);
7
+ * - `routine.roadmap_refresh` (with the cross-request roadmap write
8
+ * lock + skip-on-conflict semantics);
9
+ * - `routine.skill_curation` (P22 §3.4 — optimizer workdir
10
+ * materialization + hard-clamped tool envelope);
11
+ * - the catch-all `executeDefault` for every routine that doesn't
12
+ * have its own dedicated runner method (today_refresh,
13
+ * evening_review, weekly_review, …).
14
+ *
15
+ * Plus the today.md utilities the morning-routine path consults
16
+ * through callbacks: `rotateDayFiles`, `diagnoseTodayMdState`,
17
+ * `hasCurrentAgentDayTodayMd`.
18
+ *
19
+ * Extracted from `core/dispatcher.ts` as part of phase D-2 of
20
+ * `docs/design/appendices/file-split-plan.md`. Pattern B (stateful
21
+ * coordinator): the runner has no mutable state of its own; it
22
+ * borrows lazy accessors for the dispatcher's optimizer hooks
23
+ * (set by `setSkillCurationHooks` after construction) and bridges
24
+ * back into `MorningRoutineRunner.executeMorningRoutine` when a
25
+ * morning-routine retry wake-task fires.
26
+ *
27
+ * Dispatcher entry points served:
28
+ * - `EventDispatcher.dispatch` switches on event type; each non-
29
+ * message branch now calls into a `runner.X()` method here
30
+ * (`executeScheduledTask`, `executeRoadmapRefresh`,
31
+ * `executeSkillCurationRoutine`, `executeDefault`);
32
+ * - `MorningRoutineRunner` uses `rotateDayFiles` /
33
+ * `diagnoseTodayMdState` via the dep callbacks the dispatcher
34
+ * wires at construction time.
35
+ *
36
+ * Shared-state references held:
37
+ * - `getMaterializeOptimizerWorkdir` / `getTeardownOptimizerWorkdir`
38
+ * — lazy accessors; the optimizer hooks are wired by
39
+ * `setSkillCurationHooks` after the dispatcher is constructed.
40
+ * Reading through the closures means the runner sees the current
41
+ * value at call time.
42
+ * - `roadmapWriteLock` — read-only reference to the dispatcher's
43
+ * write-lock manager. The runner calls `acquire` / `release` but
44
+ * does not own the manager's lifecycle.
45
+ */
46
+ import { EventPriority, createEvent, formatSqliteDatetime, getAgentDayDateStr, isBackendId, isKnowledgeImportEvent, isRoutineEvent, resolveProcessKey, } from "@aitne/shared";
47
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
48
+ import { join } from "node:path";
49
+ import { randomUUID } from "node:crypto";
50
+ import { CONTEXT_RELATIVE_PATHS } from "./context-paths.js";
51
+ import { getContextDir } from "../config.js";
52
+ import { cleanupSessionWorkdir, ensureBackendMaterialized, } from "./workdir.js";
53
+ import { readIntegrations } from "../db/integrations-store.js";
54
+ import { getRepository, getRepositoryByLocalPath, recordManagementInitDone, recordManagementScan, } from "../db/repositories-store.js";
55
+ import { runRepositoryManagementInit, runRepositoryManagementScan, } from "./repository-management-docs.js";
56
+ import { routineWindowKeyFromEvent } from "./routine-fetch-window-runner.js";
57
+ import { routineHasWindows } from "./routine-windows.js";
58
+ import { releaseWikiCompileLock } from "./wiki/compile-lock.js";
59
+ import { parseGithubRepoSlug, normalizeRepositoryClassification, normalizeRepositoryCategory, parseRepositoryRunTaskContext, repositoryRunInstructionFilename, safeRepositoryRunDirName, } from "./dispatcher-repository-helpers.js";
60
+ import { createLogger } from "../logging.js";
61
+ const logger = createLogger("dispatcher-scheduled-tasks");
62
+ /**
63
+ * P22 §3.4 step 4 — the optimizer-only allowedTools envelope. Every
64
+ * `routine.skill_curation` event runs the agent with exactly these tools
65
+ * and nothing else. The curl glob is anchored on the daemon's loopback URL
66
+ * so a hook-bypassed request still hits the curation API's chokepoint
67
+ * (Zod, run-token, smoke test); `Read` is required for the agent to
68
+ * consume the inlined data dump under the workdir's `data/` subtree.
69
+ *
70
+ * Kept narrow on purpose: adding any other tool here widens the optimizer's
71
+ * blast radius. If a future signal source needs the agent to write to a
72
+ * different surface, add a new curation API endpoint and let the curl glob
73
+ * cover it — do NOT add `Bash(*)` or `Write` here.
74
+ */
75
+ export const SKILL_CURATION_OPTIMIZER_ALLOWED_TOOLS = [
76
+ "Read",
77
+ "Bash(curl http://localhost:8321/api/skill-curation/*)",
78
+ ];
79
+ /**
80
+ * Read-only tool envelope for `git.project.refresh_architecture`. This agent
81
+ * walks the user's local git worktree at `<task_context.localPath>` to compose
82
+ * the `## Architecture` section of `git/<slug>/overview.md`, and lands the
83
+ * result through `PUT /api/repositories/:id/architecture-section` — the one
84
+ * daemon-side chokepoint. Without this clamp the session would inherit
85
+ * `CLAUDE_DEFAULT_ALLOWED_TOOLS` (Write/Edit/`Bash(git *)`/`Bash(curl *)`),
86
+ * which would let a prompt-injected README or a misbehaving turn mutate the
87
+ * user's repository (e.g. `git reset --hard`, `git push --force`, arbitrary
88
+ * `Write` to source files) OR exfiltrate via other Autonomous daemon APIs
89
+ * (`POST /api/notify` to DM the owner with attacker content, `POST
90
+ * /api/observations` to inject fake observations, `PUT /api/obsidian/notes`
91
+ * to overwrite vault notes, etc.). The Architecture analysis itself only
92
+ * needs to *read* the worktree.
93
+ *
94
+ * What is INCLUDED and why:
95
+ * - `Read` / `Glob` / `Grep` — the task-flow's only durable need (README,
96
+ * manifests, source files, design docs). `Glob` covers the literal
97
+ * `ls <localPath>` step without giving the agent shell access.
98
+ * - `Bash(curl http://localhost:8321/api/repositories/*\/architecture-section*)`
99
+ * — endpoint-pinned write path. The SDK's prefix-glob layer forbids the
100
+ * command from reaching ANY other host, port, or daemon-API namespace.
101
+ * The curl PreToolUse hook adds defense-in-depth (rechecks host/port,
102
+ * denies connection-override flags); the API risk classifier supplies
103
+ * the floor (only `PUT .../architecture-section` is Autonomous under
104
+ * `/api/repositories/`; everything else inherits Approve and 401s a
105
+ * tokenless agent curl). Port is hardcoded to the daemon's default
106
+ * `8321` matching the optimizer-clamp convention; operators who change
107
+ * `PA_API_PORT` accept the gap consciously (the same constraint applies
108
+ * to `SKILL_CURATION_OPTIMIZER_ALLOWED_TOOLS`).
109
+ * - `Bash(jq *)` — body construction. The PUT body is
110
+ * `{"markdown":"..."}` and the markdown contains arbitrary characters
111
+ * that must be JSON-escaped; `jq -n --arg md "$body" '{markdown:$md}'`
112
+ * is the only robust escape path under a no-`Write` envelope. The jq
113
+ * hook denies `--slurpfile` / `--rawfile` / `-L` / `env`-filter
114
+ * exfiltration.
115
+ *
116
+ * What is INTENTIONALLY EXCLUDED:
117
+ * - `Write` / `Edit` — would let the agent write anywhere, including the
118
+ * user's checked-out worktree. The chokepoint is the daemon API.
119
+ * - `Bash(git *)` — even read-only verbs let the agent chain into
120
+ * `git push --force`, `git reset --hard`, `git checkout --`, etc. via
121
+ * shell separators; the `always-disallowed.ts` classifier hook catches
122
+ * `rm -rf` / `sudo` / pipe-to-shell but does NOT classify mutating git
123
+ * subcommands. The Architecture analysis doesn't need git CLI:
124
+ * filesystem reads via `Read` / `Glob` suffice for module / data-flow /
125
+ * build / test-surface description.
126
+ * - `Bash(ls *)` — `Glob` covers directory enumeration without shell
127
+ * access.
128
+ * - `Skill` / `WebSearch` — not referenced by the task-flow; smaller
129
+ * surface is better.
130
+ *
131
+ * Defense-in-depth layering:
132
+ * - SDK `allowedTools` (this list) — first gate. Prefix glob forces the
133
+ * curl command to literally begin with the architecture-section URL.
134
+ * - SDK `disallowedTools` — `ALWAYS_DISALLOWED_TOOLS` +
135
+ * `config.disallowedTools` still merge on top.
136
+ * - PreToolUse hooks — curl localhost-only + jq exfil bans + Write/Edit
137
+ * context-dir chokepoint stay armed. `claude-code-core.ts` forces
138
+ * strict hook mode (curl + jq hooks re-enabled) whenever any
139
+ * `allowedToolsOverride` is active, so Allow-mode operators do not
140
+ * inadvertently widen this surface — see the `optimizerClampActive`
141
+ * branch.
142
+ * - Allow mode bypass — `claude-code-core.ts` detects the non-empty
143
+ * `allowedToolsOverride` and forces `permissionMode: "dontAsk"` for
144
+ * this run, stripping `bypassPermissions` even if the operator has
145
+ * Allow mode globally enabled.
146
+ * - API risk classifier — `PUT /api/repositories/:id/architecture-section`
147
+ * is RiskTier.Autonomous (agent-callable, no Bearer required) but
148
+ * enforces marker-bracketed body validation and 64KB size cap
149
+ * server-side. All sibling routes under `/api/repositories/` inherit
150
+ * the blanket Approve tier and 401 a tokenless agent curl.
151
+ *
152
+ * Multi-request defenses (closed in `claude-tool-collection.ts:bashCurlHook`,
153
+ * benefit every clamped session inheriting the curl hook):
154
+ * 1. **Shell-chained second curl** — `curl ARCH_URL ; curl
155
+ * http://localhost:8321/api/notify -d @evil` and the `&&` / `||` /
156
+ * `|` / newline / backtick / `$(…)` variants. The hook counts
157
+ * `curl` tokens anchored at command-start positions (mirroring the
158
+ * `cmdStart` regex in `safety/always-disallowed.ts`) and blocks any
159
+ * command with more than one anchored `curl`. A single
160
+ * `jq -n '{markdown:$md}' | curl URL -d @-` pipeline still counts
161
+ * as ONE curl token and is allowed.
162
+ * 2. **`--next` / `-:` URL multiplexing** — curl's same-process URL
163
+ * separator that resets option state per transaction. Hook-blocked
164
+ * via flag regex (covers `--next`, `--next=URL`, and the `-:`
165
+ * short form).
166
+ * 3. **Multi-positional URLs** — `curl URL1 URL2 -X PUT -d @body`
167
+ * sends identical options to both URLs sequentially. The hook
168
+ * tokenizes the command at the top level (outside paired single /
169
+ * double quotes) and blocks when more than one URL appears as a
170
+ * top-level token. URLs that legitimately appear inside `-d '…'`
171
+ * / `-H "…"` strings — e.g. external links inside the architecture
172
+ * markdown body — are not counted and not host-checked, so the
173
+ * agent can reference external code in its analysis.
174
+ *
175
+ * Per-backend support:
176
+ * - Claude (`ClaudeCodeCore`) — consumes this list verbatim.
177
+ * - Codex / Gemini — no per-execute allowedTools surface today (mirrors
178
+ * `AgentExecuteParams.allowedToolsOverride` JSDoc). The default
179
+ * `process_backend_config` seed binds `git.project.refresh_architecture`
180
+ * to the medium tier (Sonnet), so the realistic risk surface today is
181
+ * Claude-only. An operator who reroutes this process key to a
182
+ * non-Claude backend via `/settings/models` accepts the gap
183
+ * consciously.
184
+ */
185
+ export const REFRESH_ARCHITECTURE_ALLOWED_TOOLS = [
186
+ "Read",
187
+ "Glob",
188
+ "Grep",
189
+ "Bash(curl http://localhost:8321/api/repositories/*/architecture-section*)",
190
+ "Bash(jq *)",
191
+ ];
192
+ /**
193
+ * Backends that honor the per-execute `allowedToolsOverride` clamp end-to-
194
+ * end. Claude consumes the list verbatim through the SDK's `dontAsk` +
195
+ * `allowedTools` posture and the dispatcher swaps Allow mode back to
196
+ * strict for the run. Codex / Gemini have no per-execute allowedTools
197
+ * surface today (see `AgentExecuteParams.allowedToolsOverride` JSDoc),
198
+ * so the clamp would silently drop and the read-only contract would
199
+ * become a no-op. We refuse-at-execute rather than silently widen the
200
+ * envelope; the operator sees an `agent_actions` row of action_type
201
+ * `scheduled_task_clamp_unsupported` and a clear log line.
202
+ *
203
+ * Add a backend here only after verifying its core threads
204
+ * `allowedToolsOverride` through to its concrete deny enforcement layer
205
+ * — NOT just into the CLI flag set.
206
+ */
207
+ export const TOOL_CLAMP_SUPPORTING_BACKENDS = new Set([
208
+ "claude",
209
+ ]);
210
+ export class ScheduledTaskRunner {
211
+ db;
212
+ config;
213
+ contextBuilder;
214
+ agentRouter;
215
+ prompt;
216
+ errorRouter;
217
+ resultProcessor;
218
+ morningRoutine;
219
+ fetchWindowRunner;
220
+ roadmapWriteLock;
221
+ writeTracker;
222
+ getConfiguredServices;
223
+ getActiveMailAccounts;
224
+ getMaterializeOptimizerWorkdir;
225
+ getTeardownOptimizerWorkdir;
226
+ constructor(deps) {
227
+ this.db = deps.db;
228
+ this.config = deps.config;
229
+ this.contextBuilder = deps.contextBuilder;
230
+ this.agentRouter = deps.agentRouter;
231
+ this.prompt = deps.prompt;
232
+ this.errorRouter = deps.errorRouter;
233
+ this.resultProcessor = deps.resultProcessor;
234
+ this.morningRoutine = deps.morningRoutine;
235
+ this.fetchWindowRunner = deps.fetchWindowRunner;
236
+ this.roadmapWriteLock = deps.roadmapWriteLock;
237
+ this.writeTracker = deps.writeTracker;
238
+ this.getConfiguredServices = deps.getConfiguredServices;
239
+ this.getActiveMailAccounts = deps.getActiveMailAccounts;
240
+ this.getMaterializeOptimizerWorkdir = deps.getMaterializeOptimizerWorkdir;
241
+ this.getTeardownOptimizerWorkdir = deps.getTeardownOptimizerWorkdir;
242
+ }
243
+ // ────── Repository run + scheduled task entry points ──────
244
+ buildRepositoryRunPrompt(ctx) {
245
+ const lines = [
246
+ "{context}",
247
+ "",
248
+ "## Repository Run",
249
+ `Repository id: ${ctx.repositoryId}`,
250
+ `Repository slug: ${ctx.slug}`,
251
+ `GitHub repo: ${ctx.githubRepo ?? "(none)"}`,
252
+ `Local path: ${ctx.localPath ?? "(none)"}`,
253
+ `Workdir mode: ${ctx.workdirMode}`,
254
+ `Trigger source: ${ctx.triggerSource}`,
255
+ ];
256
+ if (ctx.triggerId || ctx.triggerName || ctx.triggerEventType) {
257
+ lines.push("", "## Trigger", `Trigger id: ${ctx.triggerId ?? "(manual)"}`, `Trigger name: ${ctx.triggerName ?? "(manual)"}`, `Event type: ${ctx.triggerEventType ?? "(manual)"}`);
258
+ if (ctx.triggerEventPayload !== undefined) {
259
+ lines.push("", "<trigger_event_payload>", JSON.stringify(ctx.triggerEventPayload, null, 2), "</trigger_event_payload>");
260
+ }
261
+ }
262
+ lines.push("", "## User Prompt", ctx.prompt);
263
+ return lines.join("\n");
264
+ }
265
+ prepareRepositoryRunSessionDir(ctx, backendId) {
266
+ if (ctx.workdirMode === "local-clone") {
267
+ if (!ctx.localPath) {
268
+ throw new Error("Repository local-clone run missing localPath");
269
+ }
270
+ ensureBackendMaterialized(this.config.workspaceDir, ctx.localPath, backendId, "scheduled.task", "agent.task", this.getConfiguredServices(), this.getActiveMailAccounts(), readIntegrations(this.db), this.config.character);
271
+ return { sessionDir: ctx.localPath, cleanup: false };
272
+ }
273
+ if (!ctx.instructionMd) {
274
+ throw new Error("Repository temp run missing instructionMd");
275
+ }
276
+ const sessionDir = join(this.config.dataDir, "run", `${safeRepositoryRunDirName(ctx.slug)}-${Date.now()}-${randomUUID().slice(0, 8)}`);
277
+ mkdirSync(sessionDir, { recursive: true, mode: 0o700 });
278
+ try {
279
+ ensureBackendMaterialized(this.config.workspaceDir, sessionDir, backendId, "scheduled.task", "agent.task", this.getConfiguredServices(), this.getActiveMailAccounts(), readIntegrations(this.db), this.config.character);
280
+ writeFileSync(join(sessionDir, repositoryRunInstructionFilename(backendId)), ctx.instructionMd, "utf-8");
281
+ return { sessionDir, cleanup: true };
282
+ }
283
+ catch (err) {
284
+ cleanupSessionWorkdir(sessionDir);
285
+ throw err;
286
+ }
287
+ }
288
+ async executeRepositoryRunTask(event, ctx) {
289
+ const context = await this.contextBuilder.build(event);
290
+ const processKey = "agent.task";
291
+ const requestedTier = event.requestedModel
292
+ ? (event.requestedModel === "sonnet" ? "medium" : "high")
293
+ : undefined;
294
+ const internalBackendOverride = event.requestedBackendId
295
+ && isBackendId(event.requestedBackendId)
296
+ && typeof event.requestedModelId === "string"
297
+ ? {
298
+ requestedBackendId: event.requestedBackendId,
299
+ requestedModelId: event.requestedModelId,
300
+ }
301
+ : {};
302
+ const binding = this.agentRouter.resolveBinding(event, {
303
+ processKey,
304
+ requestedTier,
305
+ ...internalBackendOverride,
306
+ });
307
+ const prompt = this.buildRepositoryRunPrompt(ctx);
308
+ const { sessionDir, cleanup } = this.prepareRepositoryRunSessionDir(ctx, binding.main.backendId);
309
+ try {
310
+ const result = await this.errorRouter.executeWithRetry(() => this.agentRouter.execute({
311
+ prompt,
312
+ context,
313
+ event,
314
+ processKey,
315
+ requestedTier,
316
+ preResolvedBinding: binding,
317
+ reassemblePrompt: () => prompt,
318
+ sessionDir,
319
+ workdirEventType: "scheduled.task",
320
+ workdirProcessKey: processKey,
321
+ ...internalBackendOverride,
322
+ }), event);
323
+ await this.resultProcessor.processResult(result, event);
324
+ }
325
+ finally {
326
+ if (cleanup) {
327
+ cleanupSessionWorkdir(sessionDir);
328
+ }
329
+ }
330
+ }
331
+ /**
332
+ * Execute a scheduled task with the model specified when the task was
333
+ * registered via POST /api/schedule.
334
+ *
335
+ * Morning-routine retry tasks take a dedicated fast path: they skip
336
+ * the generic scheduled.task prompt and run the *real* morning routine
337
+ * flow via executeMorningRoutine, so the retry carries the same rotateDayFiles
338
+ * / prompt selection / roadmap-refresh chain as the cron-fired path.
339
+ */
340
+ async executeScheduledTask(event) {
341
+ // Morning-routine retry detection: if taskContext says this wake
342
+ // task is a morning-routine retry, dispatch through executeMorningRoutine
343
+ // with a synthesized RoutineEvent instead of the generic flow.
344
+ const taskCtx = event.taskContext;
345
+ if (taskCtx &&
346
+ typeof taskCtx === "object" &&
347
+ taskCtx.routine === "morning_routine") {
348
+ await this.handleMorningRoutineRetry(event, taskCtx);
349
+ return;
350
+ }
351
+ if (taskCtx &&
352
+ typeof taskCtx === "object" &&
353
+ taskCtx.routine === "today_refresh") {
354
+ await this.executeScheduledRoutine(event, "today_refresh");
355
+ return;
356
+ }
357
+ const repositoryRunCtx = parseRepositoryRunTaskContext(taskCtx);
358
+ if (repositoryRunCtx) {
359
+ await this.executeRepositoryRunTask(event, repositoryRunCtx);
360
+ return;
361
+ }
362
+ if (await this.executeGitProjectDocTaskIfApplicable(event, taskCtx)) {
363
+ return;
364
+ }
365
+ const context = await this.contextBuilder.build(event);
366
+ const processKeyOverride = taskCtx
367
+ && typeof taskCtx === "object"
368
+ && typeof taskCtx.processKey === "string"
369
+ ? taskCtx.processKey
370
+ : null;
371
+ const processKey = (processKeyOverride ?? resolveProcessKey(event));
372
+ const promptKey = processKeyOverride ?? event.type;
373
+ const requestedTier = event.requestedModel
374
+ ? (event.requestedModel === "sonnet" ? "medium" : "high")
375
+ : undefined;
376
+ const internalBackendOverride = event.requestedBackendId
377
+ && isBackendId(event.requestedBackendId)
378
+ && typeof event.requestedModelId === "string"
379
+ ? {
380
+ requestedBackendId: event.requestedBackendId,
381
+ requestedModelId: event.requestedModelId,
382
+ }
383
+ : {};
384
+ const binding = this.agentRouter.resolveBinding(event, {
385
+ processKey,
386
+ requestedTier,
387
+ ...internalBackendOverride,
388
+ });
389
+ const reassemblePrompt = (bid) => this.prompt.assemble(promptKey, processKey, bid);
390
+ const prompt = reassemblePrompt(binding.main.backendId);
391
+ // Daily-git-management safety clamp — see
392
+ // `REFRESH_ARCHITECTURE_ALLOWED_TOOLS` JSDoc. The check is on
393
+ // `processKey` (carried by the agent_schedule row's task_context)
394
+ // rather than `event.source` so a downstream rename of the schedule
395
+ // source string cannot silently widen the envelope; the process key
396
+ // is the contract surface.
397
+ const refreshArchitectureOverride = processKey === "git.project.refresh_architecture"
398
+ ? REFRESH_ARCHITECTURE_ALLOWED_TOOLS
399
+ : undefined;
400
+ if (refreshArchitectureOverride
401
+ && !this.clampSupportedByBackend(processKey, binding.main.backendId, event.correlationId, "REFRESH_ARCHITECTURE_ALLOWED_TOOLS")) {
402
+ // Refuse-at-execute. The audit row + log line are written inside
403
+ // the guard; mark the schedule row done so the operator's only
404
+ // path to "fix it" is via /settings/models, not by waiting for a
405
+ // retry storm.
406
+ this.markScheduledTaskCompleted(event);
407
+ return;
408
+ }
409
+ const result = await this.errorRouter.executeWithRetry(() => this.agentRouter.execute({
410
+ prompt,
411
+ context,
412
+ event,
413
+ processKey,
414
+ requestedTier,
415
+ preResolvedBinding: binding,
416
+ reassemblePrompt,
417
+ ...(refreshArchitectureOverride
418
+ ? { allowedToolsOverride: refreshArchitectureOverride }
419
+ : {}),
420
+ }), event);
421
+ await this.resultProcessor.processResult(result, event);
422
+ }
423
+ /**
424
+ * Legacy git project documentation tasks used to run as autonomous Claude
425
+ * task-flows. That made file creation probabilistic: the backend could
426
+ * finish "successfully" without calling the daemon context API, or fail
427
+ * before receiving the `<task_context>` block. The daemon now owns these
428
+ * writes directly, matching the manual Daily git management buttons and
429
+ * the repository-management cron.
430
+ */
431
+ async executeGitProjectDocTaskIfApplicable(event, taskCtx) {
432
+ const processKey = this.resolveGitProjectDocProcessKey(event, taskCtx);
433
+ if (!processKey)
434
+ return false;
435
+ const ctx = taskCtx && typeof taskCtx === "object"
436
+ ? taskCtx
437
+ : {};
438
+ const repo = this.resolveRepositoryForGitProjectDocTask(ctx);
439
+ const triggerSource = typeof ctx.triggerSource === "string"
440
+ ? ctx.triggerSource
441
+ : null;
442
+ const isManagementSource = triggerSource === "repository_management_cron" ||
443
+ triggerSource === "repository_management_manual";
444
+ try {
445
+ if (processKey === "git.project.init") {
446
+ const result = runRepositoryManagementInit({
447
+ db: this.db,
448
+ repo,
449
+ contextDir: getContextDir(this.config, this.db),
450
+ timezone: this.config.timezone || undefined,
451
+ writeTracker: this.writeTracker,
452
+ });
453
+ if (isManagementSource) {
454
+ recordManagementInitDone(this.db, repo.id);
455
+ }
456
+ this.markScheduledTaskCompleted(event);
457
+ logger.info({
458
+ scheduleId: event.scheduleId ?? null,
459
+ repositoryId: repo.id,
460
+ slug: repo.slug,
461
+ result: result.status,
462
+ architectureScheduleId: result.architectureScheduleId,
463
+ }, "Handled git.project.init with direct markdown writer");
464
+ }
465
+ else {
466
+ const lookbackHours = typeof ctx.lookbackHours === "number"
467
+ && Number.isFinite(ctx.lookbackHours)
468
+ && ctx.lookbackHours > 0
469
+ ? ctx.lookbackHours
470
+ : undefined;
471
+ const result = await runRepositoryManagementScan({
472
+ db: this.db,
473
+ repo,
474
+ contextDir: getContextDir(this.config, this.db),
475
+ timezone: this.config.timezone || undefined,
476
+ lookbackHours,
477
+ writeTracker: this.writeTracker,
478
+ });
479
+ if (isManagementSource) {
480
+ recordManagementScan(this.db, repo.id, result.status === "skipped_no_activity" ? "skipped_no_activity" : "ok");
481
+ }
482
+ this.markScheduledTaskCompleted(event);
483
+ logger.info({
484
+ scheduleId: event.scheduleId ?? null,
485
+ repositoryId: repo.id,
486
+ slug: repo.slug,
487
+ result: result.status,
488
+ journalPath: result.journalPath,
489
+ }, "Handled git.project.update with direct markdown writer");
490
+ }
491
+ return true;
492
+ }
493
+ catch (err) {
494
+ if (isManagementSource) {
495
+ try {
496
+ recordManagementScan(this.db, repo.id, "failed");
497
+ }
498
+ catch (recordErr) {
499
+ logger.error({ err: recordErr, repositoryId: repo.id }, "Failed to record repository management direct-writer failure");
500
+ }
501
+ }
502
+ if (event.scheduleId) {
503
+ this.db
504
+ .prepare("UPDATE agent_schedule SET status = 'failed' WHERE id = ? AND status = 'running'")
505
+ .run(event.scheduleId);
506
+ }
507
+ logger.error({ err, scheduleId: event.scheduleId ?? null, repositoryId: repo.id }, "Git project documentation direct writer failed");
508
+ throw err;
509
+ }
510
+ }
511
+ resolveGitProjectDocProcessKey(event, taskCtx) {
512
+ const ctxProcessKey = taskCtx &&
513
+ typeof taskCtx === "object" &&
514
+ typeof taskCtx.processKey === "string"
515
+ ? taskCtx.processKey
516
+ : null;
517
+ const value = ctxProcessKey ?? event.source;
518
+ return value === "git.project.init" || value === "git.project.update"
519
+ ? value
520
+ : null;
521
+ }
522
+ resolveRepositoryForGitProjectDocTask(ctx) {
523
+ const repositoryId = typeof ctx.repositoryId === "string"
524
+ ? ctx.repositoryId
525
+ : null;
526
+ if (repositoryId) {
527
+ const byId = getRepository(this.db, repositoryId);
528
+ if (byId)
529
+ return byId;
530
+ }
531
+ const localPath = typeof ctx.localPath === "string"
532
+ ? ctx.localPath
533
+ : typeof ctx.repository?.localPath === "string"
534
+ ? ctx.repository.localPath
535
+ : null;
536
+ if (localPath) {
537
+ const byPath = getRepositoryByLocalPath(this.db, localPath);
538
+ if (byPath)
539
+ return byPath;
540
+ }
541
+ const slug = typeof ctx.slug === "string"
542
+ ? ctx.slug
543
+ : typeof ctx.repository?.slug === "string"
544
+ ? ctx.repository.slug
545
+ : null;
546
+ if (!slug || !localPath) {
547
+ throw new Error("git project documentation task requires repositoryId or slug/localPath task context");
548
+ }
549
+ const githubRepo = typeof ctx.githubRepo === "string"
550
+ ? ctx.githubRepo
551
+ : typeof ctx.repository?.githubRepo === "string"
552
+ ? ctx.repository.githubRepo
553
+ : null;
554
+ const [githubOwner, githubRepoName] = parseGithubRepoSlug(githubRepo);
555
+ const now = Date.now();
556
+ return {
557
+ id: repositoryId ?? (githubRepo ? `github:${githubRepo}` : `local:${slug}`),
558
+ githubOwner,
559
+ githubRepo: githubRepoName,
560
+ githubAccount: null,
561
+ localPath,
562
+ localOnly: githubRepo === null,
563
+ displayName: typeof ctx.displayName === "string" ? ctx.displayName : slug,
564
+ classification: normalizeRepositoryClassification(ctx.classification),
565
+ category: normalizeRepositoryCategory(ctx.category),
566
+ pollPriority: "normal",
567
+ pollIntervalSec: null,
568
+ slug,
569
+ createdAt: now,
570
+ updatedAt: now,
571
+ };
572
+ }
573
+ markScheduledTaskCompleted(event) {
574
+ if (!event.scheduleId)
575
+ return;
576
+ this.db
577
+ .prepare("UPDATE agent_schedule SET status = 'completed' WHERE id = ? AND status = 'running'")
578
+ .run(event.scheduleId);
579
+ }
580
+ /**
581
+ * Defense-in-depth gate for per-execute tool clamps. When the
582
+ * dispatcher pins an `allowedToolsOverride` for a known-safe envelope
583
+ * (refresh_architecture, skill_curation) the clamp MUST hold; if the
584
+ * router resolves to a backend that ignores per-execute clamps the
585
+ * call would silently widen back to the default tool surface and the
586
+ * read-only contract documented in the clamp's JSDoc would dissolve.
587
+ *
588
+ * Returns `true` when the resolved main backend honors clamps (the
589
+ * caller should pass the override through to `execute`). Returns
590
+ * `false` when the operator has rebound the process key to a backend
591
+ * we cannot trust — the caller bails out of the execute, an
592
+ * `agent_actions` row records the refusal for the audit log, and an
593
+ * error-level log line surfaces the misconfiguration immediately.
594
+ *
595
+ * Implementation note: the audit row uses `result = 'failed'` to
596
+ * match the `blocked_absolute` precedent — the `agent_actions.result`
597
+ * CHECK constraint only permits the canonical settle states
598
+ * (success / failed / partial / skipped / in_progress); a literal
599
+ * `"blocked"` here would silently violate the constraint and the
600
+ * try/catch would swallow the audit. The `action_type` is the
601
+ * discriminator that lets dashboards / queries distinguish a "blocked
602
+ * by clamp" row from a real agent failure.
603
+ */
604
+ clampSupportedByBackend(processKey, backendId, correlationId, clampName) {
605
+ if (TOOL_CLAMP_SUPPORTING_BACKENDS.has(backendId))
606
+ return true;
607
+ logger.error({ processKey, backendId, clampName, correlationId }, "Refusing scheduled task: process key carries a per-execute tool clamp that the resolved backend cannot enforce. Reconfigure /settings/models to bind this process key to a backend in TOOL_CLAMP_SUPPORTING_BACKENDS (currently: claude) or remove the clamp.");
608
+ try {
609
+ const detail = {
610
+ process_key: processKey,
611
+ backend: backendId,
612
+ clamp: clampName,
613
+ supported_backends: Array.from(TOOL_CLAMP_SUPPORTING_BACKENDS),
614
+ correlation_id: correlationId ?? null,
615
+ reason: "allowedToolsOverride is not enforceable on this backend " +
616
+ "(no per-execute allowedTools surface); refused at dispatch.",
617
+ };
618
+ this.db
619
+ .prepare(`INSERT INTO agent_actions
620
+ (action_type, trigger, result, detail, started_at, completed_at)
621
+ VALUES ('scheduled_task_clamp_unsupported', 'autonomous', 'failed', json(?), datetime('now'), datetime('now'))`)
622
+ .run(JSON.stringify(detail));
623
+ }
624
+ catch (err) {
625
+ logger.warn({ err }, "Failed to record clamp_unsupported audit row");
626
+ }
627
+ return false;
628
+ }
629
+ async executeScheduledRoutine(event, routine) {
630
+ const routineEvent = {
631
+ ...createEvent({
632
+ type: `routine.${routine}`,
633
+ source: typeof event.taskContext.source === "string"
634
+ ? event.taskContext.source
635
+ : event.source,
636
+ priority: EventPriority.NORMAL,
637
+ correlationId: event.correlationId,
638
+ data: {
639
+ ...event.taskContext,
640
+ scheduleId: event.scheduleId ?? null,
641
+ },
642
+ }),
643
+ routine,
644
+ ...(event.requestedModel ? { requestedModel: event.requestedModel } : {}),
645
+ };
646
+ try {
647
+ await this.executeDefault(routineEvent);
648
+ if (event.scheduleId) {
649
+ this.db
650
+ .prepare("UPDATE agent_schedule SET status = 'completed' WHERE id = ? AND status = 'running'")
651
+ .run(event.scheduleId);
652
+ }
653
+ }
654
+ catch (err) {
655
+ if (event.scheduleId) {
656
+ this.db
657
+ .prepare("UPDATE agent_schedule SET status = 'failed' WHERE id = ? AND status = 'running'")
658
+ .run(event.scheduleId);
659
+ }
660
+ throw err;
661
+ }
662
+ }
663
+ /**
664
+ * Handle a morning-routine retry wake task.
665
+ *
666
+ * Steps:
667
+ * 1. Early skip: if today.md already exists (e.g., the cron-fired
668
+ * morning routine raced us to it), mark this wake task completed
669
+ * without running the agent — saves one Opus session.
670
+ * 2. Synthesize a RoutineEvent with `event.data.retryCount` carrying
671
+ * the current attempt number, so that the recursive
672
+ * scheduleMorningRetry call from executeMorningRoutine can increment the
673
+ * retry chain naturally via the event.data code path.
674
+ * 3. Invoke executeMorningRoutine — this reuses the full morning-routine flow
675
+ * (rotateDayFiles, prompt selection, agent execute, post-result
676
+ * today.md check, roadmap_refresh emission).
677
+ * 4. Mark the wake task row completed. processResult inside the
678
+ * executeMorningRoutine call operates on the synthetic RoutineEvent, which
679
+ * is not an AgentTaskEvent, so it does not touch scheduleId — we
680
+ * must do it ourselves.
681
+ */
682
+ async handleMorningRoutineRetry(event, taskCtx) {
683
+ const retryCount = Number(taskCtx.retryCount ?? 0);
684
+ // O1: early skip only when the current agent day's today.md already exists
685
+ if (this.hasCurrentAgentDayTodayMd()) {
686
+ logger.info({
687
+ retryCount,
688
+ originalCorrelationId: taskCtx.originalCorrelationId,
689
+ }, "Morning routine retry skipped — today.md already exists (cron likely raced us)");
690
+ if (event.scheduleId) {
691
+ this.db
692
+ .prepare("UPDATE agent_schedule SET status = 'completed' WHERE id = ? AND status = 'running'")
693
+ .run(event.scheduleId);
694
+ }
695
+ return;
696
+ }
697
+ // Synthesize a RoutineEvent for executeMorningRoutine. event.data.retryCount
698
+ // carries the previous attempt so executeMorningRoutine → scheduleMorningRetry
699
+ // can increment properly. correlationId tracks back to the original
700
+ // cron morning_routine for log correlation.
701
+ const synthEvent = {
702
+ ...createEvent({
703
+ type: "routine.morning_routine",
704
+ source: typeof taskCtx.source === "string"
705
+ ? taskCtx.source
706
+ : retryCount > 0
707
+ ? `morning_routine_retry_${retryCount}`
708
+ : "scheduled_morning_routine",
709
+ priority: retryCount > 0 ? EventPriority.NORMAL : EventPriority.HIGH,
710
+ correlationId: taskCtx.originalCorrelationId ?? event.correlationId,
711
+ data: {
712
+ ...(retryCount > 0 ? { retryCount, isRetry: true } : {}),
713
+ ...(Array.isArray(taskCtx.postCatchupRoutines)
714
+ ? { postCatchupRoutines: taskCtx.postCatchupRoutines }
715
+ : {}),
716
+ ...(taskCtx.postCatchupHourlyCheck === true
717
+ ? { postCatchupHourlyCheck: true }
718
+ : {}),
719
+ ...(typeof taskCtx.source === "string"
720
+ ? { queuedSource: taskCtx.source }
721
+ : {}),
722
+ },
723
+ }),
724
+ routine: "morning_routine",
725
+ };
726
+ logger.info({ retryCount, correlationId: synthEvent.correlationId }, "Morning routine retry — routing to executeMorningRoutine with synthesized RoutineEvent");
727
+ await this.morningRoutine.executeMorningRoutine(synthEvent);
728
+ // Mark the wake task row completed — executeMorningRoutine doesn't know about
729
+ // scheduleId since it received a RoutineEvent, not an AgentTaskEvent.
730
+ if (event.scheduleId) {
731
+ this.db
732
+ .prepare("UPDATE agent_schedule SET status = 'completed' WHERE id = ? AND status = 'running'")
733
+ .run(event.scheduleId);
734
+ }
735
+ }
736
+ hasCurrentAgentDayTodayMd() {
737
+ return this.diagnoseTodayMdState().kind === "fresh";
738
+ }
739
+ /**
740
+ * Inspect today.md and report its state relative to the current agent-day.
741
+ * Used by the post-routine retry gate so the log can distinguish between
742
+ * "file is missing" and "file has stale H1 date", which are different
743
+ * failure modes (process crash vs. format-confusion bug).
744
+ */
745
+ diagnoseTodayMdState() {
746
+ const todayPath = join(getContextDir(this.config, this.db), "today.md");
747
+ if (!existsSync(todayPath)) {
748
+ return { kind: "missing" };
749
+ }
750
+ const content = readFileSync(todayPath, "utf-8");
751
+ const writtenDate = content.match(/^#.*(\d{4}-\d{2}-\d{2})/)?.[1];
752
+ if (!writtenDate) {
753
+ return { kind: "no_h1_date" };
754
+ }
755
+ const expectedAgentDay = getAgentDayDateStr(this.config.timezone || undefined, this.config.dayBoundaryHour);
756
+ if (writtenDate !== expectedAgentDay) {
757
+ return { kind: "wrong_date", writtenDate, expectedAgentDay };
758
+ }
759
+ return { kind: "fresh" };
760
+ }
761
+ /**
762
+ * Rotate day files before Morning Routine:
763
+ * 1. today.md → schedule/YYYY-MM-DD.md (archive)
764
+ * 2. today.md → yesterday.md (rename for context injection)
765
+ *
766
+ * After this, ContextBuilder will read yesterday.md as <yesterday>
767
+ * and today.md will not exist (agent generates it fresh).
768
+ */
769
+ rotateDayFiles() {
770
+ const contextDir = getContextDir(this.config, this.db);
771
+ const todayPath = join(contextDir, "today.md");
772
+ if (!existsSync(todayPath))
773
+ return;
774
+ const content = readFileSync(todayPath, "utf-8");
775
+ const dateStr = content.match(/^#.*(\d{4}-\d{2}-\d{2})/)?.[1];
776
+ // Skip if today.md is already today's date (no rotation needed)
777
+ const todayDateStr = getAgentDayDateStr(this.config.timezone || undefined, this.config.dayBoundaryHour);
778
+ if (dateStr === todayDateStr)
779
+ return;
780
+ if (!dateStr)
781
+ return;
782
+ // B-007 §5.9 — mechanical copy to schedule/ is retired. The only
783
+ // rotation artifact we preserve is a DB snapshot of the closing
784
+ // today.md; the synthesized `daily/YYYY-MM-DD.md` is written later by
785
+ // the morning routine from yesterday.md + SQLite event records.
786
+ // 1. Snapshot to DB for rebuild safety
787
+ try {
788
+ this.db
789
+ .prepare("INSERT INTO md_file_snapshots (file_path, content, trigger) VALUES (?, ?, ?)")
790
+ .run("today", content, "day_rotation");
791
+ }
792
+ catch (err) {
793
+ logger.warn({ err }, "Failed to save rotation snapshot");
794
+ }
795
+ // 2. Rename today.md → yesterday.md
796
+ const yesterdayPath = join(contextDir, CONTEXT_RELATIVE_PATHS.yesterday);
797
+ renameSync(todayPath, yesterdayPath);
798
+ logger.info({ archived: `schedule/${dateStr}.md` }, "Day files rotated");
799
+ }
800
+ /**
801
+ * Roadmap-refresh execution with an exclusive cross-request write
802
+ * lock. The lockId is surfaced to the session context as
803
+ * `<roadmap_write_lock_id>` so the task-flow PUT / PATCH calls can
804
+ * pass `X-Lock-Id` and other concurrent flows (DM handler, evening
805
+ * sweeper) that attempt to write `/api/context/roadmap` during the
806
+ * refresh receive a 409.
807
+ *
808
+ * If the lock cannot be acquired (another session is mid-write), the
809
+ * refresh is skipped — `emitRoadmapRefresh` will retry on the next
810
+ * qualifying signal (dedup window permitting). This is the correct
811
+ * behaviour: the holder is already producing a fresher roadmap than
812
+ * anything we would emit right now.
813
+ */
814
+ async executeRoadmapRefresh(event) {
815
+ let lockId = null;
816
+ let effectiveEvent = event;
817
+ if (this.roadmapWriteLock) {
818
+ const lock = this.roadmapWriteLock.acquire();
819
+ if (!lock.ok) {
820
+ logger.info({
821
+ eventType: event.type,
822
+ source: event.source,
823
+ holder: lock.holder,
824
+ }, "roadmap.md write lock held — skipping this refresh");
825
+ return;
826
+ }
827
+ lockId = lock.lockId;
828
+ effectiveEvent = {
829
+ ...event,
830
+ data: {
831
+ ...event.data,
832
+ roadmapWriteLockId: lockId,
833
+ },
834
+ };
835
+ }
836
+ try {
837
+ await this.executeDefault(effectiveEvent);
838
+ }
839
+ finally {
840
+ if (lockId && this.roadmapWriteLock) {
841
+ this.roadmapWriteLock.release(lockId);
842
+ }
843
+ }
844
+ }
845
+ /**
846
+ * P22 §3.4 — skill curation routine. Provisions an isolated optimizer
847
+ * workdir, hands the runId + runToken into the agent's task context via
848
+ * `event.data`, and tears the workdir down regardless of success/failure.
849
+ *
850
+ * The standard `executeDefault` path produces the agent session itself —
851
+ * the only differences from a normal routine are: (a) the workdir is the
852
+ * pre-built optimizer dir (built by `materializeOptimizerWorkdir`), and
853
+ * (b) `executeDefault` recognises `routine.skill_curation` events and
854
+ * pins `allowedToolsOverride` to `SKILL_CURATION_OPTIMIZER_ALLOWED_TOOLS`,
855
+ * which the Claude SDK consumes verbatim and which suspends Allow-mode
856
+ * `bypassPermissions`. The curation API's run-token + Zod chokepoint
857
+ * remains the safety floor for the rare case the override is bypassed
858
+ * (e.g. a future backend that doesn't read `allowedTools`).
859
+ */
860
+ async executeSkillCurationRoutine(event) {
861
+ const materialize = this.getMaterializeOptimizerWorkdir();
862
+ if (!materialize)
863
+ return;
864
+ // P22 §6.4 — manual run flag rides on the routine event's `data.manual`
865
+ // (set by `POST /api/skill-curation/runs/manual` from the dashboard).
866
+ // Cadence-driven cron events have no `manual` key, so the default is
867
+ // false — exactly the desired contract.
868
+ const eventData = event.data ?? {};
869
+ const manual = eventData.manual === true;
870
+ const targetSkillsOverride = Array.isArray(eventData.target_skills)
871
+ ? eventData.target_skills
872
+ : undefined;
873
+ let workdir = null;
874
+ try {
875
+ workdir = await materialize({ manual, ...(targetSkillsOverride ? { targetSkillsOverride } : {}) });
876
+ logger.info({ runId: workdir.runId, targetSkills: workdir.targetSkills, workdirPath: workdir.workdirPath, manual }, "Skill-curation optimizer run starting");
877
+ // Inject the runId + token into the event so the agent core can pick
878
+ // them up. The standard executor path runs from here.
879
+ const enriched = {
880
+ ...event,
881
+ data: {
882
+ ...event.data,
883
+ skill_curation_run_id: workdir.runId,
884
+ skill_curation_run_token: workdir.runToken,
885
+ skill_curation_workdir: workdir.workdirPath,
886
+ skill_curation_target_skills: workdir.targetSkills,
887
+ },
888
+ };
889
+ await this.executeDefault(enriched);
890
+ }
891
+ catch (err) {
892
+ logger.error({ err, runId: workdir?.runId }, "Skill-curation routine failed");
893
+ throw err;
894
+ }
895
+ finally {
896
+ const teardown = this.getTeardownOptimizerWorkdir();
897
+ if (workdir && teardown) {
898
+ try {
899
+ teardown(workdir.workdirPath);
900
+ }
901
+ catch (err) {
902
+ logger.warn({ err, workdirPath: workdir.workdirPath }, "Skill-curation workdir teardown failed");
903
+ }
904
+ }
905
+ }
906
+ }
907
+ async executeDefault(event) {
908
+ // WIKI_BUILDER_DESIGN.md §3.5 / §14 Q4 — the bang handler acquires the
909
+ // workspace-scoped wiki-compile lock at enqueue time; we release it
910
+ // here in a `finally` so the lock falls back to "free" whether the
911
+ // session succeeds, fails, or any step in this method (context build,
912
+ // binding resolution, prompt assembly, executeWithRetry, result
913
+ // processing) throws. The TTL inside `compile-lock.ts` is the daemon-
914
+ // crash safety net; release-here is the steady-state path.
915
+ //
916
+ // Capture from the immutable `event` argument so the workspace name
917
+ // is fixed before any code below runs — `effectiveEvent` is rebound
918
+ // later for routine pre-pass and shouldn't influence release.
919
+ const wikiCompileWorkspace = event.type === "wiki.compile"
920
+ ? event.data?.workspace
921
+ : undefined;
922
+ try {
923
+ // ROUTINE_DATA_ACQUISITION_DESIGN.md Phase 4 / D4 — pre-pass for
924
+ // routine events whose ProcessKey appears in `ROUTINE_WINDOWS`
925
+ // (today_refresh, evening_review, weekly_review). The hourly_check
926
+ // and morning_routine dispatch paths attach their own
927
+ // `fetchReportBlock` upstream (D2 / D3); we honour an existing
928
+ // attachment to avoid double-spawning the fetcher. `monthly_review`
929
+ // has zero rows and short-circuits inside the runner.
930
+ //
931
+ // skill_curation / roadmap_refresh / user_profile_sweep are not in
932
+ // `ROUTINE_WINDOWS`, so `routineWindowKeyFromEvent` returns null
933
+ // and the pre-pass is skipped without touching the runner.
934
+ let effectiveEvent = event;
935
+ if (isRoutineEvent(event)) {
936
+ const routineKey = routineWindowKeyFromEvent(event);
937
+ const alreadyPrepassed = typeof event.data?.fetchReportBlock === "string";
938
+ if (routineKey && !alreadyPrepassed && routineHasWindows(routineKey)) {
939
+ const prepass = await this.fetchWindowRunner.run(event, routineKey);
940
+ effectiveEvent = {
941
+ ...event,
942
+ data: {
943
+ ...event.data,
944
+ fetchReportBlock: prepass.block,
945
+ },
946
+ };
947
+ }
948
+ }
949
+ const context = await this.contextBuilder.build(effectiveEvent);
950
+ const processKey = resolveProcessKey(effectiveEvent);
951
+ // Honour run-now's `requestedModel` hint for routine events. Other event
952
+ // types (messages, scheduled.task) have their own dedicated paths that
953
+ // already handle tier selection, so this branch is routine-only.
954
+ const routineHint = isRoutineEvent(effectiveEvent) && effectiveEvent.requestedModel
955
+ ? effectiveEvent.requestedModel === "opus"
956
+ ? "high"
957
+ : "medium"
958
+ : undefined;
959
+ // Knowledge-import events carry the dashboard form's backend/model
960
+ // pick. Honor the (backendId, modelId) pair only when the event was
961
+ // emitted by the dashboard route — same defense-in-depth gate as the
962
+ // chat picker — so a malformed event from another path cannot pin a
963
+ // specific model.
964
+ const importOverride = isKnowledgeImportEvent(effectiveEvent)
965
+ && effectiveEvent.platform === "dashboard"
966
+ && effectiveEvent.requestedBackendId
967
+ && effectiveEvent.requestedModelId
968
+ ? {
969
+ requestedBackendId: effectiveEvent.requestedBackendId,
970
+ requestedModelId: effectiveEvent.requestedModelId,
971
+ }
972
+ : undefined;
973
+ const binding = this.agentRouter.resolveBinding(effectiveEvent, {
974
+ processKey,
975
+ ...(routineHint ? { requestedTier: routineHint } : {}),
976
+ ...(importOverride ?? {}),
977
+ });
978
+ const reassemblePrompt = (bid) => this.prompt.assemble(effectiveEvent.type, processKey, bid);
979
+ const prompt = reassemblePrompt(binding.main.backendId);
980
+ // P22 §3.4 step 4 — optimizer agent runs with a hard-clamped tool
981
+ // envelope. The check is on event type rather than processKey so the
982
+ // override is impossible to widen by accident from a downstream
983
+ // dispatch refactor; the only path to skill_curation execution is
984
+ // through `routine.skill_curation` events, which have no other code
985
+ // path that strips the override.
986
+ const skillCurationOverride = isRoutineEvent(effectiveEvent) && effectiveEvent.routine === "skill_curation"
987
+ ? SKILL_CURATION_OPTIMIZER_ALLOWED_TOOLS
988
+ : undefined;
989
+ if (skillCurationOverride
990
+ && !this.clampSupportedByBackend(processKey, binding.main.backendId, effectiveEvent.correlationId, "SKILL_CURATION_OPTIMIZER_ALLOWED_TOOLS")) {
991
+ // Refuse-at-execute. The skill curation routine has no schedule
992
+ // row to mark (it runs from the optimizer cron), so the audit row
993
+ // + log line written by the guard are the entire signal. Early
994
+ // return is inside the try block, so the wiki-compile finally
995
+ // still runs — but `wikiCompileWorkspace` is undefined for skill
996
+ // curation events, so the release is a no-op.
997
+ return;
998
+ }
999
+ // WIKI_BUILDER_DESIGN.md §4.3 — only `wiki.ingest_url` actually fetches
1000
+ // external URLs. `wiki.compile` and `wiki.ask` operate against already-
1001
+ // ingested files via the daemon Wiki API (verified in the skill bodies:
1002
+ // wiki-compile reads 10_raw via the API; wiki-ask reads 20_wiki). Keep
1003
+ // the widening as narrow as possible — granting WebFetch / Gemini
1004
+ // web_fetch allow to sessions that should not need it would silently
1005
+ // expand the blast radius for free.
1006
+ const wikiUrlFetchEnabled = effectiveEvent.type === "wiki.ingest_url";
1007
+ const result = await this.errorRouter.executeWithRetry(() => this.agentRouter.execute({
1008
+ prompt,
1009
+ context,
1010
+ event: effectiveEvent,
1011
+ processKey,
1012
+ preResolvedBinding: binding,
1013
+ reassemblePrompt,
1014
+ ...(skillCurationOverride
1015
+ ? { allowedToolsOverride: skillCurationOverride }
1016
+ : {}),
1017
+ ...(wikiUrlFetchEnabled ? { wikiUrlFetchEnabled: true } : {}),
1018
+ }), effectiveEvent);
1019
+ await this.resultProcessor.processResult(result, effectiveEvent);
1020
+ }
1021
+ finally {
1022
+ if (wikiCompileWorkspace) {
1023
+ releaseWikiCompileLock(wikiCompileWorkspace);
1024
+ }
1025
+ }
1026
+ }
1027
+ /** Bridge for `MorningRoutineRunner`'s `formatSqliteDatetime` use. */
1028
+ static formatScheduledFor(date) {
1029
+ return formatSqliteDatetime(date);
1030
+ }
1031
+ }
1032
+ //# sourceMappingURL=dispatcher-scheduled-tasks.js.map