@theowlops/boombot 0.0.0-stage → 1.1.1

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 (544) hide show
  1. package/README.md +69 -3
  2. package/dist/LICENSE +21 -0
  3. package/dist/bin/boombot.js +16 -0
  4. package/dist/cli/api-key.js +123 -0
  5. package/dist/cli/bin-bun.js +17 -0
  6. package/dist/cli/bin.js +34 -0
  7. package/dist/cli/cli/cli-error.js +61 -0
  8. package/dist/cli/cli/dispatch.js +138 -0
  9. package/dist/cli/cli/help.js +83 -0
  10. package/dist/cli/cli/parser.js +89 -0
  11. package/dist/cli/cli/registry.js +185 -0
  12. package/dist/cli/cli/spec.js +41 -0
  13. package/dist/cli/cli/suggest.js +125 -0
  14. package/dist/cli/cli/version.js +30 -0
  15. package/dist/cli/cli/vocabulary.js +87 -0
  16. package/dist/cli/commands/doctor.js +365 -0
  17. package/dist/cli/commands/gateway.js +290 -0
  18. package/dist/cli/commands/migrate.js +270 -0
  19. package/dist/cli/commands/plugin.js +97 -0
  20. package/dist/cli/commands/setup.js +244 -0
  21. package/dist/cli/config-file.js +133 -0
  22. package/dist/cli/master-key.js +78 -0
  23. package/dist/cli/open-browser.js +59 -0
  24. package/dist/cli/web-assets.js +58 -0
  25. package/dist/gateway/auth.js +46 -0
  26. package/dist/gateway/config-format.js +202 -0
  27. package/dist/gateway/config.js +427 -0
  28. package/dist/gateway/context.js +1 -0
  29. package/dist/gateway/env-file.js +129 -0
  30. package/dist/gateway/events.js +172 -0
  31. package/dist/gateway/guard/tagging.js +505 -0
  32. package/dist/gateway/handlers/index.js +66 -0
  33. package/dist/gateway/handlers/memory.js +54 -0
  34. package/dist/gateway/handlers/providers.js +73 -0
  35. package/dist/gateway/handlers/registry.js +39 -0
  36. package/dist/gateway/handlers/sessions.js +188 -0
  37. package/dist/gateway/handlers/system.js +12 -0
  38. package/dist/gateway/handlers/tasks.js +191 -0
  39. package/dist/gateway/handlers/turn.js +100 -0
  40. package/dist/gateway/index.js +126 -0
  41. package/dist/gateway/main.js +23 -0
  42. package/dist/gateway/server.js +354 -0
  43. package/dist/gateway/services/context-limits.js +75 -0
  44. package/dist/gateway/services/history-normalizer.js +42 -0
  45. package/dist/gateway/services/image-pipeline.js +56 -0
  46. package/dist/gateway/services/key-roulette-client.js +43 -0
  47. package/dist/gateway/services/llm-client.js +593 -0
  48. package/dist/gateway/services/llm-decoder.js +200 -0
  49. package/dist/gateway/services/local-fallback.js +135 -0
  50. package/dist/gateway/services/memory.js +511 -0
  51. package/dist/gateway/services/persistent-memory.js +196 -0
  52. package/dist/gateway/services/providers.js +44 -0
  53. package/dist/gateway/services/tasks.js +237 -0
  54. package/dist/gateway/services/verification.js +124 -0
  55. package/dist/gateway/services/vision-payload.js +48 -0
  56. package/dist/gateway/services.js +257 -0
  57. package/dist/gateway/session-snapshot.js +19 -0
  58. package/dist/gateway/sessions.js +701 -0
  59. package/dist/gateway/static.js +239 -0
  60. package/dist/gateway/turns/clarify-tool.js +57 -0
  61. package/dist/gateway/turns/cronjob-tool.js +96 -0
  62. package/dist/gateway/turns/deferred-tools.js +154 -0
  63. package/dist/gateway/turns/execute-code-tool.js +84 -0
  64. package/dist/gateway/turns/file-tool-defs.js +178 -0
  65. package/dist/gateway/turns/file-tool-ops.js +178 -0
  66. package/dist/gateway/turns/file-tool-scan.js +182 -0
  67. package/dist/gateway/turns/file-tool.js +302 -0
  68. package/dist/gateway/turns/index.js +416 -0
  69. package/dist/gateway/turns/jev-middleware.js +91 -0
  70. package/dist/gateway/turns/local-reply.js +156 -0
  71. package/dist/gateway/turns/mcp-client.js +643 -0
  72. package/dist/gateway/turns/memory-tool.js +171 -0
  73. package/dist/gateway/turns/middleware.js +96 -0
  74. package/dist/gateway/turns/python-core.js +397 -0
  75. package/dist/gateway/turns/routing/index.js +3 -0
  76. package/dist/gateway/turns/routing/intent-classifier.js +60 -0
  77. package/dist/gateway/turns/routing/planner-adapter.js +91 -0
  78. package/dist/gateway/turns/routing/types.js +1 -0
  79. package/dist/gateway/turns/runtime-deps.js +228 -0
  80. package/dist/gateway/turns/self-config-tool.js +277 -0
  81. package/dist/gateway/turns/service-types.js +11 -0
  82. package/dist/gateway/turns/service.js +820 -0
  83. package/dist/gateway/turns/session-adapter.js +158 -0
  84. package/dist/gateway/turns/session-tool.js +49 -0
  85. package/dist/gateway/turns/shell-tool.js +190 -0
  86. package/dist/gateway/turns/skill-tool.js +239 -0
  87. package/dist/gateway/turns/spillover.js +201 -0
  88. package/dist/gateway/turns/subagent-tool.js +140 -0
  89. package/dist/gateway/turns/system-prompt.js +121 -0
  90. package/dist/gateway/turns/tool-schema.js +90 -0
  91. package/dist/gateway/turns/turn-utils.js +58 -0
  92. package/dist/gateway/turns/vision-tool.js +120 -0
  93. package/dist/gateway/types.js +1 -0
  94. package/dist/native/win32-x64/boombot-native-host.exe +0 -0
  95. package/dist/node_modules/@boombot/channels/handlers.d.ts +9 -0
  96. package/dist/node_modules/@boombot/channels/handlers.js +109 -0
  97. package/dist/node_modules/@boombot/channels/index.d.ts +12 -0
  98. package/dist/node_modules/@boombot/channels/index.js +12 -0
  99. package/dist/node_modules/@boombot/channels/package.json +9 -0
  100. package/dist/node_modules/@boombot/channels/service.d.ts +62 -0
  101. package/dist/node_modules/@boombot/channels/service.js +608 -0
  102. package/dist/node_modules/@boombot/channels/tool.d.ts +10 -0
  103. package/dist/node_modules/@boombot/channels/tool.js +46 -0
  104. package/dist/node_modules/@boombot/channels/transports/common/dedup.d.ts +17 -0
  105. package/dist/node_modules/@boombot/channels/transports/common/dedup.js +42 -0
  106. package/dist/node_modules/@boombot/channels/transports/common/handoff.d.ts +19 -0
  107. package/dist/node_modules/@boombot/channels/transports/common/handoff.js +32 -0
  108. package/dist/node_modules/@boombot/channels/transports/common/index.d.ts +3 -0
  109. package/dist/node_modules/@boombot/channels/transports/common/index.js +3 -0
  110. package/dist/node_modules/@boombot/channels/transports/common/types.d.ts +99 -0
  111. package/dist/node_modules/@boombot/channels/transports/common/types.js +54 -0
  112. package/dist/node_modules/@boombot/channels/transports/common.d.ts +2 -0
  113. package/dist/node_modules/@boombot/channels/transports/common.js +2 -0
  114. package/dist/node_modules/@boombot/channels/transports/dedup/index.d.ts +1 -0
  115. package/dist/node_modules/@boombot/channels/transports/dedup/index.js +1 -0
  116. package/dist/node_modules/@boombot/channels/transports/dedup.d.ts +2 -0
  117. package/dist/node_modules/@boombot/channels/transports/dedup.js +2 -0
  118. package/dist/node_modules/@boombot/channels/transports/discord/index.d.ts +1 -0
  119. package/dist/node_modules/@boombot/channels/transports/discord/index.js +1 -0
  120. package/dist/node_modules/@boombot/channels/transports/discord/transport.d.ts +36 -0
  121. package/dist/node_modules/@boombot/channels/transports/discord/transport.js +199 -0
  122. package/dist/node_modules/@boombot/channels/transports/discord.d.ts +2 -0
  123. package/dist/node_modules/@boombot/channels/transports/discord.js +2 -0
  124. package/dist/node_modules/@boombot/channels/transports/handoff.d.ts +2 -0
  125. package/dist/node_modules/@boombot/channels/transports/handoff.js +2 -0
  126. package/dist/node_modules/@boombot/channels/transports/messenger/index.d.ts +1 -0
  127. package/dist/node_modules/@boombot/channels/transports/messenger/index.js +1 -0
  128. package/dist/node_modules/@boombot/channels/transports/messenger/transport.d.ts +34 -0
  129. package/dist/node_modules/@boombot/channels/transports/messenger/transport.js +180 -0
  130. package/dist/node_modules/@boombot/channels/transports/messenger.d.ts +2 -0
  131. package/dist/node_modules/@boombot/channels/transports/messenger.js +2 -0
  132. package/dist/node_modules/@boombot/channels/transports/slack/index.d.ts +1 -0
  133. package/dist/node_modules/@boombot/channels/transports/slack/index.js +1 -0
  134. package/dist/node_modules/@boombot/channels/transports/slack/transport.d.ts +35 -0
  135. package/dist/node_modules/@boombot/channels/transports/slack/transport.js +183 -0
  136. package/dist/node_modules/@boombot/channels/transports/slack.d.ts +2 -0
  137. package/dist/node_modules/@boombot/channels/transports/slack.js +2 -0
  138. package/dist/node_modules/@boombot/channels/transports/streamer/index.d.ts +1 -0
  139. package/dist/node_modules/@boombot/channels/transports/streamer/index.js +1 -0
  140. package/dist/node_modules/@boombot/channels/transports/streamer/streamer.d.ts +31 -0
  141. package/dist/node_modules/@boombot/channels/transports/streamer/streamer.js +141 -0
  142. package/dist/node_modules/@boombot/channels/transports/streamer.d.ts +2 -0
  143. package/dist/node_modules/@boombot/channels/transports/streamer.js +2 -0
  144. package/dist/node_modules/@boombot/channels/transports/telegram/index.d.ts +1 -0
  145. package/dist/node_modules/@boombot/channels/transports/telegram/index.js +1 -0
  146. package/dist/node_modules/@boombot/channels/transports/telegram/transport.d.ts +75 -0
  147. package/dist/node_modules/@boombot/channels/transports/telegram/transport.js +395 -0
  148. package/dist/node_modules/@boombot/channels/transports/telegram.d.ts +2 -0
  149. package/dist/node_modules/@boombot/channels/transports/telegram.js +2 -0
  150. package/dist/node_modules/@boombot/channels/transports/tiktok/index.d.ts +1 -0
  151. package/dist/node_modules/@boombot/channels/transports/tiktok/index.js +1 -0
  152. package/dist/node_modules/@boombot/channels/transports/tiktok/transport.d.ts +27 -0
  153. package/dist/node_modules/@boombot/channels/transports/tiktok/transport.js +97 -0
  154. package/dist/node_modules/@boombot/channels/transports/tiktok.d.ts +2 -0
  155. package/dist/node_modules/@boombot/channels/transports/tiktok.js +2 -0
  156. package/dist/node_modules/@boombot/channels/transports/whatsapp/index.d.ts +1 -0
  157. package/dist/node_modules/@boombot/channels/transports/whatsapp/index.js +1 -0
  158. package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.d.ts +38 -0
  159. package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.js +189 -0
  160. package/dist/node_modules/@boombot/channels/transports/whatsapp.d.ts +2 -0
  161. package/dist/node_modules/@boombot/channels/transports/whatsapp.js +2 -0
  162. package/dist/node_modules/@boombot/channels/transports/zalo/index.d.ts +2 -0
  163. package/dist/node_modules/@boombot/channels/transports/zalo/index.js +2 -0
  164. package/dist/node_modules/@boombot/channels/transports/zalo/oa.d.ts +28 -0
  165. package/dist/node_modules/@boombot/channels/transports/zalo/oa.js +94 -0
  166. package/dist/node_modules/@boombot/channels/transports/zalo/personal.d.ts +106 -0
  167. package/dist/node_modules/@boombot/channels/transports/zalo/personal.js +171 -0
  168. package/dist/node_modules/@boombot/channels/transports/zalo-personal.d.ts +2 -0
  169. package/dist/node_modules/@boombot/channels/transports/zalo-personal.js +2 -0
  170. package/dist/node_modules/@boombot/channels/transports/zalo.d.ts +2 -0
  171. package/dist/node_modules/@boombot/channels/transports/zalo.js +2 -0
  172. package/dist/node_modules/@boombot/core/agent-core/index.d.ts +12 -0
  173. package/dist/node_modules/@boombot/core/agent-core/index.js +12 -0
  174. package/dist/node_modules/@boombot/core/agent-core/middleware.d.ts +60 -0
  175. package/dist/node_modules/@boombot/core/agent-core/middleware.js +152 -0
  176. package/dist/node_modules/@boombot/core/agent-core/routing.d.ts +128 -0
  177. package/dist/node_modules/@boombot/core/agent-core/routing.js +437 -0
  178. package/dist/node_modules/@boombot/core/agent-core/runtime-deps.d.ts +88 -0
  179. package/dist/node_modules/@boombot/core/agent-core/runtime-deps.js +8 -0
  180. package/dist/node_modules/@boombot/core/agent-core/tool-errors.d.ts +44 -0
  181. package/dist/node_modules/@boombot/core/agent-core/tool-errors.js +60 -0
  182. package/dist/node_modules/@boombot/core/agent-core/turn-loop.d.ts +21 -0
  183. package/dist/node_modules/@boombot/core/agent-core/turn-loop.js +561 -0
  184. package/dist/node_modules/@boombot/core/agent-core/types.d.ts +189 -0
  185. package/dist/node_modules/@boombot/core/agent-core/types.js +7 -0
  186. package/dist/node_modules/@boombot/core/compaction/compactor.d.ts +19 -0
  187. package/dist/node_modules/@boombot/core/compaction/compactor.js +127 -0
  188. package/dist/node_modules/@boombot/core/compaction/estimator.d.ts +10 -0
  189. package/dist/node_modules/@boombot/core/compaction/estimator.js +48 -0
  190. package/dist/node_modules/@boombot/core/compaction/index.d.ts +3 -0
  191. package/dist/node_modules/@boombot/core/compaction/index.js +3 -0
  192. package/dist/node_modules/@boombot/core/compaction/types.d.ts +31 -0
  193. package/dist/node_modules/@boombot/core/compaction/types.js +7 -0
  194. package/dist/node_modules/@boombot/core/index.d.ts +9 -0
  195. package/dist/node_modules/@boombot/core/index.js +9 -0
  196. package/dist/node_modules/@boombot/core/jev/index.d.ts +2 -0
  197. package/dist/node_modules/@boombot/core/jev/index.js +2 -0
  198. package/dist/node_modules/@boombot/core/jev/jev.d.ts +30 -0
  199. package/dist/node_modules/@boombot/core/jev/jev.js +164 -0
  200. package/dist/node_modules/@boombot/core/jev/types.d.ts +22 -0
  201. package/dist/node_modules/@boombot/core/jev/types.js +1 -0
  202. package/dist/node_modules/@boombot/core/memory/consolidation/budget.d.ts +63 -0
  203. package/dist/node_modules/@boombot/core/memory/consolidation/budget.js +94 -0
  204. package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.d.ts +152 -0
  205. package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.js +318 -0
  206. package/dist/node_modules/@boombot/core/memory/consolidation/index.d.ts +12 -0
  207. package/dist/node_modules/@boombot/core/memory/consolidation/index.js +12 -0
  208. package/dist/node_modules/@boombot/core/memory/curation.d.ts +86 -0
  209. package/dist/node_modules/@boombot/core/memory/curation.js +120 -0
  210. package/dist/node_modules/@boombot/core/memory/errors.d.ts +12 -0
  211. package/dist/node_modules/@boombot/core/memory/errors.js +20 -0
  212. package/dist/node_modules/@boombot/core/memory/hybrid-search.d.ts +29 -0
  213. package/dist/node_modules/@boombot/core/memory/hybrid-search.js +52 -0
  214. package/dist/node_modules/@boombot/core/memory/index.d.ts +23 -0
  215. package/dist/node_modules/@boombot/core/memory/index.js +23 -0
  216. package/dist/node_modules/@boombot/core/memory/indexer.d.ts +47 -0
  217. package/dist/node_modules/@boombot/core/memory/indexer.js +138 -0
  218. package/dist/node_modules/@boombot/core/memory/lifecycle.d.ts +12 -0
  219. package/dist/node_modules/@boombot/core/memory/lifecycle.js +66 -0
  220. package/dist/node_modules/@boombot/core/memory/provider.d.ts +84 -0
  221. package/dist/node_modules/@boombot/core/memory/provider.js +117 -0
  222. package/dist/node_modules/@boombot/core/memory/recall.d.ts +7 -0
  223. package/dist/node_modules/@boombot/core/memory/recall.js +111 -0
  224. package/dist/node_modules/@boombot/core/memory/scope-resolver.d.ts +27 -0
  225. package/dist/node_modules/@boombot/core/memory/scope-resolver.js +60 -0
  226. package/dist/node_modules/@boombot/core/memory/search.d.ts +37 -0
  227. package/dist/node_modules/@boombot/core/memory/search.js +158 -0
  228. package/dist/node_modules/@boombot/core/memory/store.d.ts +60 -0
  229. package/dist/node_modules/@boombot/core/memory/store.js +289 -0
  230. package/dist/node_modules/@boombot/core/memory/types.d.ts +121 -0
  231. package/dist/node_modules/@boombot/core/memory/types.js +21 -0
  232. package/dist/node_modules/@boombot/core/memory/vector-index.d.ts +34 -0
  233. package/dist/node_modules/@boombot/core/memory/vector-index.js +65 -0
  234. package/dist/node_modules/@boombot/core/package.json +9 -0
  235. package/dist/node_modules/@boombot/core/reflection/classifier.d.ts +13 -0
  236. package/dist/node_modules/@boombot/core/reflection/classifier.js +77 -0
  237. package/dist/node_modules/@boombot/core/reflection/index.d.ts +2 -0
  238. package/dist/node_modules/@boombot/core/reflection/index.js +2 -0
  239. package/dist/node_modules/@boombot/core/reflection/types.d.ts +13 -0
  240. package/dist/node_modules/@boombot/core/reflection/types.js +4 -0
  241. package/dist/node_modules/@boombot/core/research/config.d.ts +89 -0
  242. package/dist/node_modules/@boombot/core/research/config.js +206 -0
  243. package/dist/node_modules/@boombot/core/research/crawl.d.ts +36 -0
  244. package/dist/node_modules/@boombot/core/research/crawl.js +179 -0
  245. package/dist/node_modules/@boombot/core/research/factory.d.ts +26 -0
  246. package/dist/node_modules/@boombot/core/research/factory.js +46 -0
  247. package/dist/node_modules/@boombot/core/research/http.d.ts +87 -0
  248. package/dist/node_modules/@boombot/core/research/http.js +250 -0
  249. package/dist/node_modules/@boombot/core/research/index.d.ts +17 -0
  250. package/dist/node_modules/@boombot/core/research/index.js +10 -0
  251. package/dist/node_modules/@boombot/core/research/orchestrator.d.ts +113 -0
  252. package/dist/node_modules/@boombot/core/research/orchestrator.js +252 -0
  253. package/dist/node_modules/@boombot/core/research/parse/docx.d.ts +24 -0
  254. package/dist/node_modules/@boombot/core/research/parse/docx.js +183 -0
  255. package/dist/node_modules/@boombot/core/research/parse/guards.d.ts +89 -0
  256. package/dist/node_modules/@boombot/core/research/parse/guards.js +171 -0
  257. package/dist/node_modules/@boombot/core/research/parse/index.d.ts +68 -0
  258. package/dist/node_modules/@boombot/core/research/parse/index.js +219 -0
  259. package/dist/node_modules/@boombot/core/research/parse/pdf.d.ts +38 -0
  260. package/dist/node_modules/@boombot/core/research/parse/pdf.js +567 -0
  261. package/dist/node_modules/@boombot/core/research/providers/arxiv.d.ts +9 -0
  262. package/dist/node_modules/@boombot/core/research/providers/arxiv.js +72 -0
  263. package/dist/node_modules/@boombot/core/research/providers/brave.d.ts +11 -0
  264. package/dist/node_modules/@boombot/core/research/providers/brave.js +36 -0
  265. package/dist/node_modules/@boombot/core/research/providers/duckduckgo.d.ts +9 -0
  266. package/dist/node_modules/@boombot/core/research/providers/duckduckgo.js +78 -0
  267. package/dist/node_modules/@boombot/core/research/providers/exa.d.ts +11 -0
  268. package/dist/node_modules/@boombot/core/research/providers/exa.js +34 -0
  269. package/dist/node_modules/@boombot/core/research/providers/firecrawl.d.ts +16 -0
  270. package/dist/node_modules/@boombot/core/research/providers/firecrawl.js +47 -0
  271. package/dist/node_modules/@boombot/core/research/providers/index.d.ts +17 -0
  272. package/dist/node_modules/@boombot/core/research/providers/index.js +11 -0
  273. package/dist/node_modules/@boombot/core/research/providers/jina.d.ts +13 -0
  274. package/dist/node_modules/@boombot/core/research/providers/jina.js +114 -0
  275. package/dist/node_modules/@boombot/core/research/providers/openalex.d.ts +14 -0
  276. package/dist/node_modules/@boombot/core/research/providers/openalex.js +66 -0
  277. package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.d.ts +9 -0
  278. package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.js +56 -0
  279. package/dist/node_modules/@boombot/core/research/providers/tavily.d.ts +11 -0
  280. package/dist/node_modules/@boombot/core/research/providers/tavily.js +35 -0
  281. package/dist/node_modules/@boombot/core/research/providers/wikipedia.d.ts +12 -0
  282. package/dist/node_modules/@boombot/core/research/providers/wikipedia.js +47 -0
  283. package/dist/node_modules/@boombot/core/research/tools.d.ts +27 -0
  284. package/dist/node_modules/@boombot/core/research/tools.js +251 -0
  285. package/dist/node_modules/@boombot/core/research/types.d.ts +133 -0
  286. package/dist/node_modules/@boombot/core/research/types.js +8 -0
  287. package/dist/node_modules/@boombot/core/research/wiring.d.ts +25 -0
  288. package/dist/node_modules/@boombot/core/research/wiring.js +51 -0
  289. package/dist/node_modules/@boombot/core/scheduler/cron.d.ts +34 -0
  290. package/dist/node_modules/@boombot/core/scheduler/cron.js +147 -0
  291. package/dist/node_modules/@boombot/core/scheduler/evaluator.d.ts +42 -0
  292. package/dist/node_modules/@boombot/core/scheduler/evaluator.js +148 -0
  293. package/dist/node_modules/@boombot/core/scheduler/index.d.ts +15 -0
  294. package/dist/node_modules/@boombot/core/scheduler/index.js +15 -0
  295. package/dist/node_modules/@boombot/core/scheduler/registry.d.ts +81 -0
  296. package/dist/node_modules/@boombot/core/scheduler/registry.js +263 -0
  297. package/dist/node_modules/@boombot/core/scheduler/runner.d.ts +59 -0
  298. package/dist/node_modules/@boombot/core/scheduler/runner.js +257 -0
  299. package/dist/node_modules/@boombot/core/scheduler/store.d.ts +150 -0
  300. package/dist/node_modules/@boombot/core/scheduler/store.js +526 -0
  301. package/dist/node_modules/@boombot/core/scheduler/timezone.d.ts +35 -0
  302. package/dist/node_modules/@boombot/core/scheduler/timezone.js +96 -0
  303. package/dist/node_modules/@boombot/core/scheduler/types.d.ts +157 -0
  304. package/dist/node_modules/@boombot/core/scheduler/types.js +89 -0
  305. package/dist/node_modules/@boombot/core/task-runner/index.d.ts +7 -0
  306. package/dist/node_modules/@boombot/core/task-runner/index.js +5 -0
  307. package/dist/node_modules/@boombot/core/task-runner/planner.d.ts +37 -0
  308. package/dist/node_modules/@boombot/core/task-runner/planner.js +154 -0
  309. package/dist/node_modules/@boombot/core/task-runner/runner.d.ts +8 -0
  310. package/dist/node_modules/@boombot/core/task-runner/runner.js +564 -0
  311. package/dist/node_modules/@boombot/core/task-runner/store.d.ts +28 -0
  312. package/dist/node_modules/@boombot/core/task-runner/store.js +126 -0
  313. package/dist/node_modules/@boombot/core/task-runner/types.d.ts +138 -0
  314. package/dist/node_modules/@boombot/core/task-runner/types.js +11 -0
  315. package/dist/node_modules/@boombot/core/workflow/dag.d.ts +16 -0
  316. package/dist/node_modules/@boombot/core/workflow/dag.js +109 -0
  317. package/dist/node_modules/@boombot/core/workflow/engine.d.ts +34 -0
  318. package/dist/node_modules/@boombot/core/workflow/engine.js +304 -0
  319. package/dist/node_modules/@boombot/core/workflow/index.d.ts +3 -0
  320. package/dist/node_modules/@boombot/core/workflow/index.js +3 -0
  321. package/dist/node_modules/@boombot/core/workflow/types.d.ts +98 -0
  322. package/dist/node_modules/@boombot/core/workflow/types.js +21 -0
  323. package/dist/node_modules/@boombot/cronjobs/handlers.d.ts +11 -0
  324. package/dist/node_modules/@boombot/cronjobs/handlers.js +174 -0
  325. package/dist/node_modules/@boombot/cronjobs/index.d.ts +2 -0
  326. package/dist/node_modules/@boombot/cronjobs/index.js +2 -0
  327. package/dist/node_modules/@boombot/cronjobs/package.json +9 -0
  328. package/dist/node_modules/@boombot/cronjobs/service.d.ts +53 -0
  329. package/dist/node_modules/@boombot/cronjobs/service.js +200 -0
  330. package/dist/node_modules/@boombot/plugins/index.d.ts +2 -0
  331. package/dist/node_modules/@boombot/plugins/index.js +2 -0
  332. package/dist/node_modules/@boombot/plugins/installer.d.ts +45 -0
  333. package/dist/node_modules/@boombot/plugins/installer.js +487 -0
  334. package/dist/node_modules/@boombot/plugins/manager.d.ts +23 -0
  335. package/dist/node_modules/@boombot/plugins/manager.js +243 -0
  336. package/dist/node_modules/@boombot/plugins/package.json +9 -0
  337. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.d.ts +125 -0
  338. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.js +76 -0
  339. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.d.ts +220 -0
  340. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.js +60 -0
  341. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.d.ts +33 -0
  342. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.js +25 -0
  343. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.d.ts +4349 -0
  344. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.js +563 -0
  345. package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.d.ts +32 -0
  346. package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.js +61 -0
  347. package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.d.ts +13 -0
  348. package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.js +16 -0
  349. package/dist/node_modules/@boombot/protocol/gateway-protocol/events.d.ts +567 -0
  350. package/dist/node_modules/@boombot/protocol/gateway-protocol/events.js +395 -0
  351. package/dist/node_modules/@boombot/protocol/gateway-protocol/index.d.ts +20 -0
  352. package/dist/node_modules/@boombot/protocol/gateway-protocol/index.js +13 -0
  353. package/dist/node_modules/@boombot/protocol/gateway-protocol/types.d.ts +91 -0
  354. package/dist/node_modules/@boombot/protocol/gateway-protocol/types.js +7 -0
  355. package/dist/node_modules/@boombot/protocol/index.d.ts +4 -0
  356. package/dist/node_modules/@boombot/protocol/index.js +3 -0
  357. package/dist/node_modules/@boombot/protocol/package.json +9 -0
  358. package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.d.ts +41 -0
  359. package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.js +17 -0
  360. package/dist/node_modules/@boombot/protocol/plugin-sdk/index.d.ts +10 -0
  361. package/dist/node_modules/@boombot/protocol/plugin-sdk/index.js +10 -0
  362. package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.d.ts +58 -0
  363. package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.js +116 -0
  364. package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.d.ts +25 -0
  365. package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.js +1 -0
  366. package/dist/node_modules/@boombot/protocol/providers/cipher.d.ts +7 -0
  367. package/dist/node_modules/@boombot/protocol/providers/cipher.js +52 -0
  368. package/dist/node_modules/@boombot/protocol/providers/index.d.ts +19 -0
  369. package/dist/node_modules/@boombot/protocol/providers/index.js +19 -0
  370. package/dist/node_modules/@boombot/protocol/providers/key-roulette.d.ts +79 -0
  371. package/dist/node_modules/@boombot/protocol/providers/key-roulette.js +81 -0
  372. package/dist/node_modules/@boombot/protocol/providers/probe.d.ts +29 -0
  373. package/dist/node_modules/@boombot/protocol/providers/probe.js +96 -0
  374. package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.d.ts +11 -0
  375. package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.js +11 -0
  376. package/dist/node_modules/@boombot/protocol/providers/profiles/custom.d.ts +22 -0
  377. package/dist/node_modules/@boombot/protocol/providers/profiles/custom.js +222 -0
  378. package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.d.ts +7 -0
  379. package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.js +9 -0
  380. package/dist/node_modules/@boombot/protocol/providers/profiles/google.d.ts +10 -0
  381. package/dist/node_modules/@boombot/protocol/providers/profiles/google.js +10 -0
  382. package/dist/node_modules/@boombot/protocol/providers/profiles/index.d.ts +31 -0
  383. package/dist/node_modules/@boombot/protocol/providers/profiles/index.js +56 -0
  384. package/dist/node_modules/@boombot/protocol/providers/profiles/local.d.ts +10 -0
  385. package/dist/node_modules/@boombot/protocol/providers/profiles/local.js +11 -0
  386. package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.d.ts +7 -0
  387. package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.js +9 -0
  388. package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.d.ts +10 -0
  389. package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.js +10 -0
  390. package/dist/node_modules/@boombot/protocol/providers/profiles/openai.d.ts +7 -0
  391. package/dist/node_modules/@boombot/protocol/providers/profiles/openai.js +11 -0
  392. package/dist/node_modules/@boombot/protocol/providers/profiles/types.d.ts +58 -0
  393. package/dist/node_modules/@boombot/protocol/providers/profiles/types.js +1 -0
  394. package/dist/node_modules/@boombot/protocol/providers/profiles/xai.d.ts +7 -0
  395. package/dist/node_modules/@boombot/protocol/providers/profiles/xai.js +10 -0
  396. package/dist/node_modules/@boombot/protocol/providers/request-profile.d.ts +102 -0
  397. package/dist/node_modules/@boombot/protocol/providers/request-profile.js +118 -0
  398. package/dist/node_modules/@boombot/protocol/providers/service.d.ts +2 -0
  399. package/dist/node_modules/@boombot/protocol/providers/service.js +418 -0
  400. package/dist/node_modules/@boombot/protocol/providers/types.d.ts +141 -0
  401. package/dist/node_modules/@boombot/protocol/providers/types.js +16 -0
  402. package/dist/node_modules/@boombot/sandbox/fs-path.d.ts +13 -0
  403. package/dist/node_modules/@boombot/sandbox/fs-path.js +79 -0
  404. package/dist/node_modules/@boombot/sandbox/index.d.ts +13 -0
  405. package/dist/node_modules/@boombot/sandbox/index.js +13 -0
  406. package/dist/node_modules/@boombot/sandbox/native.d.ts +39 -0
  407. package/dist/node_modules/@boombot/sandbox/native.js +224 -0
  408. package/dist/node_modules/@boombot/sandbox/package.json +9 -0
  409. package/dist/node_modules/@boombot/sandbox/sandbox.d.ts +92 -0
  410. package/dist/node_modules/@boombot/sandbox/sandbox.js +229 -0
  411. package/dist/node_modules/@boombot/security/harness-guard/boundary.d.ts +22 -0
  412. package/dist/node_modules/@boombot/security/harness-guard/boundary.js +105 -0
  413. package/dist/node_modules/@boombot/security/harness-guard/gates.d.ts +90 -0
  414. package/dist/node_modules/@boombot/security/harness-guard/gates.js +791 -0
  415. package/dist/node_modules/@boombot/security/harness-guard/index.d.ts +23 -0
  416. package/dist/node_modules/@boombot/security/harness-guard/index.js +23 -0
  417. package/dist/node_modules/@boombot/security/harness-guard/policy.d.ts +20 -0
  418. package/dist/node_modules/@boombot/security/harness-guard/policy.js +70 -0
  419. package/dist/node_modules/@boombot/security/harness-guard/sanitizer.d.ts +53 -0
  420. package/dist/node_modules/@boombot/security/harness-guard/sanitizer.js +145 -0
  421. package/dist/node_modules/@boombot/security/harness-guard/types.d.ts +71 -0
  422. package/dist/node_modules/@boombot/security/harness-guard/types.js +10 -0
  423. package/dist/node_modules/@boombot/security/harness-guard/verifier.d.ts +59 -0
  424. package/dist/node_modules/@boombot/security/harness-guard/verifier.js +169 -0
  425. package/dist/node_modules/@boombot/security/index.d.ts +5 -0
  426. package/dist/node_modules/@boombot/security/index.js +9 -0
  427. package/dist/node_modules/@boombot/security/package.json +9 -0
  428. package/dist/node_modules/@boombot/security/policy/approval.d.ts +117 -0
  429. package/dist/node_modules/@boombot/security/policy/approval.js +223 -0
  430. package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.d.ts +10 -0
  431. package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.js +124 -0
  432. package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.d.ts +25 -0
  433. package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.js +70 -0
  434. package/dist/node_modules/@boombot/security/reasoning-guard/gate.d.ts +38 -0
  435. package/dist/node_modules/@boombot/security/reasoning-guard/gate.js +61 -0
  436. package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.d.ts +12 -0
  437. package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.js +234 -0
  438. package/dist/node_modules/@boombot/security/reasoning-guard/index.d.ts +17 -0
  439. package/dist/node_modules/@boombot/security/reasoning-guard/index.js +13 -0
  440. package/dist/node_modules/@boombot/security/reasoning-guard/policy.d.ts +15 -0
  441. package/dist/node_modules/@boombot/security/reasoning-guard/policy.js +39 -0
  442. package/dist/node_modules/@boombot/security/reasoning-guard/types.d.ts +46 -0
  443. package/dist/node_modules/@boombot/security/reasoning-guard/types.js +7 -0
  444. package/dist/node_modules/@boombot/security/security/approval.d.ts +63 -0
  445. package/dist/node_modules/@boombot/security/security/approval.js +140 -0
  446. package/dist/node_modules/@boombot/security/security/audit.d.ts +39 -0
  447. package/dist/node_modules/@boombot/security/security/audit.js +70 -0
  448. package/dist/node_modules/@boombot/security/security/index.d.ts +12 -0
  449. package/dist/node_modules/@boombot/security/security/index.js +12 -0
  450. package/dist/node_modules/@boombot/security/security/trust.d.ts +79 -0
  451. package/dist/node_modules/@boombot/security/security/trust.js +87 -0
  452. package/dist/package.json +5 -0
  453. package/dist/proto/agent_core.proto +56 -0
  454. package/dist/python/agent-core-python/pyproject.toml +40 -0
  455. package/dist/python/agent-core-python/src/boombot_agent/__init__.py +5 -0
  456. package/dist/python/agent-core-python/src/boombot_agent/generated/__init__.py +0 -0
  457. package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2.py +54 -0
  458. package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2_grpc.py +140 -0
  459. package/dist/python/agent-core-python/src/boombot_agent/rpc.py +265 -0
  460. package/dist/python/agent-core-python/src/boombot_agent/turn.py +577 -0
  461. package/dist/python/agent-core-python/src/boombot_ml/__init__.py +12 -0
  462. package/dist/python/agent-core-python/src/boombot_ml/embeddings.py +58 -0
  463. package/dist/python/agent-core-python/src/boombot_ml/training.py +156 -0
  464. package/dist/python/agent-core-python/uv.lock +2856 -0
  465. package/dist/web/assets/ChatView-BZrSD-64.js +262 -0
  466. package/dist/web/assets/ChatView-Cahbjez6.css +1 -0
  467. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  468. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  469. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  470. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  471. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  472. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  473. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  474. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  475. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  476. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  477. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  478. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  479. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  480. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  481. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  482. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  483. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  484. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  485. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  486. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  487. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  488. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  489. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  490. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  491. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  492. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  493. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  494. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  495. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  496. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  497. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  498. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  499. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  500. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  501. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  502. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  503. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  504. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  505. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  506. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  507. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  508. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  509. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  510. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  511. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  512. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  513. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  514. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  515. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  516. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  517. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  518. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  519. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  520. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  521. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  522. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  523. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  524. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  525. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  526. package/dist/web/assets/MemoryView-BJbXqqs7.css +1 -0
  527. package/dist/web/assets/MemoryView-BfMq_6CD.js +1 -0
  528. package/dist/web/assets/SchedulesView-B22QmPR-.js +1 -0
  529. package/dist/web/assets/SchedulesView-C6XK7A8a.css +1 -0
  530. package/dist/web/assets/SessionsView-C1dqdhxI.css +1 -0
  531. package/dist/web/assets/SessionsView-DS0-aH2a.js +1 -0
  532. package/dist/web/assets/TasksView-Dtasg4hd.css +1 -0
  533. package/dist/web/assets/TasksView-ey0FLsW8.js +2 -0
  534. package/dist/web/assets/Textarea-DN3Zybl1.js +1 -0
  535. package/dist/web/assets/Tooltip-Bn2cqFPz.css +1 -0
  536. package/dist/web/assets/Tooltip-MzV2Gqnc.js +1 -0
  537. package/dist/web/assets/browser-Bnt73-Ub.js +8 -0
  538. package/dist/web/assets/index-BL8bi7Yt.css +1 -0
  539. package/dist/web/assets/index-CuPR73Tn.js +73 -0
  540. package/dist/web/favicon.svg +4 -0
  541. package/dist/web/index.html +26 -0
  542. package/dist/web/logo.jpg +0 -0
  543. package/dist/web/logo.png +0 -0
  544. package/package.json +51 -4
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Next-fire-time computation for cron schedules in a job's IANA timezone.
3
+ *
4
+ * Search happens in wall-clock space with field-level skipping (month → day
5
+ * → hour → minute), so even sparse schedules (e.g. Feb 29) resolve in a
6
+ * handful of iterations. DST edge cases:
7
+ * - spring-forward gap: the wall time doesn't exist → round-trip check fails
8
+ * → the minute is skipped;
9
+ * - fall-back ambiguity: the offset iteration in `wallToUtcMs` resolves to
10
+ * the first occurrence.
11
+ */
12
+ import { type CronSchedule } from './cron.js';
13
+ /**
14
+ * Cheap static unsatisfiability pre-check (SECURITY FIX: unsatisfiable-cron
15
+ * CPU burn, medium).
16
+ *
17
+ * Rejects schedules whose day-of-month can never occur in any selected month
18
+ * (e.g. `0 0 30 2 *`, `0 0 31 4 *`) WITHOUT running the multi-million-step
19
+ * search in `computeNextFire`. Before this check, `create()` burned ~2s of
20
+ * event-loop CPU per hostile spec — and the burn happened BEFORE the
21
+ * per-principal quota check, so even a quota-exhausted attacker could
22
+ * trigger it repeatedly (CPU-exhaustion DoS in the management plane).
23
+ *
24
+ * Soundness: the check only applies when day-of-week is unrestricted (`*`).
25
+ * A restricted DOW can match days via Vixie OR-semantics even when DOM never
26
+ * matches (see `dayMatches` in cron.ts), so those schedules fall through to
27
+ * the bounded search in `computeNextFire` instead.
28
+ */
29
+ export declare function isUnsatisfiableCron(schedule: CronSchedule): boolean;
30
+ /**
31
+ * Earliest UTC instant strictly after `afterMs` matching `schedule` in
32
+ * `timeZone`. Returns `undefined` when nothing matches within the horizon
33
+ * (e.g. Feb 30 — rejected earlier by day-range validation, so this is a
34
+ * safety net for truly unsatisfiable combinations).
35
+ */
36
+ export declare function computeNextFire(schedule: CronSchedule, afterMs: number, timeZone: string): number | undefined;
37
+ export type MisfireClass = 'on_time' | 'misfired';
38
+ /**
39
+ * Classify whether `nextRunAt` is merely due or has misfired.
40
+ * Wall-clock only, per spec 04.
41
+ */
42
+ export declare function classifyMisfire(nextRunAtMs: number, nowMs: number, misfireGraceMs: number): MisfireClass;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Next-fire-time computation for cron schedules in a job's IANA timezone.
3
+ *
4
+ * Search happens in wall-clock space with field-level skipping (month → day
5
+ * → hour → minute), so even sparse schedules (e.g. Feb 29) resolve in a
6
+ * handful of iterations. DST edge cases:
7
+ * - spring-forward gap: the wall time doesn't exist → round-trip check fails
8
+ * → the minute is skipped;
9
+ * - fall-back ambiguity: the offset iteration in `wallToUtcMs` resolves to
10
+ * the first occurrence.
11
+ */
12
+ import { matchesCron } from './cron.js';
13
+ import { toWallParts, wallRoundTrips, wallToUtcMs, } from './timezone.js';
14
+ /**
15
+ * Search horizon: ~8.2 years of minutes. Covers the longest legitimate gap
16
+ * (Feb 29 across a skipped century leap year, e.g. 2096 -> 2104). Field-level
17
+ * skipping means this loop almost never runs hot — the bound is a safety net
18
+ * for unsatisfiable combinations like Feb 30.
19
+ */
20
+ const MAX_ITERATIONS = 4_300_000;
21
+ function daysInMonth(year, month) {
22
+ return new Date(Date.UTC(year, month, 0)).getUTCDate();
23
+ }
24
+ /**
25
+ * Max days per month index (1-based). February counts 29 — leap years exist —
26
+ * so this check is conservative: it only rejects combinations that can NEVER
27
+ * fire in any year.
28
+ */
29
+ const MONTH_MAX_DAYS = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
30
+ /**
31
+ * Cheap static unsatisfiability pre-check (SECURITY FIX: unsatisfiable-cron
32
+ * CPU burn, medium).
33
+ *
34
+ * Rejects schedules whose day-of-month can never occur in any selected month
35
+ * (e.g. `0 0 30 2 *`, `0 0 31 4 *`) WITHOUT running the multi-million-step
36
+ * search in `computeNextFire`. Before this check, `create()` burned ~2s of
37
+ * event-loop CPU per hostile spec — and the burn happened BEFORE the
38
+ * per-principal quota check, so even a quota-exhausted attacker could
39
+ * trigger it repeatedly (CPU-exhaustion DoS in the management plane).
40
+ *
41
+ * Soundness: the check only applies when day-of-week is unrestricted (`*`).
42
+ * A restricted DOW can match days via Vixie OR-semantics even when DOM never
43
+ * matches (see `dayMatches` in cron.ts), so those schedules fall through to
44
+ * the bounded search in `computeNextFire` instead.
45
+ */
46
+ export function isUnsatisfiableCron(schedule) {
47
+ if (!schedule.dayOfWeekIsStar)
48
+ return false;
49
+ let maxDom = 0;
50
+ for (const day of schedule.dayOfMonth) {
51
+ if (day > maxDom)
52
+ maxDom = day;
53
+ }
54
+ let maxMonthDays = 0;
55
+ for (const month of schedule.month) {
56
+ const days = MONTH_MAX_DAYS[month] ?? 0;
57
+ if (days > maxMonthDays)
58
+ maxMonthDays = days;
59
+ }
60
+ return maxDom > maxMonthDays;
61
+ }
62
+ function withWeekday(parts) {
63
+ return {
64
+ ...parts,
65
+ weekday: new Date(Date.UTC(parts.year, parts.month - 1, parts.day)).getUTCDay(),
66
+ };
67
+ }
68
+ function advanceMonth(parts) {
69
+ const month = parts.month === 12 ? 1 : parts.month + 1;
70
+ const year = parts.month === 12 ? parts.year + 1 : parts.year;
71
+ return withWeekday({ year, month, day: 1, hour: 0, minute: 0 });
72
+ }
73
+ function advanceDay(parts) {
74
+ const dim = daysInMonth(parts.year, parts.month);
75
+ if (parts.day < dim) {
76
+ return withWeekday({ ...parts, day: parts.day + 1, hour: 0, minute: 0 });
77
+ }
78
+ return advanceMonth(parts);
79
+ }
80
+ function advanceHour(parts) {
81
+ if (parts.hour < 23)
82
+ return withWeekday({ ...parts, hour: parts.hour + 1, minute: 0 });
83
+ return advanceDay(parts);
84
+ }
85
+ function advanceMinute(parts) {
86
+ if (parts.minute < 59)
87
+ return withWeekday({ ...parts, minute: parts.minute + 1 });
88
+ return advanceHour(parts);
89
+ }
90
+ /**
91
+ * Earliest UTC instant strictly after `afterMs` matching `schedule` in
92
+ * `timeZone`. Returns `undefined` when nothing matches within the horizon
93
+ * (e.g. Feb 30 — rejected earlier by day-range validation, so this is a
94
+ * safety net for truly unsatisfiable combinations).
95
+ */
96
+ export function computeNextFire(schedule, afterMs, timeZone) {
97
+ // Start at the next whole minute after `afterMs` (exclusive).
98
+ const startWall = toWallParts(Math.floor(afterMs / 60000) * 60000 + 60000, timeZone);
99
+ let wall = startWall;
100
+ for (let i = 0; i < MAX_ITERATIONS; i += 1) {
101
+ if (!schedule.month.has(wall.month)) {
102
+ wall = advanceMonth(wall);
103
+ continue;
104
+ }
105
+ // Day check needs DOM/DOW OR-semantics; reuse matchesCron on a probe.
106
+ if (!daySatisfied(schedule, wall)) {
107
+ wall = advanceDay(wall);
108
+ continue;
109
+ }
110
+ if (!schedule.hour.has(wall.hour)) {
111
+ wall = advanceHour(wall);
112
+ continue;
113
+ }
114
+ if (!schedule.minute.has(wall.minute)) {
115
+ wall = advanceMinute(wall);
116
+ continue;
117
+ }
118
+ const candidate = wallToUtcMs(wall, timeZone);
119
+ // Skip wall times that don't exist (spring-forward gap).
120
+ if (!wallRoundTrips(wall, candidate, timeZone)) {
121
+ wall = advanceMinute(wall);
122
+ continue;
123
+ }
124
+ if (candidate > afterMs)
125
+ return candidate;
126
+ wall = advanceMinute(wall);
127
+ }
128
+ return undefined;
129
+ }
130
+ function daySatisfied(schedule, wall) {
131
+ // Probe with a synthetic full match on the other fields.
132
+ return matchesCron({
133
+ minute: new Set([wall.minute]),
134
+ hour: new Set([wall.hour]),
135
+ dayOfMonth: schedule.dayOfMonth,
136
+ month: new Set([wall.month]),
137
+ dayOfWeek: schedule.dayOfWeek,
138
+ dayOfMonthIsStar: schedule.dayOfMonthIsStar,
139
+ dayOfWeekIsStar: schedule.dayOfWeekIsStar,
140
+ }, wall);
141
+ }
142
+ /**
143
+ * Classify whether `nextRunAt` is merely due or has misfired.
144
+ * Wall-clock only, per spec 04.
145
+ */
146
+ export function classifyMisfire(nextRunAtMs, nowMs, misfireGraceMs) {
147
+ return nowMs - nextRunAtMs > misfireGraceMs ? 'misfired' : 'on_time';
148
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @boombot/core — durable cron/heartbeat scheduling for the
3
+ * BoomBot harness layer.
4
+ *
5
+ * Public surface: registry (management plane), runner (execution plane),
6
+ * cron parser/evaluator, SQLite store, and the narrow ports the host must
7
+ * wire (`SchedulerAuthorizer`, `TurnSubmitter`).
8
+ */
9
+ export { parseCron, validateCronExpression, matchesCron, MAX_CRON_LENGTH, type CronSchedule, } from './cron.js';
10
+ export { computeNextFire, isUnsatisfiableCron, classifyMisfire, type MisfireClass, } from './evaluator.js';
11
+ export { toWallParts, wallToUtcMs, wallRoundTrips, tzOffsetMs, isValidTimeZone, type WallParts, } from './timezone.js';
12
+ export { ScheduleStore, SCHEDULER_SCHEMA_SQL, newJobId, newRunId, idempotencyKeyFor, type SchedulerRunRecord, type SchedulerDeliveryRecord, type DeliveryOutcome, type ClaimFire, type ClaimResult, } from './store.js';
13
+ export { ScheduleRegistry, validateSpec, buildClaimFires, MAX_SCHEDULES_PER_PRINCIPAL, MAX_NAME_LENGTH, MAX_PROMPT_LENGTH, type ValidatedSpec, type RegistryOptions, } from './registry.js';
14
+ export { SchedulerRunner, type RunnerOptions, type TickStats, } from './runner.js';
15
+ export { SchedulerError, createAuthenticatedContext, assertAuthenticatedContext, unauthorized, invalidSpec, notFound, limitExceeded, invalidState, storageError, type AuthenticatedContext, type ConcurrencyPolicy, type MissedRunPolicy, type RunStatus, type SchedulerJob, type ScheduleSpec, type SchedulerAuthorizer, type TurnSubmitter, type WallClock, type SchedulerErrorCode, } from './types.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @boombot/core — durable cron/heartbeat scheduling for the
3
+ * BoomBot harness layer.
4
+ *
5
+ * Public surface: registry (management plane), runner (execution plane),
6
+ * cron parser/evaluator, SQLite store, and the narrow ports the host must
7
+ * wire (`SchedulerAuthorizer`, `TurnSubmitter`).
8
+ */
9
+ export { parseCron, validateCronExpression, matchesCron, MAX_CRON_LENGTH, } from './cron.js';
10
+ export { computeNextFire, isUnsatisfiableCron, classifyMisfire, } from './evaluator.js';
11
+ export { toWallParts, wallToUtcMs, wallRoundTrips, tzOffsetMs, isValidTimeZone, } from './timezone.js';
12
+ export { ScheduleStore, SCHEDULER_SCHEMA_SQL, newJobId, newRunId, idempotencyKeyFor, } from './store.js';
13
+ export { ScheduleRegistry, validateSpec, buildClaimFires, MAX_SCHEDULES_PER_PRINCIPAL, MAX_NAME_LENGTH, MAX_PROMPT_LENGTH, } from './registry.js';
14
+ export { SchedulerRunner, } from './runner.js';
15
+ export { SchedulerError, createAuthenticatedContext, assertAuthenticatedContext, unauthorized, invalidSpec, notFound, limitExceeded, invalidState, storageError, } from './types.js';
@@ -0,0 +1,81 @@
1
+ import { type ClaimFire, type ClaimResult, type ScheduleStore } from './store.js';
2
+ import { type AuthenticatedContext, type ConcurrencyPolicy, type MissedRunPolicy, type SchedulerAuthorizer, type SchedulerJob, type ScheduleSpec, type WallClock } from './types.js';
3
+ export declare const MAX_SCHEDULES_PER_PRINCIPAL = 100;
4
+ export declare const MAX_NAME_LENGTH = 100;
5
+ export declare const MAX_PROMPT_LENGTH = 4000;
6
+ /** New schedules must fire within this horizon, else the spec is rejected. */
7
+ export declare const MAX_SCHEDULE_HORIZON_MS: number;
8
+ export interface ValidatedSpec {
9
+ readonly name: string;
10
+ readonly cronExpr: string;
11
+ readonly timezone: string;
12
+ readonly promptText: string;
13
+ readonly enabled: boolean;
14
+ readonly concurrencyPolicy: ConcurrencyPolicy;
15
+ readonly missedRunPolicy: MissedRunPolicy;
16
+ readonly maxCatchUp: number;
17
+ readonly maxAttempts: number;
18
+ readonly baseRetryDelayMs: number;
19
+ readonly maxRetryDelayMs: number;
20
+ }
21
+ export declare function validateSpec(spec: ScheduleSpec, nowMs: number): ValidatedSpec;
22
+ export interface RegistryOptions {
23
+ readonly authorizer: SchedulerAuthorizer;
24
+ readonly clock?: WallClock;
25
+ readonly maxSchedulesPerPrincipal?: number;
26
+ }
27
+ export declare class ScheduleRegistry {
28
+ private readonly store;
29
+ private readonly authorizer;
30
+ private readonly clock;
31
+ private readonly maxSchedulesPerPrincipal;
32
+ constructor(store: ScheduleStore, options: RegistryOptions);
33
+ private requireCanManage;
34
+ /**
35
+ * Resolve the caller: an `AuthenticatedContext` minted by the host authN
36
+ * layer (preferred), or a raw principal-id string (deprecated escape
37
+ * hatch — see overload docs).
38
+ */
39
+ private callerPrincipalId;
40
+ /**
41
+ * @deprecated SECURITY: passing a raw `principalId` string trusts a
42
+ * caller-supplied principal-id with no binding to the authenticated caller
43
+ * (confused-deputy gap). Kept for backward compatibility only.
44
+ *
45
+ * HOST INTEGRATION (MUST): pass an `AuthenticatedContext` minted via
46
+ * `createAuthenticatedContext()` from the Gateway's verified session/auth
47
+ * context instead. The string MUST be the authenticated principal id —
48
+ * never user input, a request header, or plugin-supplied data.
49
+ */
50
+ create(context: AuthenticatedContext | string, spec: ScheduleSpec): Promise<SchedulerJob>;
51
+ /**
52
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
53
+ * The string MUST be the authenticated principal id.
54
+ */
55
+ update(context: AuthenticatedContext | string, jobId: string, patch: Partial<ScheduleSpec>): Promise<SchedulerJob>;
56
+ /**
57
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
58
+ * The string MUST be the authenticated principal id.
59
+ */
60
+ remove(context: AuthenticatedContext | string, jobId: string): Promise<void>;
61
+ /**
62
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
63
+ * The string MUST be the authenticated principal id.
64
+ */
65
+ setEnabled(context: AuthenticatedContext | string, jobId: string, enabled: boolean): Promise<SchedulerJob>;
66
+ /**
67
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
68
+ * The string MUST be the authenticated principal id.
69
+ */
70
+ get(context: AuthenticatedContext | string, jobId: string): Promise<SchedulerJob>;
71
+ /**
72
+ * Lists only the caller's own schedules (least privilege).
73
+ *
74
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
75
+ * The string MUST be the authenticated principal id.
76
+ */
77
+ list(context: AuthenticatedContext | string): Promise<SchedulerJob[]>;
78
+ }
79
+ /** Build claim fires for a set of fire-time ISO strings. */
80
+ export declare function buildClaimFires(job: SchedulerJob, fireTimesIso: readonly string[]): ClaimFire[];
81
+ export type { ClaimResult };
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Schedule registry: the management-plane API (`create`/`update`/`remove`/
3
+ * `enable`/`disable`/`list`). All input is validated here — the trust
4
+ * boundary — before anything reaches storage.
5
+ */
6
+ import { parseCron } from './cron.js';
7
+ import { computeNextFire, isUnsatisfiableCron } from './evaluator.js';
8
+ import { idempotencyKeyFor, newJobId, } from './store.js';
9
+ import { isValidTimeZone } from './timezone.js';
10
+ import { assertAuthenticatedContext, invalidSpec, notFound, unauthorized, } from './types.js';
11
+ export const MAX_SCHEDULES_PER_PRINCIPAL = 100;
12
+ export const MAX_NAME_LENGTH = 100;
13
+ export const MAX_PROMPT_LENGTH = 4000;
14
+ /** New schedules must fire within this horizon, else the spec is rejected. */
15
+ export const MAX_SCHEDULE_HORIZON_MS = 9 * 365 * 24 * 60 * 60 * 1000;
16
+ const CONCURRENCY_POLICIES = new Set(['forbid', 'allow', 'replace']);
17
+ const MISSED_RUN_POLICIES = new Set(['skip', 'run_once', 'catch_up_bounded']);
18
+ function asIso(ms) {
19
+ return new Date(ms).toISOString();
20
+ }
21
+ function validateInt(value, name, min, max, defaultValue) {
22
+ if (value === undefined)
23
+ return defaultValue;
24
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < min || value > max) {
25
+ throw invalidSpec(`${name} must be an integer in [${min}, ${max}]`);
26
+ }
27
+ return value;
28
+ }
29
+ export function validateSpec(spec, nowMs) {
30
+ if (typeof spec !== 'object' || spec === null)
31
+ throw invalidSpec('spec must be an object');
32
+ const name = spec.name;
33
+ if (typeof name !== 'string' || name.trim().length === 0 || name.length > MAX_NAME_LENGTH) {
34
+ throw invalidSpec(`name must be a non-empty string up to ${MAX_NAME_LENGTH} chars`);
35
+ }
36
+ if (typeof spec.cron !== 'string')
37
+ throw invalidSpec('cron must be a string');
38
+ const schedule = parseCron(spec.cron); // throws invalidSpec
39
+ // SECURITY FIX (unsatisfiable-cron CPU burn): reject statically-impossible
40
+ // day-of-month/month combinations BEFORE the expensive next-fire search —
41
+ // and before the quota check — so a hostile spec costs microseconds, not
42
+ // ~2s of event-loop CPU.
43
+ if (isUnsatisfiableCron(schedule)) {
44
+ throw invalidSpec('cron expression is unsatisfiable: no selected month contains the given day-of-month');
45
+ }
46
+ if (typeof spec.timezone !== 'string' || !isValidTimeZone(spec.timezone)) {
47
+ throw invalidSpec(`unknown IANA timezone: ${String(spec.timezone).slice(0, 64)}`);
48
+ }
49
+ if (typeof spec.promptText !== 'string' ||
50
+ spec.promptText.length === 0 ||
51
+ spec.promptText.length > MAX_PROMPT_LENGTH) {
52
+ throw invalidSpec(`promptText must be 1..${MAX_PROMPT_LENGTH} chars`);
53
+ }
54
+ const concurrencyPolicy = spec.concurrencyPolicy ?? 'forbid';
55
+ if (!CONCURRENCY_POLICIES.has(concurrencyPolicy)) {
56
+ throw invalidSpec(`unknown concurrencyPolicy: ${String(spec.concurrencyPolicy).slice(0, 32)}`);
57
+ }
58
+ const missedRunPolicy = spec.missedRunPolicy ?? 'skip';
59
+ if (!MISSED_RUN_POLICIES.has(missedRunPolicy)) {
60
+ throw invalidSpec(`unknown missedRunPolicy: ${String(spec.missedRunPolicy).slice(0, 32)}`);
61
+ }
62
+ const maxCatchUp = validateInt(spec.maxCatchUp, 'maxCatchUp', 1, 100, 3);
63
+ const maxAttempts = validateInt(spec.maxAttempts, 'maxAttempts', 1, 10, 3);
64
+ const baseRetryDelayMs = validateInt(spec.baseRetryDelayMs, 'baseRetryDelayMs', 100, 3600000, 5000);
65
+ const maxRetryDelayMs = validateInt(spec.maxRetryDelayMs, 'maxRetryDelayMs', 1000, 86400000, 300000);
66
+ if (maxRetryDelayMs < baseRetryDelayMs) {
67
+ throw invalidSpec('maxRetryDelayMs must be >= baseRetryDelayMs');
68
+ }
69
+ const enabled = spec.enabled ?? true;
70
+ if (typeof enabled !== 'boolean')
71
+ throw invalidSpec('enabled must be a boolean');
72
+ // The schedule must resolve to a fire time within the horizon.
73
+ const nextFire = computeNextFire(schedule, nowMs, spec.timezone);
74
+ if (nextFire === undefined || nextFire - nowMs > MAX_SCHEDULE_HORIZON_MS) {
75
+ throw invalidSpec('cron expression never fires within the supported horizon');
76
+ }
77
+ return {
78
+ name: name.trim(),
79
+ cronExpr: spec.cron.trim().replace(/\s+/g, ' '),
80
+ timezone: spec.timezone,
81
+ promptText: spec.promptText,
82
+ enabled,
83
+ concurrencyPolicy: concurrencyPolicy,
84
+ missedRunPolicy: missedRunPolicy,
85
+ maxCatchUp,
86
+ maxAttempts,
87
+ baseRetryDelayMs,
88
+ maxRetryDelayMs,
89
+ };
90
+ }
91
+ function systemClock() {
92
+ return { now: () => Date.now() };
93
+ }
94
+ export class ScheduleRegistry {
95
+ store;
96
+ authorizer;
97
+ clock;
98
+ maxSchedulesPerPrincipal;
99
+ constructor(store, options) {
100
+ this.store = store;
101
+ this.authorizer = options.authorizer;
102
+ this.clock = options.clock ?? systemClock();
103
+ this.maxSchedulesPerPrincipal = options.maxSchedulesPerPrincipal ?? MAX_SCHEDULES_PER_PRINCIPAL;
104
+ }
105
+ async requireCanManage(principalId) {
106
+ const ok = await this.authorizer.canManage(principalId);
107
+ if (!ok)
108
+ throw unauthorized(`principal ${principalId} may not manage schedules`);
109
+ }
110
+ /**
111
+ * Resolve the caller: an `AuthenticatedContext` minted by the host authN
112
+ * layer (preferred), or a raw principal-id string (deprecated escape
113
+ * hatch — see overload docs).
114
+ */
115
+ callerPrincipalId(context) {
116
+ if (typeof context === 'string')
117
+ return context;
118
+ assertAuthenticatedContext(context);
119
+ return context.principalId;
120
+ }
121
+ /**
122
+ * @deprecated SECURITY: passing a raw `principalId` string trusts a
123
+ * caller-supplied principal-id with no binding to the authenticated caller
124
+ * (confused-deputy gap). Kept for backward compatibility only.
125
+ *
126
+ * HOST INTEGRATION (MUST): pass an `AuthenticatedContext` minted via
127
+ * `createAuthenticatedContext()` from the Gateway's verified session/auth
128
+ * context instead. The string MUST be the authenticated principal id —
129
+ * never user input, a request header, or plugin-supplied data.
130
+ */
131
+ async create(context, spec) {
132
+ const principalId = this.callerPrincipalId(context);
133
+ await this.requireCanManage(principalId);
134
+ const nowMs = this.clock.now();
135
+ const validated = validateSpec(spec, nowMs);
136
+ const schedule = parseCron(validated.cronExpr);
137
+ const nextRunAt = validated.enabled
138
+ ? asIso(computeNextFire(schedule, nowMs, validated.timezone))
139
+ : null;
140
+ const job = {
141
+ id: newJobId(),
142
+ ownerPrincipalId: principalId,
143
+ ...validated,
144
+ nextRunAt,
145
+ createdAt: asIso(nowMs),
146
+ updatedAt: asIso(nowMs),
147
+ };
148
+ // Quota count + insert are one BEGIN IMMEDIATE transaction inside the
149
+ // store (multi-process check-then-act fix).
150
+ this.store.insertJobWithQuotaCheck(job, this.maxSchedulesPerPrincipal);
151
+ return job;
152
+ }
153
+ /**
154
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
155
+ * The string MUST be the authenticated principal id.
156
+ */
157
+ async update(context, jobId, patch) {
158
+ const principalId = this.callerPrincipalId(context);
159
+ await this.requireCanManage(principalId);
160
+ const existing = this.store.getJob(jobId);
161
+ if (existing === undefined)
162
+ throw notFound(`schedule ${jobId} not found`);
163
+ if (existing.ownerPrincipalId !== principalId)
164
+ throw unauthorized('schedule belongs to another principal');
165
+ const nowMs = this.clock.now();
166
+ const merged = {
167
+ name: patch.name ?? existing.name,
168
+ cron: patch.cron ?? existing.cronExpr,
169
+ timezone: patch.timezone ?? existing.timezone,
170
+ promptText: patch.promptText ?? existing.promptText,
171
+ enabled: patch.enabled ?? existing.enabled,
172
+ concurrencyPolicy: patch.concurrencyPolicy ?? existing.concurrencyPolicy,
173
+ missedRunPolicy: patch.missedRunPolicy ?? existing.missedRunPolicy,
174
+ maxCatchUp: patch.maxCatchUp ?? existing.maxCatchUp,
175
+ maxAttempts: patch.maxAttempts ?? existing.maxAttempts,
176
+ baseRetryDelayMs: patch.baseRetryDelayMs ?? existing.baseRetryDelayMs,
177
+ maxRetryDelayMs: patch.maxRetryDelayMs ?? existing.maxRetryDelayMs,
178
+ };
179
+ const validated = validateSpec(merged, nowMs);
180
+ const scheduleChanged = validated.cronExpr !== existing.cronExpr || validated.timezone !== existing.timezone;
181
+ const schedule = parseCron(validated.cronExpr);
182
+ const nextRunAt = scheduleChanged
183
+ ? validated.enabled
184
+ ? asIso(computeNextFire(schedule, nowMs, validated.timezone))
185
+ : null
186
+ : validated.enabled
187
+ ? (existing.nextRunAt ?? asIso(computeNextFire(schedule, nowMs, validated.timezone)))
188
+ : null;
189
+ const updated = this.store.updateJob(jobId, {
190
+ name: validated.name,
191
+ cronExpr: validated.cronExpr,
192
+ timezone: validated.timezone,
193
+ promptText: validated.promptText,
194
+ enabled: validated.enabled,
195
+ concurrencyPolicy: validated.concurrencyPolicy,
196
+ missedRunPolicy: validated.missedRunPolicy,
197
+ maxCatchUp: validated.maxCatchUp,
198
+ maxAttempts: validated.maxAttempts,
199
+ baseRetryDelayMs: validated.baseRetryDelayMs,
200
+ maxRetryDelayMs: validated.maxRetryDelayMs,
201
+ nextRunAt,
202
+ updatedAt: asIso(nowMs),
203
+ });
204
+ if (updated === undefined)
205
+ throw notFound(`schedule ${jobId} not found`);
206
+ return updated;
207
+ }
208
+ /**
209
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
210
+ * The string MUST be the authenticated principal id.
211
+ */
212
+ async remove(context, jobId) {
213
+ const principalId = this.callerPrincipalId(context);
214
+ await this.requireCanManage(principalId);
215
+ const existing = this.store.getJob(jobId);
216
+ if (existing === undefined)
217
+ throw notFound(`schedule ${jobId} not found`);
218
+ if (existing.ownerPrincipalId !== principalId)
219
+ throw unauthorized('schedule belongs to another principal');
220
+ this.store.deleteJob(jobId); // cascades runs
221
+ }
222
+ /**
223
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
224
+ * The string MUST be the authenticated principal id.
225
+ */
226
+ async setEnabled(context, jobId, enabled) {
227
+ // Resolve once here; update() re-resolves idempotently.
228
+ return this.update(this.callerPrincipalId(context), jobId, { enabled });
229
+ }
230
+ /**
231
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
232
+ * The string MUST be the authenticated principal id.
233
+ */
234
+ async get(context, jobId) {
235
+ const principalId = this.callerPrincipalId(context);
236
+ await this.requireCanManage(principalId);
237
+ const job = this.store.getJob(jobId);
238
+ if (job === undefined)
239
+ throw notFound(`schedule ${jobId} not found`);
240
+ if (job.ownerPrincipalId !== principalId)
241
+ throw unauthorized('schedule belongs to another principal');
242
+ return job;
243
+ }
244
+ /**
245
+ * Lists only the caller's own schedules (least privilege).
246
+ *
247
+ * @deprecated SECURITY: see `create()` — prefer `AuthenticatedContext`.
248
+ * The string MUST be the authenticated principal id.
249
+ */
250
+ async list(context) {
251
+ const principalId = this.callerPrincipalId(context);
252
+ await this.requireCanManage(principalId);
253
+ return this.store.listByOwner(principalId);
254
+ }
255
+ }
256
+ /** Build claim fires for a set of fire-time ISO strings. */
257
+ export function buildClaimFires(job, fireTimesIso) {
258
+ return fireTimesIso.map((scheduledFireAt) => ({
259
+ runId: `run_${job.id}_${scheduledFireAt}`,
260
+ idempotencyKey: idempotencyKeyFor(job.id, scheduledFireAt),
261
+ scheduledFireAt,
262
+ }));
263
+ }
@@ -0,0 +1,59 @@
1
+ import { type ScheduleStore } from './store.js';
2
+ import { type SchedulerJob, type TurnSubmitter, type WallClock } from './types.js';
3
+ export interface RunnerOptions {
4
+ readonly store: ScheduleStore;
5
+ readonly submitter: TurnSubmitter;
6
+ readonly clock?: WallClock;
7
+ readonly runnerId?: string;
8
+ /** Default 1000ms. */
9
+ readonly tickIntervalMs?: number;
10
+ /** Default 60s: how late a fire may be before it counts as misfired. */
11
+ readonly misfireGraceMs?: number;
12
+ /** Default 60s: claim lease before a crashed runner's runs are recovered. */
13
+ readonly leaseMs?: number;
14
+ /** Default 5s: monotonic bound for the drive phase of one tick. */
15
+ readonly maxTickDurationMs?: number;
16
+ /** Default 100. */
17
+ readonly claimBatchSize?: number;
18
+ /** Jitter source for backoff; injectable for deterministic tests. */
19
+ readonly jitter?: () => number;
20
+ readonly onError?: (error: unknown, phase: 'recover' | 'claim' | 'drive') => void;
21
+ }
22
+ export interface TickStats {
23
+ readonly recovered: number;
24
+ readonly claimed: number;
25
+ readonly skippedConcurrency: number;
26
+ readonly driven: number;
27
+ readonly completed: number;
28
+ readonly retried: number;
29
+ readonly deadLettered: number;
30
+ }
31
+ export declare class SchedulerRunner {
32
+ private readonly store;
33
+ private readonly submitter;
34
+ private readonly clock;
35
+ private readonly runnerId;
36
+ private readonly tickIntervalMs;
37
+ private readonly misfireGraceMs;
38
+ private readonly leaseMs;
39
+ private readonly maxTickDurationMs;
40
+ private readonly claimBatchSize;
41
+ private readonly jitter;
42
+ private readonly onError;
43
+ private timer;
44
+ private tickInFlight;
45
+ private stopped;
46
+ constructor(options: RunnerOptions);
47
+ get id(): string;
48
+ /** Start the non-overlapping tick chain. */
49
+ start(): void;
50
+ /** Stop ticking; resolves when the in-flight tick (if any) settles. */
51
+ stop(): Promise<void>;
52
+ private scheduleNext;
53
+ /** Run one full tick. Exposed for tests; `start()` uses it internally. */
54
+ tickOnce(nowMs?: number): Promise<TickStats>;
55
+ private claimJob;
56
+ private driveRun;
57
+ /** Bounded exponential backoff with jitter. */
58
+ backoffDelay(job: SchedulerJob, attempt: number): number;
59
+ }