@fastagent-sh/fastagent 0.21.1 → 0.22.0

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 (388) hide show
  1. package/README.md +9 -12
  2. package/dist/agent.d.ts +24 -37
  3. package/dist/agent.js +7 -15
  4. package/dist/atomic-write.d.ts +13 -17
  5. package/dist/atomic-write.js +18 -29
  6. package/dist/bind.d.ts +12 -22
  7. package/dist/bind.js +18 -27
  8. package/dist/channel.d.ts +11 -33
  9. package/dist/channels/agentcore-limits.d.ts +2 -13
  10. package/dist/channels/agentcore-limits.js +2 -13
  11. package/dist/channels/agentcore-protocol.d.ts +67 -58
  12. package/dist/channels/agentcore-protocol.js +23 -15
  13. package/dist/channels/agentcore-service.d.ts +19 -33
  14. package/dist/channels/agentcore-service.js +176 -59
  15. package/dist/channels/agentcore.d.ts +39 -33
  16. package/dist/channels/agentcore.js +90 -224
  17. package/dist/channels/body.d.ts +15 -2
  18. package/dist/channels/body.js +24 -2
  19. package/dist/channels/busy.d.ts +1 -21
  20. package/dist/channels/busy.js +2 -39
  21. package/dist/channels/control.d.ts +17 -62
  22. package/dist/channels/control.js +74 -182
  23. package/dist/channels/define-channel.d.ts +39 -0
  24. package/dist/channels/define-channel.js +9 -0
  25. package/dist/channels/discover.d.ts +10 -31
  26. package/dist/channels/discover.js +37 -31
  27. package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
  28. package/dist/channels/feishu/bootstrap-token.js +7 -23
  29. package/dist/channels/feishu/card.d.ts +14 -28
  30. package/dist/channels/feishu/card.js +16 -30
  31. package/dist/channels/feishu/cloud.d.ts +1 -5
  32. package/dist/channels/feishu/context-buffer.d.ts +4 -23
  33. package/dist/channels/feishu/context-buffer.js +8 -31
  34. package/dist/channels/feishu/crypto.d.ts +10 -5
  35. package/dist/channels/feishu/crypto.js +12 -19
  36. package/dist/channels/feishu/feishu-api.d.ts +2 -1
  37. package/dist/channels/feishu/feishu-api.js +5 -4
  38. package/dist/channels/feishu/feishu.d.ts +4 -10
  39. package/dist/channels/feishu/feishu.js +113 -227
  40. package/dist/channels/feishu/invoke-turn.d.ts +5 -4
  41. package/dist/channels/feishu/invoke-turn.js +36 -54
  42. package/dist/channels/feishu/model.d.ts +4 -21
  43. package/dist/channels/feishu/model.js +1 -8
  44. package/dist/channels/feishu/normalize.d.ts +2 -10
  45. package/dist/channels/feishu/normalize.js +7 -42
  46. package/dist/channels/feishu/parse.d.ts +9 -29
  47. package/dist/channels/feishu/parse.js +8 -26
  48. package/dist/channels/feishu/preview.d.ts +12 -14
  49. package/dist/channels/feishu/preview.js +108 -207
  50. package/dist/channels/feishu/register-app.d.ts +8 -23
  51. package/dist/channels/feishu/register-app.js +7 -20
  52. package/dist/channels/feishu/register-webhook.d.ts +2 -9
  53. package/dist/channels/feishu/register-webhook.js +9 -33
  54. package/dist/channels/feishu/scaffold/channel.ts +27 -19
  55. package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
  56. package/dist/channels/feishu/setup-mode.d.ts +11 -36
  57. package/dist/channels/feishu/setup-mode.js +7 -26
  58. package/dist/channels/feishu/ws-ingress.d.ts +1 -2
  59. package/dist/channels/feishu/ws-ingress.js +6 -8
  60. package/dist/channels/http.d.ts +15 -15
  61. package/dist/channels/http.js +19 -9
  62. package/dist/channels/kit/attachment-path.d.ts +4 -5
  63. package/dist/channels/kit/attachment-path.js +10 -34
  64. package/dist/channels/kit/context-buffer.d.ts +14 -21
  65. package/dist/channels/kit/context-buffer.js +25 -38
  66. package/dist/channels/kit/delivery.d.ts +35 -0
  67. package/dist/channels/kit/delivery.js +129 -0
  68. package/dist/channels/kit/event-stream.d.ts +5 -0
  69. package/dist/channels/kit/event-stream.js +29 -0
  70. package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
  71. package/dist/channels/kit/invoke-turn-kit.js +89 -32
  72. package/dist/channels/kit/preview-kit.d.ts +18 -72
  73. package/dist/channels/kit/preview-kit.js +22 -88
  74. package/dist/channels/kit/signature.d.ts +3 -9
  75. package/dist/channels/kit/signature.js +3 -9
  76. package/dist/channels/kit/state.d.ts +1 -2
  77. package/dist/channels/kit/state.js +5 -14
  78. package/dist/channels/kit/stop-command.d.ts +1 -2
  79. package/dist/channels/kit/stop-command.js +6 -9
  80. package/dist/channels/kit/tasks.d.ts +1 -1
  81. package/dist/channels/kit/tasks.js +8 -21
  82. package/dist/channels/kit/text.d.ts +2 -16
  83. package/dist/channels/kit/text.js +23 -31
  84. package/dist/channels/kit/thread-participants.d.ts +4 -17
  85. package/dist/channels/kit/thread-participants.js +8 -55
  86. package/dist/channels/kit/transport.d.ts +26 -0
  87. package/dist/channels/kit/transport.js +20 -0
  88. package/dist/channels/kit/turn-queue.d.ts +7 -11
  89. package/dist/channels/kit/turn-queue.js +21 -39
  90. package/dist/channels/kit/turn-runner.d.ts +32 -18
  91. package/dist/channels/kit/turn-runner.js +98 -53
  92. package/dist/channels/kit/turn-store.d.ts +32 -32
  93. package/dist/channels/kit/turn-store.js +45 -88
  94. package/dist/channels/lark/lark.d.ts +1 -5
  95. package/dist/channels/lark/onboard.d.ts +3 -12
  96. package/dist/channels/lark/onboard.js +3 -13
  97. package/dist/channels/lark/scaffold/channel.ts +27 -19
  98. package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
  99. package/dist/channels/registration.d.ts +9 -35
  100. package/dist/channels/registration.js +6 -37
  101. package/dist/channels/secret.js +2 -5
  102. package/dist/channels/serve.d.ts +47 -44
  103. package/dist/channels/serve.js +165 -101
  104. package/dist/channels/slack/config-api.d.ts +4 -27
  105. package/dist/channels/slack/config-api.js +5 -34
  106. package/dist/channels/slack/context-buffer.d.ts +2 -3
  107. package/dist/channels/slack/context-buffer.js +5 -5
  108. package/dist/channels/slack/invoke-turn.d.ts +3 -1
  109. package/dist/channels/slack/invoke-turn.js +25 -34
  110. package/dist/channels/slack/manifest.js +1 -3
  111. package/dist/channels/slack/model.d.ts +1 -2
  112. package/dist/channels/slack/model.js +1 -1
  113. package/dist/channels/slack/onboard.d.ts +4 -3
  114. package/dist/channels/slack/onboard.js +8 -11
  115. package/dist/channels/slack/onboarding-state.d.ts +3 -5
  116. package/dist/channels/slack/onboarding-state.js +9 -6
  117. package/dist/channels/slack/parse.d.ts +4 -13
  118. package/dist/channels/slack/parse.js +7 -28
  119. package/dist/channels/slack/preview.d.ts +12 -2
  120. package/dist/channels/slack/preview.js +292 -269
  121. package/dist/channels/slack/reaction.d.ts +11 -3
  122. package/dist/channels/slack/reaction.js +19 -8
  123. package/dist/channels/slack/register-webhook.js +4 -7
  124. package/dist/channels/slack/scaffold/channel.ts +20 -13
  125. package/dist/channels/slack/scaffold/slack-send.ts +2 -2
  126. package/dist/channels/slack/setup-server.d.ts +1 -5
  127. package/dist/channels/slack/setup-server.js +1 -5
  128. package/dist/channels/slack/shared-api.d.ts +1 -4
  129. package/dist/channels/slack/shared-api.js +2 -13
  130. package/dist/channels/slack/slack-api.d.ts +2 -1
  131. package/dist/channels/slack/slack-api.js +6 -4
  132. package/dist/channels/slack/slack.d.ts +11 -14
  133. package/dist/channels/slack/slack.js +121 -122
  134. package/dist/channels/sse.d.ts +1 -1
  135. package/dist/channels/sse.js +1 -1
  136. package/dist/channels/telegram/context-buffer.d.ts +7 -22
  137. package/dist/channels/telegram/context-buffer.js +6 -23
  138. package/dist/channels/telegram/invoke-turn.d.ts +7 -7
  139. package/dist/channels/telegram/invoke-turn.js +24 -43
  140. package/dist/channels/telegram/parse.d.ts +24 -51
  141. package/dist/channels/telegram/parse.js +32 -84
  142. package/dist/channels/telegram/preview.d.ts +10 -1
  143. package/dist/channels/telegram/preview.js +79 -100
  144. package/dist/channels/telegram/register-webhook.js +5 -23
  145. package/dist/channels/telegram/scaffold/channel.ts +25 -18
  146. package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
  147. package/dist/channels/telegram/telegram-api.d.ts +4 -3
  148. package/dist/channels/telegram/telegram-api.js +9 -7
  149. package/dist/channels/telegram/telegram.d.ts +4 -18
  150. package/dist/channels/telegram/telegram.js +38 -94
  151. package/dist/channels/telegram/turn-store.d.ts +11 -14
  152. package/dist/channels/telegram/turn-store.js +8 -6
  153. package/dist/channels/wait-health.d.ts +4 -6
  154. package/dist/channels/wait-health.js +10 -14
  155. package/dist/cli/add-feishu.d.ts +2 -12
  156. package/dist/cli/add-feishu.js +24 -71
  157. package/dist/cli/add-slack.d.ts +5 -3
  158. package/dist/cli/add-slack.js +53 -76
  159. package/dist/cli/auth-view.d.ts +2 -16
  160. package/dist/cli/auth-view.js +2 -16
  161. package/dist/cli/commands/add.js +25 -40
  162. package/dist/cli/commands/chat.d.ts +0 -1
  163. package/dist/cli/commands/chat.js +5 -6
  164. package/dist/cli/commands/deploy/agentcore.js +60 -65
  165. package/dist/cli/commands/deploy/docker.js +50 -23
  166. package/dist/cli/commands/deploy/fly.js +23 -61
  167. package/dist/cli/commands/deploy/railway.js +23 -18
  168. package/dist/cli/commands/deploy/shared.d.ts +38 -57
  169. package/dist/cli/commands/deploy/shared.js +40 -59
  170. package/dist/cli/commands/deploy.d.ts +10 -27
  171. package/dist/cli/commands/deploy.js +38 -60
  172. package/dist/cli/commands/destroy.d.ts +4 -0
  173. package/dist/cli/commands/destroy.js +56 -0
  174. package/dist/cli/commands/dev.d.ts +2 -1
  175. package/dist/cli/commands/dev.js +20 -23
  176. package/dist/cli/commands/info.d.ts +0 -2
  177. package/dist/cli/commands/info.js +83 -49
  178. package/dist/cli/commands/init.d.ts +1 -2
  179. package/dist/cli/commands/init.js +16 -55
  180. package/dist/cli/commands/invoke.d.ts +0 -1
  181. package/dist/cli/commands/invoke.js +5 -7
  182. package/dist/cli/commands/login.d.ts +2 -1
  183. package/dist/cli/commands/login.js +33 -38
  184. package/dist/cli/commands/logs.js +3 -4
  185. package/dist/cli/commands/models.js +1 -5
  186. package/dist/cli/commands/routine-run.d.ts +6 -0
  187. package/dist/cli/commands/routine-run.js +47 -0
  188. package/dist/cli/commands/routine.d.ts +25 -0
  189. package/dist/cli/commands/routine.js +122 -0
  190. package/dist/cli/commands/start.d.ts +31 -3
  191. package/dist/cli/commands/start.js +131 -81
  192. package/dist/cli/commands/tool.js +26 -12
  193. package/dist/cli/fail.d.ts +13 -23
  194. package/dist/cli/fail.js +20 -23
  195. package/dist/cli/invoke-stream.d.ts +1 -8
  196. package/dist/cli/kernel.d.ts +6 -38
  197. package/dist/cli/kernel.js +14 -77
  198. package/dist/cli/models-view.d.ts +1 -9
  199. package/dist/cli/models-view.js +11 -22
  200. package/dist/cli/program.d.ts +5 -6
  201. package/dist/cli/program.js +120 -157
  202. package/dist/cli/serve.d.ts +57 -41
  203. package/dist/cli/serve.js +94 -121
  204. package/dist/cli/shared.d.ts +15 -61
  205. package/dist/cli/shared.js +55 -181
  206. package/dist/cli.js +1 -8
  207. package/dist/collect.d.ts +6 -0
  208. package/dist/collect.js +6 -0
  209. package/dist/core.d.ts +4 -1
  210. package/dist/core.js +9 -7
  211. package/dist/declared-secrets.d.ts +55 -0
  212. package/dist/declared-secrets.js +88 -0
  213. package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
  214. package/dist/deploy/agentcore/aws-cli.js +64 -0
  215. package/dist/deploy/agentcore/destroy.d.ts +51 -0
  216. package/dist/deploy/agentcore/destroy.js +273 -0
  217. package/dist/deploy/agentcore/forwarder.js +28 -79
  218. package/dist/deploy/agentcore/logs.d.ts +2 -18
  219. package/dist/deploy/agentcore/logs.js +27 -51
  220. package/dist/deploy/agentcore/plan.d.ts +85 -105
  221. package/dist/deploy/agentcore/plan.js +152 -314
  222. package/dist/deploy/agentcore/run.d.ts +28 -28
  223. package/dist/deploy/agentcore/run.js +227 -306
  224. package/dist/deploy/agentcore/zip.d.ts +2 -15
  225. package/dist/deploy/agentcore/zip.js +3 -17
  226. package/dist/deploy/build-context.d.ts +21 -0
  227. package/dist/deploy/build-context.js +137 -0
  228. package/dist/deploy/channel-ingress.d.ts +26 -39
  229. package/dist/deploy/channel-ingress.js +57 -28
  230. package/dist/deploy/container.d.ts +20 -34
  231. package/dist/deploy/container.js +49 -100
  232. package/dist/deploy/docker/plan.d.ts +12 -11
  233. package/dist/deploy/docker/plan.js +43 -24
  234. package/dist/deploy/docker/run.d.ts +22 -25
  235. package/dist/deploy/docker/run.js +39 -37
  236. package/dist/deploy/fly/plan.d.ts +15 -44
  237. package/dist/deploy/fly/plan.js +41 -73
  238. package/dist/deploy/fly/run.d.ts +16 -51
  239. package/dist/deploy/fly/run.js +42 -95
  240. package/dist/deploy/hosts.d.ts +24 -3
  241. package/dist/deploy/hosts.js +24 -3
  242. package/dist/deploy/preflight.d.ts +47 -41
  243. package/dist/deploy/preflight.js +282 -383
  244. package/dist/deploy/railway/plan.d.ts +16 -42
  245. package/dist/deploy/railway/plan.js +42 -60
  246. package/dist/deploy/railway/run.d.ts +30 -68
  247. package/dist/deploy/railway/run.js +86 -100
  248. package/dist/deploy/registration-gate.d.ts +6 -13
  249. package/dist/deploy/residency.d.ts +37 -0
  250. package/dist/deploy/residency.js +24 -0
  251. package/dist/deploy/runner.d.ts +10 -9
  252. package/dist/deploy/runner.js +7 -18
  253. package/dist/deploy/secrets.d.ts +40 -41
  254. package/dist/deploy/secrets.js +117 -103
  255. package/dist/deploy/workspace.d.ts +50 -0
  256. package/dist/deploy/workspace.js +197 -0
  257. package/dist/dev-supervisor.d.ts +1 -9
  258. package/dist/dev-supervisor.js +21 -64
  259. package/dist/effect-port.d.ts +50 -0
  260. package/dist/effect-port.js +77 -0
  261. package/dist/engines/pi/agent-session-factory.d.ts +50 -69
  262. package/dist/engines/pi/agent-session-factory.js +249 -146
  263. package/dist/engines/pi/auth.d.ts +15 -11
  264. package/dist/engines/pi/auth.js +96 -99
  265. package/dist/engines/pi/chat.d.ts +1 -5
  266. package/dist/engines/pi/chat.js +2 -14
  267. package/dist/engines/pi/config.d.ts +53 -51
  268. package/dist/engines/pi/config.js +83 -92
  269. package/dist/engines/pi/create.d.ts +60 -75
  270. package/dist/engines/pi/create.js +108 -141
  271. package/dist/engines/pi/definition.d.ts +14 -58
  272. package/dist/engines/pi/definition.js +21 -85
  273. package/dist/engines/pi/invoke-session.d.ts +5 -25
  274. package/dist/engines/pi/invoke-session.js +209 -244
  275. package/dist/engines/pi/login.d.ts +95 -35
  276. package/dist/engines/pi/login.js +259 -107
  277. package/dist/engines/pi/machine.d.ts +31 -0
  278. package/dist/engines/pi/machine.js +106 -0
  279. package/dist/engines/pi/models.d.ts +70 -89
  280. package/dist/engines/pi/models.js +188 -81
  281. package/dist/engines/pi/open.d.ts +65 -53
  282. package/dist/engines/pi/open.js +129 -69
  283. package/dist/engines/pi/report.d.ts +3 -12
  284. package/dist/engines/pi/report.js +2 -16
  285. package/dist/engines/pi/search-tools.d.ts +1 -9
  286. package/dist/engines/pi/search-tools.js +26 -57
  287. package/dist/engines/pi/service.d.ts +2 -16
  288. package/dist/engines/pi/service.js +2 -16
  289. package/dist/engines/pi/session-builder.d.ts +5 -9
  290. package/dist/engines/pi/session-builder.js +44 -82
  291. package/dist/engines/pi/session-control.js +286 -269
  292. package/dist/engines/pi/session-effects.d.ts +14 -0
  293. package/dist/engines/pi/session-effects.js +30 -0
  294. package/dist/engines/pi/session-inheritance.d.ts +9 -18
  295. package/dist/engines/pi/session-inheritance.js +103 -160
  296. package/dist/engines/pi/session-markers.d.ts +31 -30
  297. package/dist/engines/pi/session-markers.js +26 -25
  298. package/dist/engines/pi/session-settings.d.ts +6 -22
  299. package/dist/engines/pi/session-settings.js +10 -21
  300. package/dist/engines/pi/session-store.d.ts +26 -92
  301. package/dist/engines/pi/session-store.js +96 -310
  302. package/dist/engines/pi/tool-context.d.ts +17 -45
  303. package/dist/engines/pi/tool-context.js +7 -26
  304. package/dist/engines/pi/tool.d.ts +45 -36
  305. package/dist/engines/pi/tool.js +43 -57
  306. package/dist/engines/pi/turn-kit.d.ts +14 -52
  307. package/dist/engines/pi/turn-kit.js +18 -67
  308. package/dist/engines/pi/wake-tool.d.ts +2 -10
  309. package/dist/engines/pi/wake-tool.js +7 -27
  310. package/dist/env.d.ts +27 -28
  311. package/dist/env.js +45 -50
  312. package/dist/index.js +0 -2
  313. package/dist/lark.d.ts +1 -2
  314. package/dist/lark.js +1 -2
  315. package/dist/loader.d.ts +14 -55
  316. package/dist/loader.js +24 -43
  317. package/dist/log.d.ts +2 -12
  318. package/dist/log.js +3 -24
  319. package/dist/node.d.ts +2 -15
  320. package/dist/node.js +2 -18
  321. package/dist/observe.d.ts +2 -8
  322. package/dist/open-url.d.ts +1 -1
  323. package/dist/open-url.js +1 -1
  324. package/dist/paths.d.ts +70 -134
  325. package/dist/paths.js +165 -232
  326. package/dist/pi.d.ts +4 -6
  327. package/dist/pi.js +3 -3
  328. package/dist/proxy.d.ts +16 -6
  329. package/dist/proxy.js +52 -16
  330. package/dist/runtime.d.ts +18 -12
  331. package/dist/runtime.js +18 -11
  332. package/dist/scaffold/add-channel.d.ts +14 -31
  333. package/dist/scaffold/add-channel.js +48 -112
  334. package/dist/scaffold/init.d.ts +9 -31
  335. package/dist/scaffold/init.js +45 -138
  336. package/dist/scaffold/templates/env.example +6 -4
  337. package/dist/scaffold/templates/fastagent.config.ts +36 -0
  338. package/dist/scaffold/templates.d.ts +3 -3
  339. package/dist/scaffold/templates.js +5 -8
  340. package/dist/scaffold/vendor-skill.d.ts +2 -5
  341. package/dist/scaffold/vendor-skill.js +19 -29
  342. package/dist/schedule/cron.d.ts +9 -5
  343. package/dist/schedule/cron.js +9 -11
  344. package/dist/schedule/discover.d.ts +12 -12
  345. package/dist/schedule/discover.js +43 -34
  346. package/dist/schedule/routine.d.ts +43 -0
  347. package/dist/schedule/routine.js +32 -0
  348. package/dist/schedule/run.d.ts +55 -0
  349. package/dist/schedule/run.js +146 -0
  350. package/dist/schedule/scheduler.d.ts +44 -55
  351. package/dist/schedule/scheduler.js +291 -265
  352. package/dist/schedule/state.d.ts +105 -10
  353. package/dist/schedule/state.js +293 -19
  354. package/dist/schedule/wake-alarm.d.ts +10 -23
  355. package/dist/schedule/wake-alarm.js +91 -121
  356. package/dist/schedule/wakeups.d.ts +23 -39
  357. package/dist/schedule/wakeups.js +33 -75
  358. package/dist/secrets-gate.d.ts +32 -0
  359. package/dist/secrets-gate.js +33 -0
  360. package/dist/service.d.ts +137 -98
  361. package/dist/service.js +250 -252
  362. package/dist/session-remote.d.ts +33 -44
  363. package/dist/session-remote.js +203 -192
  364. package/dist/session.d.ts +153 -228
  365. package/dist/session.js +33 -55
  366. package/dist/tunnel.d.ts +8 -36
  367. package/dist/tunnel.js +24 -65
  368. package/dist/version.d.ts +2 -2
  369. package/dist/version.js +2 -2
  370. package/package.json +10 -16
  371. package/dist/channels/agentcore-state.d.ts +0 -77
  372. package/dist/channels/agentcore-state.js +0 -276
  373. package/dist/channels/github/github.d.ts +0 -31
  374. package/dist/channels/github/github.js +0 -89
  375. package/dist/channels/github/scaffold/channel.ts +0 -25
  376. package/dist/cli/commands/attach.d.ts +0 -108
  377. package/dist/cli/commands/attach.js +0 -637
  378. package/dist/cli/commands/fire.d.ts +0 -7
  379. package/dist/cli/commands/fire.js +0 -38
  380. package/dist/cli/commands/schedule.d.ts +0 -12
  381. package/dist/cli/commands/schedule.js +0 -87
  382. package/dist/github.d.ts +0 -3
  383. package/dist/github.js +0 -2
  384. package/dist/scaffold/templates/fastagent.config.mjs +0 -23
  385. package/dist/schedule/audit.d.ts +0 -18
  386. package/dist/schedule/audit.js +0 -57
  387. package/dist/schedule/schedule.d.ts +0 -35
  388. package/dist/schedule/schedule.js +0 -23
@@ -1,52 +1,61 @@
1
1
  /**
2
- * Schedule discovery (the N axis, clock form): an agent declares its time-triggers by dropping files
3
- * in `schedules/`, mirroring `tools/` and `channels/`. Each file default-exports `defineSchedule({...})`,
4
- * named from its filename. This is the FILE producer of scheduled invocations (the author's, declarative,
5
- * git-tracked, deploy-guaranteed); the agent's `wake` tool is the second producer.
2
+ * Routine discovery: an agent declares its named units of work by dropping files in `routines/`, mirroring
3
+ * `tools/` and `channels/`.
6
4
  */
7
5
  import { join } from "node:path";
8
- import { loadModuleDir, moduleInventory } from "../loader.js";
6
+ import { loadModuleDir } from "../loader.js";
7
+ import { readSecretDeclaration } from "../declared-secrets.js";
9
8
  import { assertInsideAgentDir } from "../paths.js";
10
9
  import { cronError } from "./cron.js";
11
- /** Schedule file basenames under `<dir>/schedules/` — an existence probe listed WITHOUT importing
12
- * (deploy pre-flight's time-trigger detection; `fastagent info` uses {@link loadSchedules} instead,
13
- * since it also reports broken files and next instants). */
14
- export async function discoverScheduleFiles(dir) {
15
- await assertInsideAgentDir(dir, "schedules");
16
- const entries = await moduleInventory(join(dir, "schedules"));
17
- return entries.map((entry) => entry.name);
18
- }
10
+ import { isSafeScheduleName } from "./state.js";
19
11
  /**
20
- * Discover schedules in `<dir>/schedules/`: each file default-exports a `defineSchedule({...})`, named
21
- * from its filename. A file broken for ANY reason — a failed import, not a schedule (no cron/prompt), or
22
- * an invalid cron/tz — is ISOLATED into `failures` (skipped + reported, never a crash-loop, G2), the same
23
- * way `loadTools`/`loadChannels` isolate theirs. A duplicate name (foo.ts + foo.js) keeps the first and
24
- * reports the rest.
12
+ * Discover routines in `<dir>/routines/`: each file default-exports a `defineRoutine({...})`, named from its
13
+ * filename.
25
14
  */
26
- export async function loadSchedules(dir) {
27
- await assertInsideAgentDir(dir, "schedules");
28
- const { modules, failures } = await loadModuleDir(join(dir, "schedules"));
15
+ export async function loadRoutines(dir) {
16
+ await assertInsideAgentDir(dir, "routines");
17
+ const { modules, failures } = await loadModuleDir(join(dir, "routines"));
29
18
  const byName = new Map();
19
+ const secrets = new Map();
30
20
  for (const { name, label, file, mod } of modules) {
31
21
  try {
32
- const s = mod.default;
33
- if (!s || typeof s.cron !== "string" || typeof s.prompt !== "string") {
34
- throw new Error(`${label} must default-export defineSchedule({ cron, prompt })`);
22
+ const r = mod.default;
23
+ if (!r || typeof r.prompt !== "string") {
24
+ throw new Error(`${label} must default-export defineRoutine({ prompt, cron? })`);
25
+ }
26
+ const declaration = readSecretDeclaration(r, label);
27
+ if (declaration.error !== undefined)
28
+ throw new Error(declaration.error);
29
+ // NO CRON IS NOT AN ERROR: the routine is then reachable by name only (`POST /run`, the CLI, a scheduler
30
+ // that calls the route). A `tz` without one is, though — it would read as a time this routine does not have.
31
+ if (r.cron !== undefined && typeof r.cron !== "string")
32
+ throw new Error(`${label}: "cron" must be a string`);
33
+ if (r.cron === undefined && r.tz !== undefined) {
34
+ throw new Error(`${label}: "tz" means nothing without "cron" — remove it, or give this routine a cron`);
35
+ }
36
+ if (r.cron !== undefined) {
37
+ const err = cronError(r.cron, r.tz);
38
+ if (err)
39
+ throw new Error(`${label}: invalid cron/tz — ${err}`);
40
+ }
41
+ // The name becomes a path segment (the fired-slot claims live under `claims/<name>/`), so anything that could
42
+ // leave that directory is refused here — where the author sees which file is wrong — rather than deeper.
43
+ if (!isSafeScheduleName(name)) {
44
+ throw new Error(`${label}: a routine name cannot be ".", ".." or contain a path separator`);
35
45
  }
36
- const err = cronError(s.cron, s.tz);
37
- if (err)
38
- throw new Error(`${label}: invalid cron/tz — ${err}`);
39
- // "wake" is reserved: the run audit records self-scheduled wake-ups under that name, so a schedule
40
- // named wake would make `schedule history wake` an unreadable mix of two different things.
41
- if (name === "wake")
42
- throw new Error(`${label}: "wake" is a reserved schedule name (the self-scheduling audit uses it)`);
43
46
  if (byName.has(name))
44
- throw new Error(`${label}: duplicate schedule name "${name}" — kept the first`);
45
- byName.set(name, { name, cron: s.cron, tz: s.tz, prompt: s.prompt });
47
+ throw new Error(`${label}: duplicate routine name "${name}" — kept the first`);
48
+ byName.set(name, {
49
+ name,
50
+ prompt: r.prompt,
51
+ ...(r.cron !== undefined ? { cron: r.cron } : {}),
52
+ ...(r.tz !== undefined ? { tz: r.tz } : {}),
53
+ });
54
+ secrets.set(name, declaration.secrets);
46
55
  }
47
56
  catch (error) {
48
57
  failures.push({ label, file, message: error.message });
49
58
  }
50
59
  }
51
- return { schedules: [...byName.values()], failures };
60
+ return { routines: [...byName.values()], secrets, failures };
52
61
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * WHICH conversation a routine's turns belong to: all of them, so a routine shares one continuing conversation —
3
+ * and without depending on engine session storage. The ONE spelling of it: the clock invokes through it,
4
+ * `fastagent routine run` reproduces a run with it, `POST /run` reports it as where to look, and
5
+ * `routine history` prints the command that opens it.
6
+ */
7
+ export declare function routineSession(name: string): string;
8
+ /** A named unit of work the definition owns. With a `cron`, a clock fires it; without one, only its name reaches it. */
9
+ export interface Routine {
10
+ /**
11
+ * 5-field cron expression (`minute hour day-of-month month day-of-week`), or absent.
12
+ *
13
+ * ABSENT IS A REAL CHOICE, not an omission: the routine is then reachable only by name — `POST /run`, the CLI, a
14
+ * platform scheduler that calls the route. A time is one way to ask for work, not what the work is.
15
+ */
16
+ cron?: string;
17
+ /** IANA timezone for the cron (default "UTC"). Meaningless without `cron`. */
18
+ tz?: string;
19
+ /** The turn's text = the routine's instruction. */
20
+ prompt: string;
21
+ /** Env vars this routine declared — carried by `deploy`, asserted before the clock starts. */
22
+ secrets?: readonly string[];
23
+ }
24
+ /** What an author writes. `prompt` may be built FROM the declared secrets, which is the only reason a
25
+ * routine file ever read the environment (a channel id, a target inbox). */
26
+ export interface DefineRoutineOptions<S extends readonly string[]> {
27
+ cron?: string;
28
+ tz?: string;
29
+ prompt: string | ((secrets: Record<S[number], string>) => string);
30
+ /**
31
+ * Env vars this routine needs (`["SLACK_DIGEST_CHANNEL"]`), typed into the `prompt` builder.
32
+ * `deploy` carries them and the clock refuses to start while one is unset, naming this file
33
+ * (see src/declared-secrets.ts) — the guarantees a bare `process.env` read cannot have.
34
+ */
35
+ secrets?: S;
36
+ }
37
+ /** Type + completion for a routine file, and the ONE place a prompt builder is resolved (at load:
38
+ * what the loader stores is always a string, so every consumer sees one shape). */
39
+ export declare function defineRoutine<const S extends readonly string[] = readonly []>(routine: DefineRoutineOptions<S>): Routine;
40
+ /** A loaded routine: its authored fields plus the name derived from its filename (authoritative). */
41
+ export interface LoadedRoutine extends Routine {
42
+ name: string;
43
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Routine authoring: `defineRoutine`.
3
+ *
4
+ * A ROUTINE IS THE UNIT OF WORK, and the only named one in the system: a prompt the definition owns, addressed by
5
+ * name. `cron` is a FIELD of it, not a second concept — with one, a clock fires it; without one, it is reached by
6
+ * name alone (`POST /run`).
7
+ *
8
+ * WHAT IT IS NOT: a wake-up. The agent can schedule work for itself too (the `wake` tool, schedule/wakeups.ts), and
9
+ * conceptually that is the same idea — work to be done later. Every operational difference between them follows
10
+ * from ONE root: a routine is written in the DEFINITION (versioned, reviewed, shipped with the image, addressable
11
+ * by name) and a wake-up is written into the STATE by a running agent (minted id, cancellable, aimed back at the
12
+ * conversation it came from, erased by the next release if it were a definition file). Code and data.
13
+ *
14
+ * A `prompt` may be a function of the file's declared `secrets` — the only reason such a file ever read the
15
+ * environment (src/declared-secrets.ts).
16
+ */
17
+ import { secretValues } from "../declared-secrets.js";
18
+ /**
19
+ * WHICH conversation a routine's turns belong to: all of them, so a routine shares one continuing conversation —
20
+ * and without depending on engine session storage. The ONE spelling of it: the clock invokes through it,
21
+ * `fastagent routine run` reproduces a run with it, `POST /run` reports it as where to look, and
22
+ * `routine history` prints the command that opens it.
23
+ */
24
+ export function routineSession(name) {
25
+ return `routine:${name}`;
26
+ }
27
+ /** Type + completion for a routine file, and the ONE place a prompt builder is resolved (at load:
28
+ * what the loader stores is always a string, so every consumer sees one shape). */
29
+ export function defineRoutine(routine) {
30
+ const { prompt, ...rest } = routine;
31
+ return { ...rest, prompt: typeof prompt === "function" ? prompt(secretValues(routine.secrets)) : prompt };
32
+ }
@@ -0,0 +1,55 @@
1
+ import type { Agent } from "../agent.ts";
2
+ import { type LoadedRoutine } from "./routine.ts";
3
+ /**
4
+ * The 404 for a name this deployment does not declare — drift: a caller outliving the thing it calls. The names are
5
+ * listed so an operator can see whether it is a typo or a stale job without shelling in. The caller's name is CLIPPED
6
+ * before it goes back out: listing this deployment's own names is deliberate, quoting an unauthenticated caller's
7
+ * 4 KiB of body is not (`refuseNonJsonBody` states the same rule for the same table).
8
+ */
9
+ export declare function unknownRoutine(name: string, routines: readonly LoadedRoutine[]): Response;
10
+ /** The reply shape both doors answer with — one for the route, one for a host that has no route (AgentCore). */
11
+ export interface RoutineRunReply {
12
+ name: string;
13
+ /** WHERE TO LOOK: the turn's output lives in this session's journal (`fastagent routine history`, `/control/*`). */
14
+ session: string;
15
+ ran: boolean;
16
+ /** Present when `ran: false` — today, the previous turn still holding this routine's one session. */
17
+ reason?: string;
18
+ /** Present when the turn RAN and did not finish. */
19
+ failed?: string;
20
+ ms: number;
21
+ }
22
+ /**
23
+ * Run one routine and say what happened. THE one implementation, because there are two doors and only one
24
+ * contract: `POST /run` on any host that publishes routes, and AgentCore's IAM-gated `routine-run` envelope on the
25
+ * host that publishes none. A second copy is how the two would come to answer differently.
26
+ */
27
+ export declare function runRoutineByName(agent: Agent, routine: LoadedRoutine): Promise<RoutineRunReply>;
28
+ /**
29
+ * Build the handler for `GET /routines` — what a caller may ask for by name.
30
+ *
31
+ * DISCOVERY, because the alternative was a 404. The names were already public: `POST /run` lists them when a caller
32
+ * gets one wrong, which is deliberate (an operator has to tell a typo from a stale caller) — so the only thing this
33
+ * adds is not having to guess wrong first. "Expose a declared prompt as an API" is half a sentence without it.
34
+ *
35
+ * NAMES AND SCHEDULES, NEVER PROMPTS. What a routine SAYS is the definition's content; handing it to an
36
+ * unauthenticated caller would publish the agent's behaviour, which is the one thing keeping the prompt out of the
37
+ * request body was for. `cron` is here because it answers "will this run on its own, or is my clock the only one?",
38
+ * which is a caller's question, not the definition's secret.
39
+ *
40
+ * Mounted exactly where `POST /run` is: it describes that route, so listing names a caller cannot use would be a
41
+ * catalogue of nothing.
42
+ */
43
+ export declare function createRoutineListHandler(options: {
44
+ routines: readonly LoadedRoutine[];
45
+ }): ((req: Request) => Promise<Response>) | undefined;
46
+ /**
47
+ * Build the handler for `POST /run`, bound to what this serve loaded.
48
+ *
49
+ * `undefined` when the definition declares nothing runnable: a route that can only ever answer 404 is not a route,
50
+ * and the startup report names what is actually mounted.
51
+ */
52
+ export declare function createRunHandler(options: {
53
+ agent: Agent;
54
+ routines: readonly LoadedRoutine[];
55
+ }): ((req: Request) => Promise<Response>) | undefined;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * `POST /run` — **an API**: run a unit of work this definition declares, by name.
3
+ *
4
+ * NOT A TIME TRIGGER, and the difference is the whole design. Occurrence semantics — a slot, a claim, a fire
5
+ * history, an overlap policy — exist exactly where fastagent OWNS the clock: the resident loop in `dev`/`start`,
6
+ * and AgentCore, where `deploy` writes the EventBridge rule, injects the instant and relays it through an ingress
7
+ * secret (channels/agentcore.ts). Both of those know which occurrence a run is for because they produced it.
8
+ *
9
+ * This route is the other case: somebody else's clock, CI job, button or script. It cannot be told which occurrence
10
+ * it means, and pretending otherwise was measurably worse than admitting it. The attempt cost four rounds — an
11
+ * instant dated ahead poisoned the claim gate permanently, one off the grid minted a claim no occurrence would ever
12
+ * match, an old one replayed history a turn at a time, and the grid arithmetic all three needed was ~6ms of
13
+ * synchronous CPU per request on an anonymous route. A platform cron drifting a few minutes then lost an occurrence
14
+ * outright (Railway documents "a few minutes" of variance; measured: 3 fires, 2 turns).
15
+ *
16
+ * So the contract is the one an API has:
17
+ *
18
+ * - the body names WHAT to run and nothing else — the prompt stays in the definition, which is the whole reason
19
+ * this exists beside `POST /invoke`, whose caller brings its own text and therefore its own behaviour
20
+ * - the reply says what happened and WHERE TO LOOK (the session the turn ran in). Retry policy is the caller's,
21
+ * because only the caller knows whether its work tolerates running twice
22
+ * - it is exactly as exposed as `POST /invoke`: unauthenticated, full tool authority, `http.invoke: false` and a
23
+ * gateway in front are the answers. The framework authenticates nothing (docs/design/session-control.md §14)
24
+ *
25
+ * AND NO IDEMPOTENCY KEY. A key would deduplicate the CALL, not the WORK: a turn that sent one message and then died
26
+ * would answer a keyed retry with "already ran" — safety exactly where it is absent, which is the same reason a failed
27
+ * fire is not retried (schedule/scheduler.ts). What makes a retry safe is idempotent WORK, which only the author can
28
+ * arrange.
29
+ *
30
+ * NAME IN THE BODY, not the path. A declared name is a filename (`每日简报`, `my schedule` are both legal), and a
31
+ * path segment would mean percent-encoding it — the cost the control plane pays for session ids and this route has
32
+ * no reason to. It also keeps the shape of `POST /invoke`, whose scope rides in the body for the same reason.
33
+ */
34
+ import * as Effect from "effect/Effect";
35
+ import { readBodyCapped, refuseNonJsonBody } from "../channels/body.js";
36
+ import { text } from "../channels/respond.js";
37
+ import { routineSession } from "./routine.js";
38
+ import { runTurn } from "./scheduler.js";
39
+ /** A trigger body is a name; the cap only has to admit that. */
40
+ const MAX_RUN_BODY_BYTES = 4 * 1024;
41
+ /**
42
+ * How much of a caller's `name` a 404 will quote back — the ONE thing this route echoes, because telling a typo
43
+ * from a stale caller needs the name and nothing else does. Long enough to recognise one, short enough not to be a
44
+ * page.
45
+ */
46
+ const MAX_ECHOED_NAME = 64;
47
+ /**
48
+ * The 404 for a name this deployment does not declare — drift: a caller outliving the thing it calls. The names are
49
+ * listed so an operator can see whether it is a typo or a stale job without shelling in. The caller's name is CLIPPED
50
+ * before it goes back out: listing this deployment's own names is deliberate, quoting an unauthenticated caller's
51
+ * 4 KiB of body is not (`refuseNonJsonBody` states the same rule for the same table).
52
+ */
53
+ export function unknownRoutine(name, routines) {
54
+ return text(`no declared work named "${name.slice(0, MAX_ECHOED_NAME)}" (this deployment has: ${routines
55
+ .map((r) => r.name)
56
+ .join(", ")})\n`, 404);
57
+ }
58
+ /** The Effect boundary, in one place: the turn runs, and its outcome is what the caller reads. */
59
+ const runOnce = (agent, routine) => Effect.runPromise(runTurn(agent, routine.name, routineSession(routine.name), routine.prompt));
60
+ /**
61
+ * Run one routine and say what happened. THE one implementation, because there are two doors and only one
62
+ * contract: `POST /run` on any host that publishes routes, and AgentCore's IAM-gated `routine-run` envelope on the
63
+ * host that publishes none. A second copy is how the two would come to answer differently.
64
+ */
65
+ export async function runRoutineByName(agent, routine) {
66
+ const session = routineSession(routine.name);
67
+ const { busy, failed, ms } = await runOnce(agent, routine);
68
+ // BUSY IS NOT A FAILURE and it is the one "did not run" this has: a declared unit of work has ONE session, so a
69
+ // call arriving while the previous turn holds it is refused by that session. Reported as itself, with what the
70
+ // caller needs in order to decide — try later — rather than as an error it would retry blindly.
71
+ if (busy) {
72
+ return {
73
+ name: routine.name,
74
+ session,
75
+ ran: false,
76
+ reason: `the previous turn of "${routine.name}" is still running`,
77
+ ms,
78
+ };
79
+ }
80
+ return { name: routine.name, session, ran: true, ...(failed !== undefined ? { failed } : {}), ms };
81
+ }
82
+ /**
83
+ * Build the handler for `GET /routines` — what a caller may ask for by name.
84
+ *
85
+ * DISCOVERY, because the alternative was a 404. The names were already public: `POST /run` lists them when a caller
86
+ * gets one wrong, which is deliberate (an operator has to tell a typo from a stale caller) — so the only thing this
87
+ * adds is not having to guess wrong first. "Expose a declared prompt as an API" is half a sentence without it.
88
+ *
89
+ * NAMES AND SCHEDULES, NEVER PROMPTS. What a routine SAYS is the definition's content; handing it to an
90
+ * unauthenticated caller would publish the agent's behaviour, which is the one thing keeping the prompt out of the
91
+ * request body was for. `cron` is here because it answers "will this run on its own, or is my clock the only one?",
92
+ * which is a caller's question, not the definition's secret.
93
+ *
94
+ * Mounted exactly where `POST /run` is: it describes that route, so listing names a caller cannot use would be a
95
+ * catalogue of nothing.
96
+ */
97
+ export function createRoutineListHandler(options) {
98
+ const { routines } = options;
99
+ if (routines.length === 0)
100
+ return undefined;
101
+ const body = routines.map((r) => ({
102
+ name: r.name,
103
+ ...(r.cron !== undefined ? { cron: r.cron } : {}),
104
+ ...(r.tz !== undefined ? { tz: r.tz } : {}),
105
+ }));
106
+ // NO METHOD CHECK. The router dispatches `GET /routines` here and nothing else, so the only thing such a check
107
+ // could reach is HEAD — which `channels/serve.ts` deliberately answers with the GET route, the same way
108
+ // `GET /health` is HEAD-probeable. Checking would break that and buy nothing.
109
+ return async () => Response.json(body);
110
+ }
111
+ /**
112
+ * Build the handler for `POST /run`, bound to what this serve loaded.
113
+ *
114
+ * `undefined` when the definition declares nothing runnable: a route that can only ever answer 404 is not a route,
115
+ * and the startup report names what is actually mounted.
116
+ */
117
+ export function createRunHandler(options) {
118
+ const { agent, routines } = options;
119
+ if (routines.length === 0)
120
+ return undefined;
121
+ return async (req) => {
122
+ if (req.method !== "POST")
123
+ return text("POST only\n", 405);
124
+ const wrongType = refuseNonJsonBody(req);
125
+ if (wrongType)
126
+ return wrongType;
127
+ const body = await readBodyCapped(req, MAX_RUN_BODY_BYTES);
128
+ if ("tooLarge" in body)
129
+ return text("body too large\n", 413);
130
+ let payload;
131
+ try {
132
+ payload = JSON.parse(body.text);
133
+ }
134
+ catch {
135
+ return text("invalid json\n", 400);
136
+ }
137
+ const { name } = (payload ?? {});
138
+ if (typeof name !== "string" || name === "") {
139
+ return text('need { "name": string } — e.g. {"name":"daily"}\n', 400);
140
+ }
141
+ const routine = routines.find((r) => r.name === name);
142
+ if (!routine)
143
+ return unknownRoutine(name, routines);
144
+ return Response.json(await runRoutineByName(agent, routine));
145
+ };
146
+ }
@@ -1,75 +1,64 @@
1
- /**
2
- * The scheduler: a time-trigger that fires the agent on each schedule's cron. SINGLE-PROCESS (like all
3
- * fastagent state today) — a deployment with schedules must keep one machine running, since cron has no
4
- * external wake-up (`deploy` enforces that). Started/stopped by the serve path (dev/start).
5
- *
6
- * Fire model:
7
- * - **stable session per schedule** (`schedule:<name>`), so a schedule's turns share one continuing
8
- * conversation persisted by the core session store; the scheduler is ZERO-touch on session storage.
9
- * - **output is the agent's tools' job** — the scheduler only fires and logs the outcome.
10
- * - **durability = catch up ONCE** (`state.ts`): on start, if the next instant after the last fire is
11
- * already past (the process was down across it), fire once and advance — not once per missed slot.
12
- * lastFired is claimed BEFORE the invoke (at-most-once per slot: a crash mid-turn won't re-fire it;
13
- * "a digest late once" beats "twice"). Strict at-least-once (a per-turn WAL) is a later tier.
14
- */
1
+ import * as Effect from "effect/Effect";
15
2
  import { type Agent } from "../agent.ts";
16
- import type { LoadedSchedule } from "./schedule.ts";
17
- /** A schedule's turns share this stable session — a continuing conversation, like the telegram channel's
18
- * per-chat session. Derived at RUNTIME from the name (never an authored field). */
19
- export declare function scheduleSession(name: string): string;
3
+ import { PortFailure } from "../effect-port.ts";
4
+ import { type LoadedRoutine } from "./routine.ts";
20
5
  export interface Scheduler {
21
- /** Arm every schedule (catching up an overdue one once). Idempotent-ish: call once per process. */
6
+ /** Arm routines, account for a fire the previous run was killed in, and catch up overdue work once. */
22
7
  start(): void;
23
- /** Clear all armed timers. Does NOT drain an in-flight fire (SIGTERM exits mid-turn by design; the
24
- * interrupted turn is not re-fired — lastFired was already claimed). A catch-up fire in flight has NO
25
- * timer entry to clear either (the overdue branch fires directly, without arming), so `stop()` simply
26
- * lets it run out or be cut by process exit — same non-drain, and its claim is already persisted. */
8
+ /** Cancel pending waits. */
27
9
  stop(): void;
28
10
  }
29
11
  export interface SchedulerOptions {
30
12
  agent: Agent;
31
13
  stateRoot: string;
32
- schedules: LoadedSchedule[];
33
- /** Injectable clock for tests; defaults to the wall clock. */
14
+ /** Every routine this serve loaded. Only the ones WITH a cron are armed — the rest are reached by name. */
15
+ routines: LoadedRoutine[];
16
+ /** Override wall-clock dates; elapsed time and waits use the Effect clock. */
34
17
  now?: () => Date;
35
- /** External-clock mode (the AgentCore deployment): cron slots are DELIVERED by an external
36
- * scheduler through the serving surface ({@link fireScheduleOnce} with a `slot`), so `start()`
37
- * arms NO cron timers and does NO boot catch-up — delivery, including for instants that passed
38
- * while this process was down, is the external clock's job; the adapter's slot-idempotent claim
39
- * is the duplicate guard. The wake-up poll still runs (degraded: it only fires while the
40
- * process happens to be awake — the deploy path warns about this). */
18
+ /** External slot delivery owns cron timers and catch-up; local wake polling still runs. */
41
19
  externalClock?: boolean;
42
20
  }
43
- /** One schedule fire's outcome, for the external caller (the AgentCore adapter returns it to the
44
- * triggering clock's logs). The resident scheduler ignores it beyond completion. */
21
+ /**
22
+ * The iterator is a Promise port inside an uninterruptible claimed occurrence, including its cleanup.
23
+ *
24
+ * WHAT THE TURN SAID IS NOT LOGGED. It is already durable: every fire runs in the session named in the line below
25
+ * (`routine:<name>` for a cron, the asking conversation for a wake-up), and a session is persisted under
26
+ * EXPORTED for the API path (`POST /run`), which runs the same turn without a claim: it has no occurrence to
27
+ * claim, because nobody's grid produced it (schedule/run.ts). The resident clock's own claim/run/settle is
28
+ * {@link fireRoutineOnce}.
29
+ *
30
+ * `<stateRoot>/sessions/` like any other. Copying the reply into the log would be a second store of the same text,
31
+ * unstructured, in a stream with a wider audience and a bound that differs per host.
32
+ */
33
+ export declare function runTurn(agent: Agent, label: string, session: string, prompt: string): Effect.Effect<{
34
+ busy: boolean;
35
+ failed: string | undefined;
36
+ ms: number;
37
+ } | {
38
+ busy: boolean;
39
+ failed: string;
40
+ ms: number;
41
+ }, never, never>;
45
42
  export interface ScheduleFireOutcome {
46
43
  fired: boolean;
47
- /** Set when a slot-keyed fire was skipped because that slot (or a later one) was already claimed. */
44
+ /** A delivery whose slot was already claimed, or which is older than the newest claim (a stale replay). */
48
45
  skippedReason?: string;
49
46
  failed?: string;
47
+ /**
48
+ * The occurrence was claimed but the turn was refused because the previous one is still running — the overlap
49
+ * policy, not a fault. Reported apart from `failed` because an operator reacts to the two differently, and a
50
+ * clock that reads `failed` on a public route would be reading an error that is not one.
51
+ */
52
+ skipped?: true;
50
53
  ms: number;
51
54
  }
52
- /**
53
- * Fire ONE schedule's turn: claim (persist lastFired BEFORE invoking, so a crash mid-turn does not
54
- * re-fire on restart), run, audit. Shared by the resident scheduler's timers and the external-clock
55
- * serving surface (the AgentCore adapter).
56
- *
57
- * `slot` is the external clock's idempotency key — the cron instant this fire is FOR. External
58
- * delivery (EventBridge-style) is at-least-once, so a duplicate slot must not double-fire: when
59
- * lastFired ≥ slot the fire is SKIPPED (at-most-once per slot, same trade as the resident claim —
60
- * "a digest late once" beats "twice"). The resident scheduler omits `slot`: its timers fire each
61
- * slot exactly once, so the unconditional claim is already correct.
62
- *
63
- * A state fault while claiming (loadFires/saveFires — both before the invoke, so nothing ran)
64
- * THROWS: the resident path catches it at its single skip+audit boundary ({@link createScheduler}'s
65
- * fireThenReArm — skipping rather than firing unclaimed also avoids an infinite catch-up loop on
66
- * restart), and the external path lets it surface as a failed request (visible in the clock's logs).
67
- */
68
- export declare function fireScheduleOnce(opts: {
55
+ /** Shared resident/external claim → run → settle. */
56
+ export declare function fireRoutineOnce(opts: {
69
57
  agent: Agent;
70
58
  stateRoot: string;
71
- schedule: LoadedSchedule;
72
- slot?: Date;
59
+ routine: LoadedRoutine;
60
+ /** The instant this fire is FOR. Two schedulers agree on it, which is what makes the claim exclude. */
61
+ slot: Date;
73
62
  now?: () => Date;
74
- }): Promise<ScheduleFireOutcome>;
75
- export declare function createScheduler({ agent, stateRoot, schedules, now, externalClock, }: SchedulerOptions): Scheduler;
63
+ }): Effect.Effect<ScheduleFireOutcome, PortFailure>;
64
+ export declare function createScheduler(options: SchedulerOptions): Effect.Effect<Scheduler>;