@fastagent-sh/fastagent 0.21.1 → 0.22.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 (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 +233 -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 +166 -229
  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,38 +1,25 @@
1
- /**
2
- * `fastagent init [dir]`: scaffold a runnable agent and install its dependencies. Where the files land is
3
- * a default plus one knob, never a detection and never a prompt — non-interactive executors (coding
4
- * agents) get deterministic behavior they can read. By default the agent goes into `./fastagent/` and the
5
- * directory around it is untouched; `--agent-dir <name>` names that directory anything, and `--agent-dir .`
6
- * (spelled `--flat`) makes the directory itself the agent (a standalone agent repo, a monorepo package).
7
- */
1
+ /** `fastagent init [dir]`: scaffold a runnable agent and install its dependencies. */
8
2
  import { spawn } from "node:child_process";
9
- import { basename, join, resolve } from "node:path";
10
- import { DEFAULT_AGENT_DIRNAME, SECRETS_DIRNAME, agentsAt, displayPath } from "../../paths.js";
11
- import { detectRuntime, readPackageJson } from "../../runtime.js";
3
+ import { basename, resolve } from "node:path";
4
+ import { DEFAULT_AGENT_DIRNAME, agentsAt, displayPath } from "../../paths.js";
12
5
  import { agentDirName, agentDirNameError, scaffoldAgent } from "../../scaffold/init.js";
13
6
  import { failStartup, failUsage } from "../fail.js";
14
7
  export async function runInit(dirArg, opts) {
15
8
  const dir = resolve(dirArg);
16
- // A rejected flag VALUE is the usage class, same as one the parser rejects — so it is checked here,
17
- // where exit 2 lives, not inside the scaffolder (whose throws are runtime failures, exit 1).
9
+ // A rejected flag VALUE is the usage class, same as one the parser rejects.
18
10
  const requested = agentDirName(opts.agentDir);
19
11
  const invalid = agentDirNameError(requested);
20
12
  if (invalid)
21
13
  failUsage(`--agent-dir "${requested}" ${invalid}`);
22
- const { complete, agentDir: rel, created, kept, } = await scaffoldAgent(dir, {
23
- minimal: opts.minimal,
24
- agentDir: requested,
25
- }).catch(failStartup);
26
- const flat = rel === ".";
27
- // The agent dir is where the manifest lives, so any install runs there — never against a surrounding
28
- // workspace's package.json (its deps are its own concern). With `.` they are the same directory.
14
+ const { agentDir: rel, created } = await scaffoldAgent(dir, { agentDir: requested }).catch(failStartup);
15
+ // The agent dir is where the manifest lives, so the install runs there — never against a surrounding workspace's
16
+ // package.json (its deps are its own concern).
29
17
  const agentDir = resolve(dir, rel);
30
- console.error(`[fastagent] initialized ${dir}${complete ? "" : " (minimal)"} — ${flat ? "the directory IS the agent" : `agent in ./${rel}/`}`);
18
+ console.error(`[fastagent] initialized ${dir} — agent in ./${rel}/`);
31
19
  console.error(` created: ${created.join(", ")}`);
32
- // A second agent beside an existing one is a supported shape, not an accident (an engineer's, a PM's
33
- // and a content owner's agent can drive one repository), but it changes how this workspace resolves
34
- // from now on — so say it HERE, at the moment it becomes true, instead of at the next command's
35
- // refusal. The default name is the tie-break, so a workspace keeping `fastagent/` needs nothing.
20
+ // A second agent beside an existing one is a supported shape, not an accident (an engineer's, a PM's and a content
21
+ // owner's agent can drive one repository), but it changes how this workspace resolves from now on — so say it HERE,
22
+ // at the moment it becomes true, instead of at the next command's refusal.
36
23
  const siblings = agentsAt(dir).map((a) => basename(a));
37
24
  if (siblings.length > 1) {
38
25
  const pick = siblings.includes(DEFAULT_AGENT_DIRNAME)
@@ -40,34 +27,9 @@ export async function runInit(dirArg, opts) {
40
27
  : `set FASTAGENT_AGENT=<name> (in your shell or .envrc) to pick one`;
41
28
  console.error(`[fastagent] note: ${dir} now holds ${siblings.length} agents (${siblings.join(", ")}) — ${pick}`);
42
29
  }
43
- if (kept.length > 0) {
44
- // Adopting a directory: its own files win, always. Say which ones — and for the two that carry a
45
- // consequence, say what that consequence is, because keeping them silently leaves the agent broken
46
- // (package.json) or its machinery committable (.gitignore).
47
- console.error(` kept your existing: ${kept.join(", ")}`);
48
- if (kept.includes(join(rel, "package.json")) && complete) {
49
- const add = detectRuntime(agentDir, await readPackageJson(agentDir)).runtime === "bun" ? "bun add" : "npm install";
50
- console.error(`[fastagent] note: your package.json is untouched, so it does not list @fastagent-sh/fastagent — ` +
51
- `run \`${add} @fastagent-sh/fastagent\` there or the scaffolded tools/ cannot resolve`);
52
- }
53
- const keptSecretsIgnore = kept.includes(join(rel, SECRETS_DIRNAME, ".gitignore"));
54
- if (kept.includes(join(rel, ".gitignore"))) {
55
- // The "credentials are covered either way" reassurance holds only when `.secrets/.gitignore` is
56
- // OURS. On an adopted directory it may be the author's too, and then we know nothing about it —
57
- // `add <channel>` defends that file with a write; `init` must not claim more than it did.
58
- console.error(`[fastagent] note: your .gitignore is untouched — make sure it ignores node_modules, .state ` +
59
- `and .cache` +
60
- (keptSecretsIgnore ? `` : ` (.secrets/ carries its own .gitignore, so credentials are covered either way)`));
61
- }
62
- if (keptSecretsIgnore) {
63
- console.error(`[fastagent] note: your ${join(rel, SECRETS_DIRNAME, ".gitignore")} is untouched — verify it ignores ` +
64
- `everything but .env.example, because credentials land in that directory`);
65
- }
66
- }
67
- const willInstall = complete && opts.install && !kept.includes(join(rel, "package.json"));
68
30
  let installFailed = false;
69
- if (willInstall) {
70
- console.error(`[fastagent] installing dependencies (npm install${flat ? "" : ` in ${rel}`})…`);
31
+ if (opts.install) {
32
+ console.error(`[fastagent] installing dependencies (npm install in ${rel})…`);
71
33
  installFailed = (await npmInstall(agentDir)) !== 0;
72
34
  if (installFailed)
73
35
  console.error(`[fastagent] warn: npm install failed — run it manually in ${agentDir} before \`fastagent dev\``);
@@ -76,13 +38,12 @@ export async function runInit(dirArg, opts) {
76
38
  const cdTarget = displayPath(process.cwd(), dir);
77
39
  if (cdTarget)
78
40
  console.error(` cd ${cdTarget}`);
79
- if (complete && (!willInstall || installFailed)) {
80
- console.error(` ${flat ? "npm install" : `(cd ${rel} && npm install)`}`);
81
- }
41
+ if (!opts.install || installFailed)
42
+ console.error(` (cd ${rel} && npm install)`);
82
43
  console.error(` fastagent dev # serve locally and iterate`);
83
44
  console.error(` fastagent add skill <owner/repo/path> # vendor more skills from GitHub`);
84
45
  }
85
- /** Run `npm install` in `cwd` (inherit stdio). Returns the exit code. */
46
+ /** Run `npm install` in `cwd` (inherit stdio). */
86
47
  function npmInstall(cwd) {
87
48
  return new Promise((resolveCode) => {
88
49
  const child = spawn("npm", ["install"], { cwd, stdio: "inherit" });
@@ -1,6 +1,5 @@
1
1
  export interface InvokeOptions {
2
2
  model?: string;
3
- authPath?: string;
4
3
  /** false ⇔ `--no-input`. */
5
4
  input?: boolean;
6
5
  }
@@ -6,19 +6,17 @@ import { failStartup } from "../fail.js";
6
6
  import { enterAgentCommand, reportAuth } from "../shared.js";
7
7
  export async function runInvoke(message, dirArg, opts) {
8
8
  const placement = await enterAgentCommand(dirArg, opts);
9
- const { agent, modelSpec, authPath } = await createPiAgentFromDir(placement.workspace, {
9
+ const { agent, modelSpec, auth } = await createPiAgentFromDir(placement.agentDir, {
10
10
  model: opts.model,
11
- authPath: opts.authPath, // flag > FASTAGENT_AUTH_PATH > default — resolved by the opener (one owner)
12
11
  }).catch(failStartup);
13
- // BOTH directories, like dev/start: from the workspace, `placement.workspace` alone equals the dir you
14
- // typed, so it cannot tell you which agent actually ran.
12
+ // BOTH directories, like dev/start: which agent ran, and what it worked on.
15
13
  console.error(`[fastagent] invoke: ${placement.agentDir} (workspace ${placement.workspace}, ${modelSpec})`);
16
- await reportAuth(placement.agentDir, modelSpec, authPath);
14
+ await reportAuth(placement.agentDir, modelSpec, auth);
17
15
  // Fresh session per invoke (one-shot, no resume). runInvokeStream maps events→IO: reply→stdout,
18
16
  // tool/failure→stderr, exit 1 iff the turn failed (so CI can gate on it).
19
17
  const exitCode = await runInvokeStream(agent.invoke({ session: randomUUID() }, { text: message }), (text) => process.stdout.write(text), (line) => console.error(line));
20
18
  process.stdout.write("\n");
21
- // Always exit explicitly: the undici proxy agent's keep-alive sockets would otherwise hold the
22
- // event loop open after a successful one-shot turn.
19
+ // Always exit explicitly: the undici proxy agent's keep-alive sockets would otherwise hold the event loop open
20
+ // after a successful one-shot turn.
23
21
  process.exit(exitCode);
24
22
  }
@@ -1,5 +1,6 @@
1
1
  export interface LoginOptions {
2
- authPath?: string;
2
+ /** `-g`: store in the user-global file every agent on this machine falls back to. */
3
+ global?: boolean;
3
4
  /** false ⇔ `--no-input`. */
4
5
  input?: boolean;
5
6
  }
@@ -1,59 +1,54 @@
1
1
  /**
2
2
  * `fastagent login [provider]`: authenticate a model provider into the project-level auth file
3
- * (`<agentDir>/.secrets/auth.json`) by default, or `--auth-path`/`FASTAGENT_AUTH_PATH`. The
4
- * positional is the PROVIDER (not a dir), so the agent resolves from cwd — `cd` into your agent
5
- * before logging in. OUTSIDE an agent there is no project credential to write, so the target is the
6
- * user-global `~/.fastagent/.secrets/auth.json` (which is what running it from $HOME has always
7
- * meant) — announced on stderr BEFORE the flow, because that is a different credential than the one
8
- * `dev`/`start` in a real agent would read, and a silent scope switch is exactly the surprise this
9
- * command must not ship.
10
- *
11
- * fastagent writes no `.gitignore` here or anywhere else at runtime: `init` scaffolds the agent's one
12
- * ignore file (covering `.secrets/`) and the author owns it from then on. The credential store creates
13
- * its own directory, mode 0700.
3
+ * (`<agentDir>/.secrets/auth.json`) by default, or `FASTAGENT_AUTH_PATH`.
14
4
  */
15
5
  import { homedir } from "node:os";
16
6
  import { join } from "node:path";
17
- import { loadDotEnv } from "../../env.js";
7
+ import { enterAgentEnv } from "../../env.js";
18
8
  import { resolveAuthPath } from "../../engines/pi/config.js";
9
+ import { GLOBAL_AUTH_PATH } from "../../engines/pi/auth.js";
19
10
  import { GLOBAL_HOME_DIR, findAgentDir, placementDeadEnd } from "../../paths.js";
20
- import { LoginCancelled } from "../../engines/pi/login.js";
21
- import { installProxyFetch } from "../../proxy.js";
11
+ import { LoginCancelled, loginFlow } from "../../engines/pi/login.js";
22
12
  import { failStartup, placementOrExit } from "../fail.js";
23
- import { isInteractive, loginWithKeyCheck } from "../shared.js";
13
+ import { isInteractive, terminalLoginIO } from "../shared.js";
24
14
  export async function runLogin(provider, opts) {
25
15
  const cwd = process.cwd();
26
16
  const agentDir = findAgentDir(cwd);
27
- // "Outside an agent" must mean exactly that: a position with its own way out (inside an agent, or on a
28
- // directory holding several) is a dead end every other command refuses, and taking the global fallback
29
- // there would write a credential to a different scope under a message saying there is no agent here.
17
+ // "Outside an agent" must mean exactly that.
30
18
  if (!agentDir && placementDeadEnd(cwd))
31
19
  placementOrExit(cwd);
32
- // Outside any agent the target is the user-global machinery home — handed over explicitly, so the
33
- // path resolvers need no "is this $HOME?" special case to infer it.
20
+ // Outside any agent the target is the user-global machinery home — handed over explicitly, so the path resolvers
21
+ // need no "is this $HOME?" special case to infer it.
34
22
  const loginDir = agentDir ?? join(homedir(), GLOBAL_HOME_DIR);
35
- loadDotEnv(loginDir); // FASTAGENT_AUTH_PATH / a proxy (HTTPS_PROXY) may be configured in the project .env
36
- installProxyFetch(); // the OAuth token exchange must go through HTTPS_PROXY (region-locked providers)
37
- const authPath = resolveAuthPath(loginDir, opts.authPath); // flag > FASTAGENT_AUTH_PATH > default — the one owner
38
- // Announce when the FALLBACK is what decided the target: outside an agent with no explicit path,
39
- // the credential lands somewhere no agent will read. An explicit `--auth-path`/FASTAGENT_AUTH_PATH
40
- // is the user naming the file, so there is no surprise to announce (and announcing a path this run
41
- // does not write would be worse than saying nothing). The resolved path goes in the message.
42
- if (!agentDir && !opts.authPath && !process.env.FASTAGENT_AUTH_PATH) {
43
- console.error(`[fastagent] no agent here (no fastagent.config.*, here or one level inside) — ` +
44
- `logging in GLOBALLY (${authPath}). An agent reads its own .secrets/auth.json: \`cd\` into one ` +
45
- `first, or point this run at it with --auth-path.`);
23
+ // FASTAGENT_AUTH_PATH and a proxy may both be configured in the project .env, and the OAuth token exchange must go
24
+ // through that proxy (region-locked providers).
25
+ enterAgentEnv(loginDir);
26
+ // `-g` names the global file outright. Otherwise: FASTAGENT_AUTH_PATH > default — the one owner. The
27
+ // store built here is deliberately UNLAYERED: reading falls back to the global file, but writing must land
28
+ // exactly where the operator said, or a second `login` for a provider they already have globally would silently
29
+ // rewrite the global credential instead of creating the project-level override they asked for.
30
+ const authPath = opts.global ? GLOBAL_AUTH_PATH : resolveAuthPath(loginDir);
31
+ // Announce when the FALLBACK is what decided the target: outside an agent with no explicit path, the credential
32
+ // lands somewhere no agent will read.
33
+ if (!agentDir && !opts.global && !process.env.FASTAGENT_AUTH_PATH) {
34
+ console.error(`[fastagent] no agent here (no fastagent.config.ts, here or one level inside) — ` +
35
+ `logging in GLOBALLY (${authPath}). Every agent on this machine reads this file for providers its own ` +
36
+ `.secrets/auth.json does not have, so this is usually what you want; \`cd\` into an agent to give that ` +
37
+ `one its own account instead.`);
46
38
  }
47
- // login is inherently interactive — loginFlow renders provider/method menus and opens a browser (or
48
- // prompts for a key). In a non-TTY (a pipe, CI, a coding-agent shell) the menu can't receive keystrokes
49
- // and would hang; --no-input asks for the same posture explicitly. Fail fast with the reason instead
50
- // of stalling on an unanswerable prompt.
39
+ // login is inherently interactive — loginFlow renders provider/method menus and opens a browser (or prompts for a
40
+ // key).
51
41
  if (opts.input === false || !isInteractive()) {
52
42
  failStartup(new Error(`login is interactive (it shows a menu and opens a browser) — run it in a terminal, not a pipe/CI`));
53
43
  }
54
- // loginWithKeyCheck: an entered API key is verified with one minimal request; a rejected key (401)
55
- // re-prompts in place, so a returned result is always a stored-and-not-definitively-bad credential.
56
- const result = await loginWithKeyCheck(provider, authPath).catch((error) => {
44
+ // An entered API key is verified with one minimal request (login.ts); a rejected one is asked for again.
45
+ // Inside an agent, the key is checked on that agent's registry: its models.json may point a provider at a gateway.
46
+ // Not with `-g`: the global file serves every agent on the machine, so no one agent's routing may judge its key.
47
+ const result = await loginFlow(terminalLoginIO(), {
48
+ authPath,
49
+ ...(provider ? { provider } : {}),
50
+ ...(agentDir && !opts.global ? { agentDir } : {}),
51
+ }).catch((error) => {
57
52
  if (error instanceof LoginCancelled) {
58
53
  // A decision, not a failure — neutral wording; non-zero exit because no credential was stored.
59
54
  console.error(`[fastagent] login cancelled`);
@@ -3,15 +3,14 @@ import { basename, resolve } from "node:path";
3
3
  import { agentcoreName } from "../../deploy/agentcore/plan.js";
4
4
  import { tailAgentcoreLogs } from "../../deploy/agentcore/logs.js";
5
5
  import { spawnRunner } from "../../deploy/runner.js";
6
- import { loadDotEnv } from "../../env.js";
6
+ import { enterAgentEnv } from "../../env.js";
7
7
  import { failStartup, failUsage, placementOrExit } from "../fail.js";
8
8
  export async function runLogs(host, dirArg, opts) {
9
- // The host argument is DISPATCHED here, as in runDeploy — the parser's `choices` happens to have a
10
- // single member today, and a future second host must land on its own reader, not silently on this one.
9
+ // The host argument is DISPATCHED here, as in runDeploy.
11
10
  if (host !== "agentcore")
12
11
  failUsage(`logs: unsupported host "${host}" — only agentcore has remote logs`);
13
12
  const placement = placementOrExit(resolve(dirArg));
14
- loadDotEnv(placement.agentDir); // AWS_PROFILE/region/proxy may be definition-local, as on deploy
13
+ enterAgentEnv(placement.agentDir); // AWS_PROFILE/region/proxy may be definition-local, as on deploy
15
14
  const source = opts.source ?? "runtime";
16
15
  if (source !== "runtime" && source !== "forwarder") {
17
16
  failUsage(`logs: --source must be "runtime" or "forwarder"`);
@@ -1,8 +1,4 @@
1
- /**
2
- * `fastagent models [search]`: print every registered "provider/modelId"; `[search]` filters by
3
- * substring. This module pulls the pi model catalog (heavy) — it is lazy-imported by the spec, so
4
- * only an actual `models` invocation pays for it.
5
- */
1
+ /** `fastagent models [search]`: print every registered "provider/modelId"; `[search]` filters by substring. */
6
2
  import { formatModelsCommand } from "../models-view.js";
7
3
  import { listModels } from "../../engines/pi/config.js";
8
4
  import { createPiModels } from "../../engines/pi/models.js";
@@ -0,0 +1,6 @@
1
+ export interface RoutineRunOptions {
2
+ model?: string;
3
+ /** false ⇔ `--no-input`. */
4
+ input?: boolean;
5
+ }
6
+ export declare function runRoutine(name: string, dirArg: string, opts: RoutineRunOptions): Promise<void>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `fastagent routine run <name> [dir]`: run ONE routine's turn immediately — the authoring loop for routines (like
3
+ * `invoke` is for a prompt). Cron or not: a routine that declares none has no other local entry.
4
+ */
5
+ import { join } from "node:path";
6
+ import { displayPath } from "../../paths.js";
7
+ import { reportModuleLoadFailures } from "../../loader.js";
8
+ import { createPiAgentFromDir } from "../../engines/pi/open.js";
9
+ import { runInvokeStream } from "../invoke-stream.js";
10
+ import { loadRoutines } from "../../schedule/discover.js";
11
+ import { routineSession } from "../../schedule/routine.js";
12
+ import { failStartup, gateSecretsOrExit } from "../fail.js";
13
+ import { enterAgentCommand, reportAuth } from "../shared.js";
14
+ export async function runRoutine(name, dirArg, opts) {
15
+ const placement = await enterAgentCommand(dirArg, opts);
16
+ // Routines are agent surface — discovered where dev/start/`routine list` do (the agent dir), so this command
17
+ // sees the same set the clock serves.
18
+ const { routines, secrets, failures } = await loadRoutines(placement.agentDir).catch(failStartup);
19
+ // Reported BEFORE the name is looked up: a routine file that failed to import is missing from
20
+ // `routines`, so "unknown routine" is the case where the author most needs to hear about it.
21
+ reportModuleLoadFailures(failures);
22
+ const routine = routines.find((r) => r.name === name);
23
+ if (!routine) {
24
+ // Name the discovery path: a schedule misplaced in the workspace (outside the agent dir) should read as "wrong
25
+ // place", not "broken file".
26
+ failStartup(new Error(`unknown routine "${name}" (looked in ${displayPath(process.cwd(), join(placement.agentDir, "routines")) ?? "routines"}). ` +
27
+ `available: ${routines.map((r) => r.name).join(", ") || "(none)"}`));
28
+ }
29
+ // This RUNS the routine, so it takes the serving path's guarantee: a prompt built from an
30
+ // unset declared value is the degraded turn this feature exists to prevent (`Post the digest to `
31
+ // — sent and executed), and the loader resolved it into a string before anything could notice.
32
+ // THIS schedule only (`owner`): running one routine must not fail because a SIBLING ROUTINE needs a
33
+ // credential this machine has no reason to hold. The agent assembled below is a different question
34
+ // and gates every mounted tool — a run's turn can call any of them — so this command is not free of a
35
+ // tool's declaration, only of another routine's. The failures go to the gate too, even though they
36
+ // were printed above: its guarantee must not depend on this call site remembering (a repeated line
37
+ // on the refusal path is the cheaper failure).
38
+ gateSecretsOrExit({ declared: secrets, failures, owner: name });
39
+ const { agent, modelSpec, auth } = await createPiAgentFromDir(placement.agentDir, {
40
+ model: opts.model,
41
+ }).catch(failStartup);
42
+ console.error(`[fastagent] routine run: ${name} (${modelSpec})`);
43
+ await reportAuth(placement.agentDir, modelSpec, auth);
44
+ const exitCode = await runInvokeStream(agent.invoke({ session: routineSession(name) }, { text: routine.prompt }), (text) => process.stdout.write(text), (line) => console.error(line));
45
+ process.stdout.write("\n");
46
+ process.exit(exitCode);
47
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `fastagent routine history <name> [dir]`: print this routine's fired slots — when each fired, how it ended, how
3
+ * long it took.
4
+ *
5
+ * The history IS the claims (`schedule/claims/<name>/`), so it is bounded by construction and carries no turn text:
6
+ * what the run SAID is in its session (`routine:<name>`), stored once, like any other turn's, under the state
7
+ * root's `sessions/` — which is where this command points rather than copying any of it here.
8
+ *
9
+ * This command does NOT try to say which turn belongs to which fire. Nothing links them: a routine's fires share
10
+ * ONE continuing conversation, so the session id is the same for all of them, and the turn-level identifier would
11
+ * have to cross the engine-neutral contract (no `AgentEvent` carries an entry id) or cost the shared conversation.
12
+ * A claim's timestamp against a time-ordered journal is what an operator reads anyway — matching them HERE only
13
+ * moves a human's judgement into a heuristic that cannot be right about a session other writers also append to.
14
+ */
15
+ export declare function runRoutineHistory(name: string, dirArg: string, json: boolean): void;
16
+ /**
17
+ * `fastagent routine list [dir]`: every declared routine, and when (or whether) a clock fires it.
18
+ *
19
+ * IT ALSO READS the agent's pending wake-ups. They are a different owner (the state, not the definition) but the
20
+ * same question when an operator is asking it: "what is going to wake this agent up?". Deleting the command that
21
+ * CANCELLED one was right — `unwake({ id })` covers it, and a serve that can receive the alarm can also be spoken
22
+ * to — but that argument is about WRITING. Without a read, the only way to learn why an agent wakes at 3am is to
23
+ * open `<stateRoot>/schedule/wakeups.json` by hand.
24
+ */
25
+ export declare function runRoutineList(dirArg: string, json: boolean): Promise<void>;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * `fastagent routine history|list` — the routines this DEFINITION declares.
3
+ *
4
+ * The agent's own pending wake-ups are not here and have no command: they live in the state, not the definition,
5
+ * and `unwake({ id })` is what cancels one (schedule/wakeups.ts says why).
6
+ */
7
+ import { resolve } from "node:path";
8
+ import { enterAgentEnv } from "../../env.js";
9
+ import { resolveSessionsDir, resolveStateRoot } from "../../paths.js";
10
+ import { reportModuleLoadFailures } from "../../loader.js";
11
+ import { nextRun } from "../../schedule/cron.js";
12
+ import { loadRoutines } from "../../schedule/discover.js";
13
+ import { routineSession } from "../../schedule/routine.js";
14
+ import { isSafeScheduleName, readFires } from "../../schedule/state.js";
15
+ import { listWakeups } from "../../schedule/wakeups.js";
16
+ import { failStartup, placementOrExit } from "../fail.js";
17
+ /**
18
+ * `fastagent routine history <name> [dir]`: print this routine's fired slots — when each fired, how it ended, how
19
+ * long it took.
20
+ *
21
+ * The history IS the claims (`schedule/claims/<name>/`), so it is bounded by construction and carries no turn text:
22
+ * what the run SAID is in its session (`routine:<name>`), stored once, like any other turn's, under the state
23
+ * root's `sessions/` — which is where this command points rather than copying any of it here.
24
+ *
25
+ * This command does NOT try to say which turn belongs to which fire. Nothing links them: a routine's fires share
26
+ * ONE continuing conversation, so the session id is the same for all of them, and the turn-level identifier would
27
+ * have to cross the engine-neutral contract (no `AgentEvent` carries an entry id) or cost the shared conversation.
28
+ * A claim's timestamp against a time-ordered journal is what an operator reads anyway — matching them HERE only
29
+ * moves a human's judgement into a heuristic that cannot be right about a session other writers also append to.
30
+ */
31
+ export function runRoutineHistory(name, dirArg, json) {
32
+ const { agentDir: target } = placementOrExit(resolve(dirArg));
33
+ enterAgentEnv(target); // FASTAGENT_STATE_DIR may live in .env — read the SAME state root the scheduler wrote
34
+ const stateRoot = resolveStateRoot(target);
35
+ // A name that cannot be a routine is a MISTYPED ARGUMENT, and it must not look like an empty history: exit 1 the
36
+ // way every other user-input refusal does, so `--json` printing nothing is never read as "the command succeeded".
37
+ if (!isSafeScheduleName(name)) {
38
+ failStartup(new Error(`"${name}" cannot be a routine name (no path separators, "." or "..")`));
39
+ }
40
+ // `readFires` throws a raw fs error on unreadable state, because its other caller is the serving boot, which must
41
+ // fail rather than arm a schedule it cannot read. This caller is a read-only CLI command: the same fact is an
42
+ // operator's to fix, so it exits the way every other refusal in this file does instead of printing a Node stack.
43
+ let fires;
44
+ try {
45
+ fires = readFires(stateRoot, name);
46
+ }
47
+ catch (e) {
48
+ failStartup(new Error(`the fired-slot claims for "${name}" are unreadable (state: ${stateRoot}): ${String(e)}`));
49
+ }
50
+ if (json) {
51
+ console.log(JSON.stringify(fires, null, 2));
52
+ return;
53
+ }
54
+ if (fires.length === 0) {
55
+ // No special case for "wake": it is an ordinary schedule name now (`discover.ts` stopped reserving it).
56
+ console.error(`no recorded fires for "${name}" (state: ${stateRoot})`);
57
+ return;
58
+ }
59
+ // The question is "did LAST NIGHT's run fail?", so text mode tails the most recent fires; --json above is the
60
+ // whole retained window.
61
+ const TAIL = 20;
62
+ const shown = fires.slice(-TAIL);
63
+ for (const f of shown) {
64
+ // An unreported fire has no duration to print, and `0ms` would be a value a fast turn really produces — the
65
+ // column stays empty rather than claiming the turn took no time.
66
+ const took = f.ms === undefined ? "" : `${f.ms}ms`;
67
+ console.log(`${f.firedAt} ${(f.outcome ?? "unreported").padEnd(11)} ${took.padStart(8)}`);
68
+ }
69
+ if (fires.length > shown.length) {
70
+ console.error(`(the last ${shown.length} of ${fires.length} fires — --json for all)`);
71
+ }
72
+ // The other half of the answer, and where it lives: these rows say a fire happened, not what it produced. The
73
+ // session id comes from the ONE place that spells it and the directory from the ONE place that resolves it. The
74
+ // record is one level further down and under a name pi accepts (`piSessionId` escapes the `:`), so the pointer
75
+ // says how to recognise the file rather than claiming a path the reader can paste.
76
+ console.error(`(what these runs said: session ${routineSession(name)} — a JSON-lines journal under ${resolveSessionsDir(target)}, ` +
77
+ `in the file whose name carries "${name}")`);
78
+ }
79
+ /**
80
+ * `fastagent routine list [dir]`: every declared routine, and when (or whether) a clock fires it.
81
+ *
82
+ * IT ALSO READS the agent's pending wake-ups. They are a different owner (the state, not the definition) but the
83
+ * same question when an operator is asking it: "what is going to wake this agent up?". Deleting the command that
84
+ * CANCELLED one was right — `unwake({ id })` covers it, and a serve that can receive the alarm can also be spoken
85
+ * to — but that argument is about WRITING. Without a read, the only way to learn why an agent wakes at 3am is to
86
+ * open `<stateRoot>/schedule/wakeups.json` by hand.
87
+ */
88
+ export async function runRoutineList(dirArg, json) {
89
+ const { agentDir: target } = placementOrExit(resolve(dirArg));
90
+ enterAgentEnv(target);
91
+ const { routines, failures } = await loadRoutines(target).catch(failStartup);
92
+ reportModuleLoadFailures(failures);
93
+ const wakeups = listWakeups(resolveStateRoot(target));
94
+ if (json) {
95
+ console.log(JSON.stringify({
96
+ routines: routines.map((r) => ({
97
+ ...r,
98
+ next: r.cron === undefined ? null : (nextRun(r.cron, r.tz, new Date())?.toISOString() ?? null),
99
+ })),
100
+ wakeups,
101
+ }, null, 2));
102
+ return;
103
+ }
104
+ if (routines.length === 0 && wakeups.length === 0) {
105
+ console.error(`nothing declared or pending — no routines/ files, no wake-ups (agent: ${target})`);
106
+ return;
107
+ }
108
+ for (const r of routines) {
109
+ // A routine with no cron is not broken and not idle: it is reached by NAME (`routine run`, `POST /run`), and
110
+ // saying "on demand" is the difference between that and a cron that will never fire again.
111
+ const when = r.cron === undefined
112
+ ? "on demand".padEnd(26)
113
+ : `${(nextRun(r.cron, r.tz, new Date())?.toISOString() ?? "(never)").padEnd(26)}cron ${r.cron}${r.tz ? ` ${r.tz}` : ""}`;
114
+ console.log(`${r.name.padEnd(20)} ${when}`);
115
+ }
116
+ // Labelled, because these two are NOT the same kind of thing: a routine is the author's, a wake-up is the
117
+ // agent's own and is cancelled with `unwake` rather than a command here.
118
+ for (const w of wakeups) {
119
+ const kind = w.cron ? `cron ${w.cron}${w.tz ? ` ${w.tz}` : ""}` : "one-shot";
120
+ console.log(`wake ${w.id} ${w.fireAt} ${kind} session=${w.session} ${w.prompt.slice(0, 60)}`);
121
+ }
122
+ }
@@ -1,11 +1,39 @@
1
+ import { type AgentService } from "../../service.ts";
1
2
  export interface StartOptions {
2
3
  port?: string;
3
4
  bind?: string;
4
5
  model?: string;
5
- sessionsDir?: string;
6
- authPath?: string;
7
6
  tunnel?: boolean;
8
- /** false ⇔ `--no-input`. */
7
+ /** false ⇔ `--no-invoke`: withhold `POST /invoke` for THIS run (`http.invoke` is the persistent form). */
8
+ invoke?: boolean;
9
9
  input?: boolean;
10
10
  }
11
+ type StartedService = AgentService & {
12
+ stateRoot: string;
13
+ port: number;
14
+ };
11
15
  export declare function runStart(dirArg: string, opts: StartOptions): Promise<void>;
16
+ /** What the storage stage hands the opening stage. */
17
+ export interface PreparedWorkspace {
18
+ /** The workspace to open — the volume's `base/` when a release manifest selected one. */
19
+ dir: string;
20
+ /** Set when this process took a deployed workspace. */
21
+ deployed?: {
22
+ root: string;
23
+ agent: string;
24
+ };
25
+ }
26
+ /**
27
+ * Stage one — TAKE the workspace: verify the storage is mounted, apply the release, and point the machinery at the
28
+ * volume.
29
+ */
30
+ export declare function prepareStartWorkspace(dirArg: string): Promise<PreparedWorkspace>;
31
+ /**
32
+ * Stage two — RUN in the prepared workspace: install the agent's dependencies, then open through the workspace's own
33
+ * FastAgent install.
34
+ */
35
+ export declare function openPreparedWorkspace(prepared: PreparedWorkspace, opts: StartOptions): Promise<StartedService>;
36
+ export declare function openStartService(dirArg: string, opts: StartOptions): Promise<StartedService>;
37
+ /** Internal entry loaded from the active workspace's installed package after storage initialization. */
38
+ export declare function openPreparedStartService(dirArg: string, opts: StartOptions): Promise<StartedService>;
39
+ export {};