@fastagent-sh/fastagent 0.24.4 → 0.26.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 (487) hide show
  1. package/README.md +54 -82
  2. package/dist/agent-api.d.ts +460 -0
  3. package/dist/agent-api.js +209 -0
  4. package/dist/answer-text.d.ts +8 -0
  5. package/dist/answer-text.js +18 -0
  6. package/dist/atomic-write.d.ts +2 -2
  7. package/dist/atomic-write.js +2 -2
  8. package/dist/authoring.d.ts +81 -0
  9. package/dist/authoring.js +245 -0
  10. package/dist/channel.d.ts +29 -15
  11. package/dist/channel.js +4 -1
  12. package/dist/channels/agentcore-protocol.d.ts +36 -50
  13. package/dist/channels/agentcore-protocol.js +3 -3
  14. package/dist/channels/agentcore-service.d.ts +8 -18
  15. package/dist/channels/agentcore-service.js +108 -127
  16. package/dist/channels/agentcore.d.ts +8 -24
  17. package/dist/channels/agentcore.js +32 -65
  18. package/dist/channels/body.d.ts +0 -16
  19. package/dist/channels/body.js +0 -25
  20. package/dist/channels/busy.js +6 -1
  21. package/dist/channels/context.d.ts +4 -0
  22. package/dist/channels/context.js +35 -0
  23. package/dist/channels/discover.d.ts +18 -20
  24. package/dist/channels/discover.js +41 -99
  25. package/dist/channels/feishu/feishu-api.d.ts +54 -4
  26. package/dist/channels/feishu/feishu-api.js +192 -149
  27. package/dist/channels/feishu/feishu.d.ts +3 -0
  28. package/dist/channels/feishu/feishu.js +117 -106
  29. package/dist/channels/feishu/history.d.ts +55 -0
  30. package/dist/channels/feishu/history.js +199 -0
  31. package/dist/channels/feishu/invoke-turn.d.ts +13 -11
  32. package/dist/channels/feishu/invoke-turn.js +9 -10
  33. package/dist/channels/feishu/model.d.ts +2 -1
  34. package/dist/channels/feishu/normalize.js +100 -0
  35. package/dist/channels/feishu/preview.d.ts +2 -2
  36. package/dist/channels/feishu/register-webhook.d.ts +1 -0
  37. package/dist/channels/feishu/register-webhook.js +3 -2
  38. package/dist/channels/feishu/scaffold/channel.ts +65 -46
  39. package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
  40. package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
  41. package/dist/channels/feishu/setup-mode.d.ts +45 -25
  42. package/dist/channels/feishu/setup-mode.js +74 -23
  43. package/dist/channels/feishu/shared-api.d.ts +24 -6
  44. package/dist/channels/feishu/shared-api.js +30 -24
  45. package/dist/channels/kit/context-buffer.d.ts +11 -2
  46. package/dist/channels/kit/context-buffer.js +7 -4
  47. package/dist/channels/kit/delivery.d.ts +19 -3
  48. package/dist/channels/kit/delivery.js +74 -15
  49. package/dist/channels/kit/event-stream.js +2 -1
  50. package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
  51. package/dist/channels/kit/invoke-turn-kit.js +62 -71
  52. package/dist/channels/kit/place-history.d.ts +107 -0
  53. package/dist/channels/kit/place-history.js +182 -0
  54. package/dist/channels/kit/preview-kit.d.ts +9 -5
  55. package/dist/channels/kit/preview-kit.js +36 -15
  56. package/dist/channels/kit/room-threads.d.ts +46 -0
  57. package/dist/channels/kit/room-threads.js +81 -0
  58. package/dist/channels/kit/seen.js +1 -1
  59. package/dist/channels/kit/stop-command.d.ts +6 -3
  60. package/dist/channels/kit/stop-command.js +20 -18
  61. package/dist/channels/kit/thread-fork.d.ts +17 -0
  62. package/dist/channels/kit/thread-fork.js +43 -0
  63. package/dist/channels/kit/thread-participants.js +1 -1
  64. package/dist/channels/kit/transport.d.ts +5 -0
  65. package/dist/channels/kit/transport.js +5 -0
  66. package/dist/channels/kit/turn-runner.d.ts +10 -9
  67. package/dist/channels/kit/turn-runner.js +17 -19
  68. package/dist/channels/kit/turn-store.d.ts +5 -4
  69. package/dist/channels/kit/turn-store.js +5 -5
  70. package/dist/channels/lark/lark.d.ts +3 -0
  71. package/dist/channels/lark/lark.js +5 -0
  72. package/dist/channels/lark/onboard.d.ts +5 -3
  73. package/dist/channels/lark/onboard.js +14 -14
  74. package/dist/channels/lark/scaffold/channel.ts +67 -44
  75. package/dist/channels/lark/scaffold/lark-send.ts +38 -33
  76. package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
  77. package/dist/channels/respond.d.ts +0 -4
  78. package/dist/channels/respond.js +1 -1
  79. package/dist/channels/router.d.ts +18 -0
  80. package/dist/channels/router.js +68 -0
  81. package/dist/channels/serve.d.ts +5 -70
  82. package/dist/channels/serve.js +105 -287
  83. package/dist/channels/slack/history.d.ts +42 -0
  84. package/dist/channels/slack/history.js +282 -0
  85. package/dist/channels/slack/invoke-turn.d.ts +12 -5
  86. package/dist/channels/slack/invoke-turn.js +7 -5
  87. package/dist/channels/slack/manifest.d.ts +1 -4
  88. package/dist/channels/slack/manifest.js +22 -10
  89. package/dist/channels/slack/model.d.ts +2 -1
  90. package/dist/channels/slack/onboard.d.ts +0 -2
  91. package/dist/channels/slack/onboard.js +2 -4
  92. package/dist/channels/slack/onboarding-state.d.ts +0 -2
  93. package/dist/channels/slack/onboarding-state.js +0 -1
  94. package/dist/channels/slack/parse.d.ts +0 -5
  95. package/dist/channels/slack/parse.js +1 -16
  96. package/dist/channels/slack/preview.d.ts +2 -3
  97. package/dist/channels/slack/preview.js +78 -52
  98. package/dist/channels/slack/register-webhook.d.ts +1 -0
  99. package/dist/channels/slack/register-webhook.js +6 -5
  100. package/dist/channels/slack/scaffold/channel.ts +23 -24
  101. package/dist/channels/slack/scaffold/slack-send.ts +50 -43
  102. package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
  103. package/dist/channels/slack/setup-server.d.ts +19 -0
  104. package/dist/channels/slack/setup-server.js +113 -11
  105. package/dist/channels/slack/shared-api.d.ts +15 -4
  106. package/dist/channels/slack/shared-api.js +19 -14
  107. package/dist/channels/slack/slack-api.d.ts +61 -2
  108. package/dist/channels/slack/slack-api.js +28 -5
  109. package/dist/channels/slack/slack.js +97 -52
  110. package/dist/channels/slack/welcomed.js +1 -1
  111. package/dist/channels/telegram/context-buffer.d.ts +6 -1
  112. package/dist/channels/telegram/context-buffer.js +19 -0
  113. package/dist/channels/telegram/invoke-turn.d.ts +8 -4
  114. package/dist/channels/telegram/invoke-turn.js +7 -7
  115. package/dist/channels/telegram/parse.d.ts +7 -2
  116. package/dist/channels/telegram/parse.js +7 -0
  117. package/dist/channels/telegram/preview.d.ts +2 -2
  118. package/dist/channels/telegram/register-webhook.d.ts +1 -0
  119. package/dist/channels/telegram/register-webhook.js +3 -2
  120. package/dist/channels/telegram/scaffold/channel.ts +27 -28
  121. package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
  122. package/dist/channels/telegram/shared-api.d.ts +34 -0
  123. package/dist/channels/telegram/shared-api.js +79 -0
  124. package/dist/channels/telegram/telegram-api.d.ts +15 -5
  125. package/dist/channels/telegram/telegram-api.js +10 -6
  126. package/dist/channels/telegram/telegram.js +27 -21
  127. package/dist/channels/telegram/turn-store.js +1 -1
  128. package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
  129. package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
  130. package/dist/cli/add-feishu.d.ts +42 -12
  131. package/dist/cli/add-feishu.js +177 -124
  132. package/dist/cli/add-slack.d.ts +8 -3
  133. package/dist/cli/add-slack.js +116 -64
  134. package/dist/cli/auth-view.d.ts +10 -3
  135. package/dist/cli/auth-view.js +8 -6
  136. package/dist/cli/box-login.d.ts +18 -7
  137. package/dist/cli/box-login.js +46 -12
  138. package/dist/cli/commands/add.d.ts +15 -4
  139. package/dist/cli/commands/add.js +253 -143
  140. package/dist/cli/commands/auth.d.ts +6 -0
  141. package/dist/cli/commands/auth.js +50 -0
  142. package/dist/cli/commands/content.d.ts +6 -0
  143. package/dist/cli/commands/content.js +45 -0
  144. package/dist/cli/commands/deploy/agentcore.js +50 -77
  145. package/dist/cli/commands/deploy/docker.js +38 -39
  146. package/dist/cli/commands/deploy/fly.js +21 -18
  147. package/dist/cli/commands/deploy/host-login.d.ts +26 -0
  148. package/dist/cli/commands/deploy/host-login.js +90 -0
  149. package/dist/cli/commands/deploy/railway.js +21 -26
  150. package/dist/cli/commands/deploy/shared.d.ts +45 -17
  151. package/dist/cli/commands/deploy/shared.js +34 -15
  152. package/dist/cli/commands/deploy.d.ts +0 -4
  153. package/dist/cli/commands/deploy.js +237 -50
  154. package/dist/cli/commands/destroy.js +8 -6
  155. package/dist/cli/commands/dev.d.ts +0 -2
  156. package/dist/cli/commands/dev.js +20 -19
  157. package/dist/cli/commands/env.d.ts +12 -0
  158. package/dist/cli/commands/env.js +191 -0
  159. package/dist/cli/commands/info.js +95 -156
  160. package/dist/cli/commands/init.d.ts +2 -2
  161. package/dist/cli/commands/init.js +35 -33
  162. package/dist/cli/commands/invoke.d.ts +6 -0
  163. package/dist/cli/commands/invoke.js +132 -16
  164. package/dist/cli/commands/login.d.ts +31 -2
  165. package/dist/cli/commands/login.js +416 -65
  166. package/dist/cli/commands/logout.d.ts +4 -0
  167. package/dist/cli/commands/logout.js +53 -0
  168. package/dist/cli/commands/logs.js +8 -6
  169. package/dist/cli/commands/models.d.ts +2 -1
  170. package/dist/cli/commands/models.js +39 -37
  171. package/dist/cli/commands/schedules.d.ts +21 -0
  172. package/dist/cli/commands/schedules.js +143 -0
  173. package/dist/cli/commands/start.d.ts +11 -10
  174. package/dist/cli/commands/start.js +35 -62
  175. package/dist/cli/content-view.d.ts +9 -0
  176. package/dist/cli/content-view.js +26 -0
  177. package/dist/cli/fail.d.ts +9 -12
  178. package/dist/cli/fail.js +17 -14
  179. package/dist/cli/invoke-stream.d.ts +12 -3
  180. package/dist/cli/invoke-stream.js +85 -23
  181. package/dist/cli/kernel.d.ts +9 -2
  182. package/dist/cli/kernel.js +15 -6
  183. package/dist/cli/login-relay.d.ts +6 -4
  184. package/dist/cli/login-relay.js +19 -7
  185. package/dist/cli/models-view.d.ts +5 -1
  186. package/dist/cli/models-view.js +1 -1
  187. package/dist/cli/person.d.ts +10 -0
  188. package/dist/cli/person.js +23 -0
  189. package/dist/cli/program.js +421 -203
  190. package/dist/cli/serve.d.ts +13 -63
  191. package/dist/cli/serve.js +31 -121
  192. package/dist/cli/shared.d.ts +21 -48
  193. package/dist/cli/shared.js +70 -89
  194. package/dist/cli/ticket.d.ts +136 -0
  195. package/dist/cli/ticket.js +476 -0
  196. package/dist/client.d.ts +7 -0
  197. package/dist/client.js +2 -0
  198. package/dist/content/declare.d.ts +70 -0
  199. package/dist/content/declare.js +163 -0
  200. package/dist/content/file.d.ts +32 -0
  201. package/dist/content/file.js +119 -0
  202. package/dist/content/git.d.ts +41 -0
  203. package/dist/content/git.js +220 -0
  204. package/dist/content/mount.d.ts +2 -0
  205. package/dist/content/mount.js +15 -0
  206. package/dist/content/resolve.d.ts +52 -0
  207. package/dist/content/resolve.js +151 -0
  208. package/dist/content/source.d.ts +27 -0
  209. package/dist/content/source.js +53 -0
  210. package/dist/conversation-sessions.d.ts +26 -0
  211. package/dist/conversation-sessions.js +99 -0
  212. package/dist/declared-secrets.d.ts +19 -37
  213. package/dist/declared-secrets.js +16 -58
  214. package/dist/deploy/agentcore/destroy.d.ts +3 -3
  215. package/dist/deploy/agentcore/destroy.js +46 -28
  216. package/dist/deploy/agentcore/forwarder.js +105 -22
  217. package/dist/deploy/agentcore/logs.js +1 -1
  218. package/dist/deploy/agentcore/plan.d.ts +10 -24
  219. package/dist/deploy/agentcore/plan.js +31 -135
  220. package/dist/deploy/agentcore/run.d.ts +1 -1
  221. package/dist/deploy/agentcore/run.js +8 -5
  222. package/dist/deploy/agentcore/shell.d.ts +1 -1
  223. package/dist/deploy/agentcore/shell.js +5 -5
  224. package/dist/deploy/api.d.ts +34 -0
  225. package/dist/deploy/api.js +50 -0
  226. package/dist/deploy/box-shell.d.ts +6 -0
  227. package/dist/deploy/box-shell.js +7 -0
  228. package/dist/deploy/build-context.d.ts +7 -5
  229. package/dist/deploy/build-context.js +41 -35
  230. package/dist/deploy/channel-ingress.d.ts +6 -1
  231. package/dist/deploy/channel-ingress.js +18 -5
  232. package/dist/deploy/container.d.ts +22 -9
  233. package/dist/deploy/container.js +96 -55
  234. package/dist/deploy/docker/plan.d.ts +3 -2
  235. package/dist/deploy/docker/plan.js +13 -14
  236. package/dist/deploy/docker/run.d.ts +2 -2
  237. package/dist/deploy/docker/run.js +8 -4
  238. package/dist/deploy/fly/plan.d.ts +1 -1
  239. package/dist/deploy/fly/plan.js +12 -21
  240. package/dist/deploy/fly/run.d.ts +10 -6
  241. package/dist/deploy/fly/run.js +11 -6
  242. package/dist/deploy/hosts.d.ts +2 -1
  243. package/dist/deploy/hosts.js +2 -1
  244. package/dist/deploy/preflight.d.ts +21 -23
  245. package/dist/deploy/preflight.js +223 -202
  246. package/dist/deploy/railway/plan.d.ts +8 -6
  247. package/dist/deploy/railway/plan.js +15 -58
  248. package/dist/deploy/railway/run.d.ts +8 -3
  249. package/dist/deploy/railway/run.js +12 -8
  250. package/dist/deploy/residency.d.ts +5 -11
  251. package/dist/deploy/residency.js +0 -6
  252. package/dist/deploy/runner.d.ts +1 -1
  253. package/dist/deploy/runner.js +1 -1
  254. package/dist/deploy/secrets.d.ts +16 -2
  255. package/dist/deploy/secrets.js +33 -6
  256. package/dist/deploy/workspace.d.ts +11 -6
  257. package/dist/deploy/workspace.js +55 -38
  258. package/dist/dev-supervisor.d.ts +11 -2
  259. package/dist/dev-supervisor.js +52 -27
  260. package/dist/effect-port.d.ts +4 -4
  261. package/dist/effect-port.js +4 -4
  262. package/dist/env.d.ts +0 -13
  263. package/dist/env.js +1 -44
  264. package/dist/environment/apt.d.ts +4 -0
  265. package/dist/environment/apt.js +32 -0
  266. package/dist/environment/declare.d.ts +59 -0
  267. package/dist/environment/declare.js +210 -0
  268. package/dist/environment/mise.d.ts +51 -0
  269. package/dist/environment/mise.js +272 -0
  270. package/dist/feishu.d.ts +2 -2
  271. package/dist/feishu.js +2 -2
  272. package/dist/git.d.ts +13 -0
  273. package/dist/git.js +55 -0
  274. package/dist/harnesses/models/catalog.d.ts +20 -0
  275. package/dist/harnesses/models/catalog.js +254 -0
  276. package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
  277. package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
  278. package/dist/harnesses/models/environment.d.ts +48 -0
  279. package/dist/harnesses/models/environment.js +88 -0
  280. package/dist/harnesses/models/host.d.ts +30 -0
  281. package/dist/harnesses/models/host.js +97 -0
  282. package/dist/{engines/pi → harnesses/models}/login.d.ts +66 -70
  283. package/dist/harnesses/models/login.js +439 -0
  284. package/dist/harnesses/models/models.d.ts +84 -0
  285. package/dist/harnesses/models/models.js +197 -0
  286. package/dist/harnesses/pi-durable/agent.d.ts +42 -0
  287. package/dist/harnesses/pi-durable/agent.js +581 -0
  288. package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
  289. package/dist/harnesses/pi-durable/defaults.js +26 -0
  290. package/dist/harnesses/pi-durable/define.d.ts +77 -0
  291. package/dist/harnesses/pi-durable/define.js +28 -0
  292. package/dist/harnesses/pi-durable/entries.d.ts +32 -0
  293. package/dist/harnesses/pi-durable/entries.js +126 -0
  294. package/dist/harnesses/pi-durable/events.d.ts +25 -0
  295. package/dist/harnesses/pi-durable/events.js +238 -0
  296. package/dist/harnesses/pi-durable/ids.d.ts +16 -0
  297. package/dist/harnesses/pi-durable/ids.js +55 -0
  298. package/dist/harnesses/pi-durable/messages.d.ts +41 -0
  299. package/dist/harnesses/pi-durable/messages.js +116 -0
  300. package/dist/harnesses/pi-durable/open.d.ts +95 -0
  301. package/dist/harnesses/pi-durable/open.js +330 -0
  302. package/dist/harnesses/pi-durable/reads.d.ts +30 -0
  303. package/dist/harnesses/pi-durable/reads.js +94 -0
  304. package/dist/harnesses/pi-durable/registry.d.ts +18 -0
  305. package/dist/harnesses/pi-durable/registry.js +56 -0
  306. package/dist/harnesses/pi-durable/release.d.ts +13 -0
  307. package/dist/harnesses/pi-durable/release.js +40 -0
  308. package/dist/harnesses/pi-durable/serve.d.ts +90 -0
  309. package/dist/harnesses/pi-durable/serve.js +186 -0
  310. package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
  311. package/dist/harnesses/pi-durable/sessions.js +47 -0
  312. package/dist/harnesses/pi-durable/storage.d.ts +4 -0
  313. package/dist/harnesses/pi-durable/storage.js +49 -0
  314. package/dist/harnesses/pi-durable/stream.d.ts +20 -0
  315. package/dist/harnesses/pi-durable/stream.js +221 -0
  316. package/dist/harnesses/pi-durable/text.d.ts +30 -0
  317. package/dist/harnesses/pi-durable/text.js +292 -0
  318. package/dist/harnesses/pi-durable/wake.d.ts +11 -0
  319. package/dist/harnesses/pi-durable/wake.js +90 -0
  320. package/dist/http/client.d.ts +6 -0
  321. package/dist/http/client.js +256 -0
  322. package/dist/http/contract.d.ts +541 -0
  323. package/dist/http/contract.js +179 -0
  324. package/dist/http/server.d.ts +34 -0
  325. package/dist/http/server.js +194 -0
  326. package/dist/index.d.ts +19 -4
  327. package/dist/index.js +13 -5
  328. package/dist/lark.d.ts +2 -2
  329. package/dist/lark.js +2 -2
  330. package/dist/locked-file.d.ts +17 -0
  331. package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
  332. package/dist/once.d.ts +6 -0
  333. package/dist/once.js +23 -0
  334. package/dist/paths.d.ts +36 -52
  335. package/dist/paths.js +66 -182
  336. package/dist/runtime.d.ts +25 -2
  337. package/dist/runtime.js +27 -0
  338. package/dist/scaffold/add-channel.d.ts +14 -27
  339. package/dist/scaffold/add-channel.js +82 -180
  340. package/dist/scaffold/init.d.ts +17 -18
  341. package/dist/scaffold/init.js +87 -99
  342. package/dist/scaffold/templates/SYSTEM.md +12 -0
  343. package/dist/scaffold/templates/env.example +4 -10
  344. package/dist/scaffold/templates/fastagent.config.ts +21 -33
  345. package/dist/scaffold/templates/gitignore +8 -1
  346. package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
  347. package/dist/scaffold/templates.d.ts +9 -4
  348. package/dist/scaffold/templates.js +7 -5
  349. package/dist/scaffold/vendor-skill.d.ts +1 -4
  350. package/dist/scaffold/vendor-skill.js +5 -6
  351. package/dist/schedule/cron.d.ts +13 -0
  352. package/dist/schedule/cron.js +52 -0
  353. package/dist/schedule/eventbridge-cron.d.ts +17 -0
  354. package/dist/schedule/eventbridge-cron.js +88 -0
  355. package/dist/schedule/schedule.d.ts +23 -0
  356. package/dist/schedule/schedule.js +34 -0
  357. package/dist/schedule/scheduler.d.ts +34 -33
  358. package/dist/schedule/scheduler.js +251 -137
  359. package/dist/schedule/state.d.ts +4 -14
  360. package/dist/schedule/state.js +7 -7
  361. package/dist/schedule/wake-alarm.d.ts +22 -2
  362. package/dist/schedule/wake-alarm.js +66 -20
  363. package/dist/schedule/wakeups.d.ts +16 -15
  364. package/dist/schedule/wakeups.js +47 -45
  365. package/dist/service.d.ts +56 -169
  366. package/dist/service.js +91 -187
  367. package/dist/serving-command.d.ts +10 -0
  368. package/dist/serving-command.js +16 -0
  369. package/dist/sign-in-api.d.ts +147 -0
  370. package/dist/sign-in-api.js +9 -0
  371. package/dist/slack.d.ts +1 -1
  372. package/dist/slack.js +1 -1
  373. package/dist/{channels/kit/state.js → state-file.js} +6 -6
  374. package/dist/telegram.d.ts +1 -0
  375. package/dist/telegram.js +1 -0
  376. package/dist/tunnel.d.ts +3 -0
  377. package/dist/tunnel.js +14 -5
  378. package/dist/value-file.d.ts +40 -0
  379. package/dist/value-file.js +164 -0
  380. package/dist/version.d.ts +5 -0
  381. package/dist/version.js +14 -1
  382. package/package.json +18 -28
  383. package/dist/agent.d.ts +0 -106
  384. package/dist/agent.js +0 -21
  385. package/dist/channels/control.d.ts +0 -31
  386. package/dist/channels/control.js +0 -329
  387. package/dist/channels/define-channel.d.ts +0 -39
  388. package/dist/channels/define-channel.js +0 -9
  389. package/dist/channels/feishu/context-buffer.d.ts +0 -35
  390. package/dist/channels/feishu/context-buffer.js +0 -65
  391. package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
  392. package/dist/channels/http.d.ts +0 -23
  393. package/dist/channels/http.js +0 -57
  394. package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
  395. package/dist/channels/slack/context-buffer.d.ts +0 -23
  396. package/dist/channels/slack/context-buffer.js +0 -37
  397. package/dist/channels/sse.d.ts +0 -4
  398. package/dist/channels/sse.js +0 -66
  399. package/dist/cli/commands/chat.d.ts +0 -3
  400. package/dist/cli/commands/chat.js +0 -14
  401. package/dist/cli/commands/routine-run.d.ts +0 -6
  402. package/dist/cli/commands/routine-run.js +0 -46
  403. package/dist/cli/commands/routine.d.ts +0 -25
  404. package/dist/cli/commands/routine.js +0 -122
  405. package/dist/cli/commands/tool.d.ts +0 -1
  406. package/dist/cli/commands/tool.js +0 -57
  407. package/dist/collect.d.ts +0 -63
  408. package/dist/collect.js +0 -90
  409. package/dist/core.d.ts +0 -13
  410. package/dist/core.js +0 -15
  411. package/dist/engines/pi/agent-models.d.ts +0 -83
  412. package/dist/engines/pi/agent-models.js +0 -120
  413. package/dist/engines/pi/agent-session-factory.d.ts +0 -121
  414. package/dist/engines/pi/agent-session-factory.js +0 -535
  415. package/dist/engines/pi/chat.d.ts +0 -3
  416. package/dist/engines/pi/chat.js +0 -8
  417. package/dist/engines/pi/config.d.ts +0 -71
  418. package/dist/engines/pi/config.js +0 -199
  419. package/dist/engines/pi/create.d.ts +0 -170
  420. package/dist/engines/pi/create.js +0 -343
  421. package/dist/engines/pi/definition.d.ts +0 -74
  422. package/dist/engines/pi/definition.js +0 -236
  423. package/dist/engines/pi/entry-images.d.ts +0 -14
  424. package/dist/engines/pi/entry-images.js +0 -23
  425. package/dist/engines/pi/invoke-session.d.ts +0 -18
  426. package/dist/engines/pi/invoke-session.js +0 -403
  427. package/dist/engines/pi/locked-file.d.ts +0 -9
  428. package/dist/engines/pi/login.js +0 -331
  429. package/dist/engines/pi/machine.d.ts +0 -53
  430. package/dist/engines/pi/machine.js +0 -137
  431. package/dist/engines/pi/models.d.ts +0 -200
  432. package/dist/engines/pi/models.js +0 -533
  433. package/dist/engines/pi/open.d.ts +0 -180
  434. package/dist/engines/pi/open.js +0 -270
  435. package/dist/engines/pi/openai-account-models.d.ts +0 -35
  436. package/dist/engines/pi/openai-account-models.js +0 -116
  437. package/dist/engines/pi/report.d.ts +0 -18
  438. package/dist/engines/pi/report.js +0 -59
  439. package/dist/engines/pi/retry-event.d.ts +0 -6
  440. package/dist/engines/pi/retry-event.js +0 -15
  441. package/dist/engines/pi/service.d.ts +0 -9
  442. package/dist/engines/pi/service.js +0 -14
  443. package/dist/engines/pi/session-builder.d.ts +0 -13
  444. package/dist/engines/pi/session-builder.js +0 -117
  445. package/dist/engines/pi/session-control.d.ts +0 -77
  446. package/dist/engines/pi/session-control.js +0 -908
  447. package/dist/engines/pi/session-effects.d.ts +0 -14
  448. package/dist/engines/pi/session-effects.js +0 -30
  449. package/dist/engines/pi/session-inheritance.d.ts +0 -21
  450. package/dist/engines/pi/session-inheritance.js +0 -204
  451. package/dist/engines/pi/session-markers.d.ts +0 -49
  452. package/dist/engines/pi/session-markers.js +0 -60
  453. package/dist/engines/pi/session-settings.d.ts +0 -67
  454. package/dist/engines/pi/session-settings.js +0 -119
  455. package/dist/engines/pi/session-store.d.ts +0 -69
  456. package/dist/engines/pi/session-store.js +0 -430
  457. package/dist/engines/pi/tool-context.d.ts +0 -45
  458. package/dist/engines/pi/tool-context.js +0 -31
  459. package/dist/engines/pi/tool.d.ts +0 -99
  460. package/dist/engines/pi/tool.js +0 -118
  461. package/dist/engines/pi/turn-kit.d.ts +0 -92
  462. package/dist/engines/pi/turn-kit.js +0 -288
  463. package/dist/engines/pi/wake-tool.d.ts +0 -9
  464. package/dist/engines/pi/wake-tool.js +0 -92
  465. package/dist/loader.d.ts +0 -41
  466. package/dist/loader.js +0 -116
  467. package/dist/node.d.ts +0 -3
  468. package/dist/node.js +0 -4
  469. package/dist/observe.d.ts +0 -6
  470. package/dist/observe.js +0 -47
  471. package/dist/pi.d.ts +0 -25
  472. package/dist/pi.js +0 -15
  473. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  474. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  475. package/dist/schedule/discover.d.ts +0 -17
  476. package/dist/schedule/discover.js +0 -61
  477. package/dist/schedule/routine.d.ts +0 -43
  478. package/dist/schedule/routine.js +0 -32
  479. package/dist/schedule/run.d.ts +0 -55
  480. package/dist/schedule/run.js +0 -146
  481. package/dist/secrets-gate.d.ts +0 -32
  482. package/dist/secrets-gate.js +0 -33
  483. package/dist/session-remote.d.ts +0 -47
  484. package/dist/session-remote.js +0 -460
  485. package/dist/session.d.ts +0 -466
  486. package/dist/session.js +0 -57
  487. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
@@ -1,32 +1,26 @@
1
1
  /**
2
- * `deploy agentcore`: one CloudFormation stack (runtime + forwarder Lambda behind a Function URL + EventBridge
3
- * schedules); no resident process.
2
+ * `deploy agentcore`: one CloudFormation stack (runtime + forwarder Lambda behind a Function URL); the container
3
+ * sets its EventBridge schedules itself; no resident process.
4
4
  */
5
5
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
6
6
  import { tmpdir } from "node:os";
7
7
  import { basename, join } from "node:path";
8
- import { FORWARDER_FILE, TEMPLATE_FILE, agentcoreName, agentcoreStackName, forwarderLogGroup, ingressSessionId, isGeneratedAgentcoreTemplate, planAgentcoreDeploy, } from "../../../deploy/agentcore/plan.js";
8
+ import { FORWARDER_FILE, TEMPLATE_FILE, agentcoreName, agentcoreStackName, forwarderLogGroup, PROBE_PAYLOAD, ingressSessionId, isGeneratedAgentcoreTemplate, planAgentcoreDeploy, } from "../../../deploy/agentcore/plan.js";
9
9
  import { deployAgentcoreRun, pickStackOutputs } from "../../../deploy/agentcore/run.js";
10
10
  import { awsCli, awsJson } from "../../../deploy/agentcore/aws-cli.js";
11
11
  import { agentcoreShell } from "../../../deploy/agentcore/shell.js";
12
12
  import { awsRunner, spawnRunner } from "../../../deploy/runner.js";
13
13
  import { SECRET_FILE_MODE, exists } from "../../../paths.js";
14
- import { loadRoutines } from "../../../schedule/discover.js";
15
14
  import { assembleSecrets } from "../../../deploy/secrets.js";
16
15
  import { failStartup } from "../../fail.js";
17
- import { isInteractive } from "../../shared.js";
16
+ import { shellArg } from "../../shared.js";
18
17
  import { boxLoginStep, registrarsFor } from "./shared.js";
19
- /** A copy/paste-safe POSIX shell argument for the command hints deploy prints. */
20
- function shellArg(value) {
21
- return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
22
- }
23
18
  export const agentcoreHost = {
24
19
  isOurs: (path, content) => path.endsWith(TEMPLATE_FILE) && isGeneratedAgentcoreTemplate(content),
25
- artifact: TEMPLATE_FILE,
26
- async shell({ workspace }) {
27
- const name = agentcoreName(basename(workspace));
20
+ async shell(agentDir) {
21
+ const name = agentcoreName(basename(agentDir));
28
22
  const stack = agentcoreStackName(name);
29
- const aws = awsRunner(workspace);
23
+ const aws = awsRunner(agentDir);
30
24
  const read = await awsCli(aws).read(["cloudformation", "describe-stacks", "--stack-name", stack, "--query", "Stacks[0].Outputs", "--output", "json"], awsJson(pickStackOutputs));
31
25
  if ("absent" in read)
32
26
  throw new Error(`no AgentCore stack ${stack} in this account/region — deploy it first`);
@@ -37,17 +31,8 @@ export const agentcoreHost = {
37
31
  throw new Error(`stack ${stack} has no RuntimeArn output — redeploy it`);
38
32
  return agentcoreShell(runtimeArn, ingressSessionId(name), aws);
39
33
  },
40
- async deploy(ctx) {
41
- const { opts, agentDir, workspace, config, channels, longConnectionChannels, pre, write } = ctx;
42
- const { modelAuth, boxLogin, container, declaredSecrets, values, valueFile } = pre;
43
- if (boxLogin) {
44
- console.error(`[fastagent] note: AgentCore resets the runtime's storage on every deploy AND after 14 idle days, and the ` +
45
- `${boxLogin} login with it: every deploy of this agent ends with a login on the runtime, and after an idle ` +
46
- `reset every turn fails until \`fastagent login ${boxLogin} --deployment agentcore\` (the runtime's log says ` +
47
- `so). For an agent that must keep answering unattended, set ${boxLogin}'s API key in ${valueFile}.`);
48
- }
49
- // Long-connection channels are STRUCTURALLY unsupported: the connection is the ingress, and a reclaimed session
50
- // has nothing to wake it.
34
+ validate({ opts, agentDir, pre }) {
35
+ const longConnectionChannels = pre.channels.filter((channel) => channel.ingress === "long-connection");
51
36
  if (longConnectionChannels.length > 0) {
52
37
  const msg = `long-connection channel (${longConnectionChannels.map((c) => c.name).join(", ")}) cannot run on AgentCore — there is no ` +
53
38
  `resident process to hold the connection, and nothing wakes a reclaimed session. Switch the channel ` +
@@ -56,50 +41,37 @@ export const agentcoreHost = {
56
41
  failStartup(new Error(`deploy stopped: ${msg}`));
57
42
  console.error(`[fastagent] warn: ${msg}`);
58
43
  }
59
- // Host capability limit, stated before the image is built rather than left to a line in CloudWatch.
60
- if (config.sessionControl === true) {
61
- console.error(`[fastagent] warn: sessionControl: true has no effect on AgentCore — /control/* is not served here. This ` +
62
- `host's only public ingress relays anonymous traffic to the container as trusted (the forwarder holds the ` +
63
- `ingress secret and attaches it), and the plane has nothing of its own to verify. Steer sessions from a ` +
64
- `host that serves it behind your own auth (docs/design/session-control.md §14).`);
65
- }
66
- // Wake-ups are a default capability here as everywhere: pending ones are mirrored into one-shot EventBridge
67
- // schedules via the forwarder (the wake-alarm mechanism — see deploy/agentcore/plan.ts), which is why every stack
68
- // has one.
69
- const loaded = await loadRoutines(agentDir).catch(failStartup);
70
- if (loaded.failures.length > 0) {
71
- failStartup(new Error(`deploy stopped: cannot load routines: ${loaded.failures.map((x) => `${x.label}: ${x.message}`).join("; ")}`));
72
- }
73
- const acName = agentcoreName(basename(workspace));
74
- // Every derived AWS name embeds acName; the tightest ceiling is the Lambda function name
75
- // (`fastagent-<name>-forwarder` ≤ 64 chars).
76
- if (acName.length > 40) {
77
- failStartup(new Error(`deploy stopped: the directory name maps to "${acName}" (${acName.length} chars) — AWS resource ` +
44
+ const name = agentcoreName(basename(agentDir));
45
+ // The forwarder Lambda name embeds this name and must fit AWS's 64-character limit.
46
+ if (name.length > 40) {
47
+ failStartup(new Error(`deploy stopped: the directory name maps to "${name}" (${name.length} chars) — AWS resource ` +
78
48
  `names derived from it exceed their limits past 40 chars. Deploy from a shorter directory name.`));
79
49
  }
50
+ },
51
+ async deploy(ctx) {
52
+ const { opts, agentDir, config, channels, pre, write } = ctx;
53
+ const { modelAuth, boxLogin, container, declaredSecrets, values, valueFile } = pre;
54
+ if (boxLogin) {
55
+ console.error(`[fastagent] note: AgentCore resets the runtime's storage on every deploy AND after 14 idle days, and the ` +
56
+ `${boxLogin} login with it: every deploy of this agent ends with a login on the runtime, and after an idle ` +
57
+ `reset every turn fails until \`fastagent login ${boxLogin} --deployment agentcore\` (the runtime's log says ` +
58
+ `so). For an agent that must keep answering unattended, set ${boxLogin}'s API key in ${valueFile}.`);
59
+ }
60
+ // Schedules and wake-ups need nothing from the template beyond the forwarder every stack has: the container
61
+ // mirrors them into EventBridge schedules itself (schedule/wake-alarm.ts). A schedule that does not build is the
62
+ // pre-flight's to report.
63
+ const acName = agentcoreName(basename(agentDir));
80
64
  const plan = planAgentcoreDeploy({
81
65
  name: acName,
82
66
  boxLogin,
83
67
  channels,
84
68
  secrets: pre.secrets,
85
- // ONLY THE ONES WITH A CRON become rules. A routine without one is reached by NAME, through this host's
86
- // IAM-gated `routine-run` envelope (channels/agentcore.ts) rather than a route — so it needs no rule and
87
- // loses nothing by not having one.
88
- schedules: loaded.routines.flatMap((r) => r.cron === undefined ? [] : [{ name: r.name, cron: r.cron, ...(r.tz !== undefined ? { tz: r.tz } : {}) }]),
89
69
  idleTimeoutSeconds: config.deploy?.agentcore?.idleTimeoutSeconds,
90
70
  ...container,
91
71
  });
92
- for (const u of plan.untranslatableSchedules) {
93
- // Same discipline as Fly's kept-toml time-trigger gate: a deploy whose schedule silently never fires is worse
94
- // than a stopped deploy.
95
- const msg = `schedule "${u.name}" cannot be expressed as an EventBridge rule — ${u.reason}`;
96
- if (opts.run)
97
- failStartup(new Error(`deploy stopped: ${msg}`));
98
- console.error(`[fastagent] warn: ${msg} — it will NOT fire on this deployment`);
99
- }
100
- // The template IS the topology (EventBridge rules, wake wiring, secrets).
72
+ // The template IS the topology (forwarder, alarm wiring, secrets).
101
73
  const templateArtifact = plan.artifacts.find((a) => a.path.endsWith(TEMPLATE_FILE));
102
- const templateHome = join(workspace, templateArtifact?.path ?? TEMPLATE_FILE);
74
+ const templateHome = join(agentDir, templateArtifact?.path ?? TEMPLATE_FILE);
103
75
  if (!opts.force && templateArtifact && (await exists(templateHome))) {
104
76
  const existing = await readFile(templateHome, "utf8");
105
77
  if (isGeneratedAgentcoreTemplate(existing) && existing !== templateArtifact.content) {
@@ -108,44 +80,45 @@ export const agentcoreHost = {
108
80
  // rule (deploy.ts) for every host and every path — stating it a second time here is how the two
109
81
  // wordings drift apart.
110
82
  console.error(`[fastagent] warn: ${templateArtifact.path} no longer matches this definition ` +
111
- `(channels/routines or a deploy.agentcore setting changed) — the kept template would ` +
83
+ `(channels or a deploy.agentcore setting changed) — the kept template would ` +
112
84
  `silently drop the difference.`);
113
85
  }
114
86
  }
115
87
  await write(plan.artifacts, {
116
88
  force: !!opts.force,
117
- alwaysWrite: [`${container.agentPrefix}${FORWARDER_FILE}`],
89
+ alwaysWrite: [FORWARDER_FILE],
118
90
  });
119
91
  if (opts.run) {
120
- return runDeployAgentcore({
92
+ await runDeployAgentcore({
121
93
  agentDir,
122
- workspace,
123
- agentPrefix: container.agentPrefix,
124
94
  name: acName,
125
95
  modelAuth,
126
96
  boxLogin,
127
- input: opts.input !== false && isInteractive(),
97
+ answerer: ctx.answerer,
98
+ rerun: ctx.rerun,
128
99
  channels,
129
100
  declaredSecrets,
130
101
  values,
131
102
  valueFile,
132
103
  topology: plan.topology,
133
104
  });
105
+ return undefined;
134
106
  }
135
107
  console.log(plan.runbook.join("\n"));
136
- return;
108
+ return undefined;
137
109
  },
138
110
  };
139
111
  /** `deploy agentcore --run`: drive aws + docker to completion. */
140
112
  async function runDeployAgentcore(params) {
141
- const { agentDir, workspace, agentPrefix, name, channels, topology } = params;
113
+ const { agentDir, name, channels, topology } = params;
142
114
  // Decided before the first side effect: the deploy resets the runtime's storage and the login with it, so with nobody
143
115
  // to log it in again it would end at "not logged in" AFTER replacing a runtime that was serving, with the webhooks
144
- // a previous deploy registered still pointing at it.
145
- if (params.boxLogin && !params.input) {
116
+ // a previous deploy registered still pointing at it. A coding agent's ticket has someone: its user.
117
+ if (params.boxLogin && params.answerer.kind === "nobody") {
146
118
  failStartup(new Error(`deploy stopped: every AgentCore deploy wipes the runtime's ${params.boxLogin} login, and without a terminal ` +
147
- `nobody can log it in again — run this deploy in a terminal, or set ${params.boxLogin}'s API key in ` +
148
- `${params.valueFile}`));
119
+ `nobody can log it in again — run this deploy in a terminal; from a coding agent, run ` +
120
+ `\`${params.rerun} --json\`, which hands the login to its user; or set ` +
121
+ `${params.boxLogin}'s API key in ${params.valueFile}`));
149
122
  }
150
123
  const { secrets, missingSecrets } = assembleSecrets({
151
124
  modelAuth: params.modelAuth,
@@ -162,8 +135,8 @@ async function runDeployAgentcore(params) {
162
135
  try {
163
136
  const outcome = await deployAgentcoreRun({
164
137
  name,
165
- templatePath: `${agentPrefix}${TEMPLATE_FILE}`,
166
- dockerfilePath: `${agentPrefix}Dockerfile`,
138
+ templatePath: TEMPLATE_FILE,
139
+ dockerfilePath: "Dockerfile",
167
140
  tag: new Date()
168
141
  .toISOString()
169
142
  .replace(/[-:.TZ]/g, "")
@@ -174,8 +147,8 @@ async function runDeployAgentcore(params) {
174
147
  valueFile: params.valueFile,
175
148
  channels,
176
149
  topology,
177
- ...boxLoginStep("agentcore", params, (runtimeArn) => agentcoreShell(runtimeArn, ingressSessionId(name), awsRunner(workspace))),
178
- }, awsRunner(workspace), spawnRunner("docker", workspace), (m) => console.error(`[fastagent] ${m}`), async (content) => {
150
+ ...boxLoginStep("agentcore", params, (runtimeArn) => agentcoreShell(runtimeArn, ingressSessionId(name), awsRunner(agentDir))),
151
+ }, awsRunner(agentDir), spawnRunner("docker", agentDir), (m) => console.error(`[fastagent] ${m}`), async (content) => {
179
152
  const path = join(paramsDir, "params.json");
180
153
  await writeFile(path, content, { mode: SECRET_FILE_MODE });
181
154
  return path;
@@ -183,13 +156,13 @@ async function runDeployAgentcore(params) {
183
156
  const path = join(paramsDir, "forwarder.zip");
184
157
  await writeFile(path, bytes);
185
158
  return path;
186
- }, registrarsFor(agentDir));
159
+ }, registrarsFor(agentDir, params.values, params.answerer));
187
160
  if (!outcome.ok)
188
161
  failStartup(new Error(`deploy stopped: ${outcome.gate}`));
189
162
  console.error(`[fastagent] deployed → ${outcome.runtimeArn}`);
190
163
  if (outcome.url)
191
164
  console.error(`[fastagent] webhook ingress → ${outcome.url}`);
192
- const logsDir = shellArg(workspace);
165
+ const logsDir = shellArg(agentDir);
193
166
  console.error(`[fastagent] runtime logs → fastagent logs agentcore ${logsDir} --follow`);
194
167
  console.error(`[fastagent] forwarder logs → fastagent logs agentcore ${logsDir} --source forwarder --follow`);
195
168
  // `--run` never prints the runbook, and this is the ONE step in it that nothing else will remind anyone of: a
@@ -200,9 +173,9 @@ async function runDeployAgentcore(params) {
200
173
  `--log-group-name <the group \`fastagent logs agentcore ${logsDir} --follow\` resolves> --retention-in-days 14`);
201
174
  console.error(`[fastagent] ...and for the forwarder: aws logs put-retention-policy ` +
202
175
  `--log-group-name ${forwarderLogGroup(name)} --retention-in-days 14`);
203
- console.error(`[fastagent] invoke: aws bedrock-agentcore invoke-agent-runtime --agent-runtime-arn ${outcome.runtimeArn} \\\n` +
176
+ console.error(`[fastagent] probe: aws bedrock-agentcore invoke-agent-runtime --agent-runtime-arn ${outcome.runtimeArn} \\\n` +
204
177
  ` --runtime-session-id "${ingressSessionId(name)}" \\\n` +
205
- ` --payload '{"kind":"invoke","session":"cli","text":"hello"}' --cli-binary-format raw-in-base64-out /dev/stdout`);
178
+ ` --payload '${PROBE_PAYLOAD}' --cli-binary-format raw-in-base64-out /dev/stdout`);
206
179
  }
207
180
  finally {
208
181
  await rm(paramsDir, { recursive: true, force: true });
@@ -1,45 +1,38 @@
1
1
  /** `deploy docker`: one app service + loopback port + state volume, as a user-owned Compose file. */
2
2
  import { mkdir, writeFile } from "node:fs/promises";
3
- import { basename, dirname, join, relative, resolve } from "node:path";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { webhookPaths } from "../../../deploy/channel-ingress.js";
5
- import { DOCKER_COMPOSE_FILE, composeHasTunnelService, isGeneratedCompose, planDockerDeploy, toDockerProjectName, } from "../../../deploy/docker/plan.js";
5
+ import { DOCKER_COMPOSE_FILE, DOCKER_VALUE_FILE, composeHasTunnelService, isGeneratedCompose, planDockerDeploy, toDockerProjectName, } from "../../../deploy/docker/plan.js";
6
6
  import { deployDockerRun } from "../../../deploy/docker/run.js";
7
7
  import { spawnRunner } from "../../../deploy/runner.js";
8
8
  import { openExternalUrl } from "../../../open-url.js";
9
- import { SECRETS_DIRNAME, SECRET_FILE_MODE, exists, readTextIfExists, resolveStateRoot, } from "../../../paths.js";
10
- import { dotEnvPath } from "../../../env.js";
9
+ import { SECRET_FILE_MODE, exists, readTextIfExists, resolveStateRoot } from "../../../paths.js";
11
10
  import { announceWebhooks } from "../../../tunnel.js";
12
11
  import { assembleSecrets } from "../../../deploy/secrets.js";
13
12
  import { processShell } from "../../../deploy/box-shell.js";
14
13
  import { failStartup } from "../../fail.js";
15
- import { isInteractive } from "../../shared.js";
16
- import { boxLoginStep } from "./shared.js";
14
+ import { boxLoginStep, leftToPerson } from "./shared.js";
17
15
  export const dockerHost = {
18
16
  isOurs: (path, content) => path.endsWith(DOCKER_COMPOSE_FILE) && isGeneratedCompose(content),
19
- artifact: DOCKER_COMPOSE_FILE,
20
- shell: async ({ agentDir, workspace }) => composeShell(`${basename(agentDir)}/${DOCKER_COMPOSE_FILE}`, workspace),
21
- async deploy(ctx) {
22
- const { opts, agentDir, workspace, channels, webhookChannels, pre, write } = ctx;
23
- const { modelAuth, boxLogin, container, port, declaredSecrets, values, valueFile } = pre;
24
- // The generated Compose names `<agent>/.secrets/.env` unconditionally, so it has to be there — Compose refuses
25
- // an `env_file` entry pointing at a missing path, and this floor predates `required: false` (Compose 2.24).
26
- // Creating it empty is honest: a deployment that declares nothing declares it in an empty file.
27
- const composeValueFile = join(agentDir, SECRETS_DIRNAME, ".env");
28
- await mkdir(dirname(composeValueFile), { recursive: true });
29
- if (!(await exists(composeValueFile)))
30
- await writeFile(composeValueFile, "", { mode: SECRET_FILE_MODE });
31
- // The pre-flight read whatever `FASTAGENT_SECRETS_DIR` resolved to; the committed Compose cannot, because a
32
- // builder's path would not mean the same thing anywhere else. Under `--run` the two files disagreeing is a
33
- // DETERMINISTIC failure and gates like every other one: the missing-values gate would pass on the file this
34
- // machine reads while `compose up` starts a container whose declared secrets and model are all absent.
35
- if (resolve(dotEnvPath(agentDir)) !== resolve(composeValueFile)) {
36
- const issue = `FASTAGENT_SECRETS_DIR points this run's values at ${valueFile}, but the generated Compose reads ` +
37
- `${relative(workspace, composeValueFile)} (a committed artifact cannot carry this machine's path), so the ` +
17
+ shell: async (agentDir) => composeShell(DOCKER_COMPOSE_FILE, agentDir),
18
+ validate({ opts, agentDir, pre }) {
19
+ if (resolve(agentDir, pre.valueFile) !== join(agentDir, DOCKER_VALUE_FILE)) {
20
+ const issue = `FASTAGENT_SECRETS_DIR points this run's values at ${pre.valueFile}, but the generated Compose reads ` +
21
+ `${DOCKER_VALUE_FILE} (a committed artifact cannot carry this machine's path), so the ` +
38
22
  `container would start with none of them. Unset FASTAGENT_SECRETS_DIR, or put the values in that file.`;
39
23
  if (opts.run)
40
24
  failStartup(new Error(`deploy stopped: ${issue}`));
41
25
  console.error(`[fastagent] warn: ${issue}`);
42
26
  }
27
+ },
28
+ async deploy(ctx) {
29
+ const { opts, agentDir, channels, webhookChannels, pre, write } = ctx;
30
+ const { modelAuth, boxLogin, container, port, declaredSecrets, values, valueFile } = pre;
31
+ // Compose requires its committed env_file to exist, even when no values are declared.
32
+ const composeValueFile = join(agentDir, DOCKER_VALUE_FILE);
33
+ await mkdir(dirname(composeValueFile), { recursive: true });
34
+ if (!(await exists(composeValueFile)))
35
+ await writeFile(composeValueFile, "", { mode: SECRET_FILE_MODE });
43
36
  // Compose interpolates `$VAR` INSIDE env_file values (`format: raw` needs Compose 2.30), so a credential
44
37
  // containing `$` reaches the container rewritten — silently, and differently from what this pre-flight read.
45
38
  // No escaping advice: this ONE file is also read literally by `dev`/`start` (`parseEnvContent`) and pushed
@@ -52,7 +45,7 @@ export const dockerHost = {
52
45
  `"$". Prefer a value without "$".`);
53
46
  }
54
47
  const hasDeclaredChannels = channels.length > 0;
55
- const projectName = toDockerProjectName(basename(workspace));
48
+ const projectName = toDockerProjectName(basename(agentDir));
56
49
  const dockerPlan = (tunnel) => planDockerDeploy({
57
50
  projectName,
58
51
  port,
@@ -70,7 +63,7 @@ export const dockerHost = {
70
63
  let plan = dockerPlan(requestedTunnel);
71
64
  // An existing Compose file is authoritative: shape its comparison/runbook from the topology on disk, regardless
72
65
  // of the current flag.
73
- const composeFile = join(workspace, plan.composePath);
66
+ const composeFile = join(agentDir, plan.composePath);
74
67
  let keptWithoutRequestedTunnel = false;
75
68
  // Same ownership question as fly.toml: a hand-owned compose file survives --force, so the plan must describe the
76
69
  // topology that will actually be there.
@@ -84,13 +77,12 @@ export const dockerHost = {
84
77
  if (opts.run) {
85
78
  return runDeployDocker({
86
79
  agentDir,
87
- workspace,
88
80
  composeFile: plan.composePath,
89
81
  port,
90
82
  requireTunnel: requestedTunnel,
91
83
  modelAuth,
92
84
  boxLogin,
93
- input: opts.input !== false && isInteractive(),
85
+ answerer: ctx.answerer,
94
86
  channels,
95
87
  declaredSecrets,
96
88
  values,
@@ -102,12 +94,13 @@ export const dockerHost = {
102
94
  `edit it, delete it and regenerate, or pass --force`);
103
95
  }
104
96
  console.log(plan.runbook.join("\n"));
105
- return;
97
+ return `http://127.0.0.1:${port}`;
106
98
  },
107
99
  };
108
100
  /** `deploy docker --run`: reconcile the user-owned local topology, then log the box in if it must. */
109
101
  async function runDeployDocker(params) {
110
- const { agentDir, workspace, composeFile, port, requireTunnel, channels } = params;
102
+ const { agentDir, composeFile, port, requireTunnel, channels } = params;
103
+ const leave = leftToPerson(params.answerer);
111
104
  const { secrets, missingSecrets } = assembleSecrets({
112
105
  modelAuth: params.modelAuth,
113
106
  declared: params.declaredSecrets,
@@ -121,12 +114,14 @@ async function runDeployDocker(params) {
121
114
  valueFile: params.valueFile,
122
115
  requireTunnel,
123
116
  channels,
124
- ...boxLoginStep("docker", params, () => composeShell(composeFile, workspace)),
117
+ ...boxLoginStep("docker", params, () => composeShell(composeFile, agentDir)),
125
118
  announce: (tunnelUrl) => announceWebhooks(agentDir, tunnelUrl, channels, {
126
- openUrl: openExternalUrl,
119
+ // A ticket's process opens no browser: its person is told, through `leave`.
120
+ ...(leave === undefined ? { openUrl: openExternalUrl } : { leave }),
127
121
  stateRoot: resolveStateRoot(agentDir),
122
+ env: Object.fromEntries(params.values),
128
123
  }),
129
- }, spawnRunner("docker", workspace), (message) => console.error(`[fastagent] ${message}`));
124
+ }, spawnRunner("docker", agentDir), (message) => console.error(`[fastagent] ${message}`));
130
125
  const compose = `docker compose -f ${composeFile}`;
131
126
  // Compose reached "up" iff the driver could report where it is.
132
127
  const isUp = outcome.ok || outcome.url !== undefined || outcome.tunnelUrl !== undefined;
@@ -144,14 +139,18 @@ async function runDeployDocker(params) {
144
139
  if (!outcome.ok)
145
140
  failStartup(new Error(`deploy stopped: ${outcome.gate}`));
146
141
  if (outcome.tunnelUrl)
147
- return;
142
+ return outcome.tunnelUrl;
148
143
  const paths = webhookPaths(channels);
149
144
  if (paths.length > 0) {
150
- console.error(`[fastagent] note: public ingress is operator-owned — configure your tunnel/proxy, then wire the ` +
151
- `default webhook path(s): ${paths.join(", ")} (or your remapped channel routes)`);
145
+ const unreached = `${paths.join(", ")} receive nothing until a public URL reaches this container — re-run with --tunnel for a ` +
146
+ `Quick Tunnel, or configure your own tunnel/proxy and point those webhook path(s) (or your remapped channel ` +
147
+ `routes) at it`;
148
+ console.error(`[fastagent] note: ${unreached}`);
149
+ leave?.(unreached);
152
150
  }
151
+ return outcome.url;
153
152
  }
154
153
  /** `docker compose exec` into the running agent service. */
155
- function composeShell(composeFile, workspace) {
156
- return processShell("docker", (command) => ["compose", "-f", composeFile, "exec", "-T", "agent", "sh", "-c", command], workspace);
154
+ function composeShell(composeFile, agentDir) {
155
+ return processShell("docker", (command) => ["compose", "-f", composeFile, "exec", "-T", "agent", "sh", "-c", command], agentDir);
157
156
  }
@@ -8,17 +8,16 @@ import { residencyFor } from "../../../deploy/residency.js";
8
8
  import { assembleSecrets } from "../../../deploy/secrets.js";
9
9
  import { processShell } from "../../../deploy/box-shell.js";
10
10
  import { failStartup } from "../../fail.js";
11
- import { isInteractive } from "../../shared.js";
11
+ import { FLY_LOGIN, hostLogin } from "./host-login.js";
12
12
  import { boxLoginStep, registrarsFor } from "./shared.js";
13
13
  export const flyHost = {
14
14
  isOurs: (path, content) => path.endsWith("fly.toml") && isGeneratedFlyToml(content),
15
- artifact: "fly.toml",
16
- async shell({ agentDir, workspace }) {
15
+ async shell(agentDir) {
17
16
  const flyToml = await readTextIfExists(join(agentDir, "fly.toml"));
18
- return flyShell((flyToml && parseFlyAppName(flyToml)) ?? toFlyAppName(basename(workspace)), workspace);
17
+ return flyShell((flyToml && parseFlyAppName(flyToml)) ?? toFlyAppName(basename(agentDir)), agentDir);
19
18
  },
20
19
  async deploy(ctx) {
21
- const { opts, agentDir, workspace, channels, pre, write } = ctx;
20
+ const { opts, agentDir, channels, pre, write } = ctx;
22
21
  const { hasCron, modelAuth, boxLogin, container, port, declaredSecrets, values, valueFile } = pre;
23
22
  // Two consistent modes.
24
23
  const flyTomlPath = join(agentDir, "fly.toml");
@@ -26,7 +25,7 @@ export const flyHost = {
26
25
  // Every decision below turns on ONE question — will `writeArtifacts` keep this file?
27
26
  const flyTomlKept = flyToml !== undefined && (!opts.force || !isGeneratedFlyToml(flyToml));
28
27
  const keptApp = flyTomlKept ? parseFlyAppName(flyToml) : undefined;
29
- const appName = keptApp ?? toFlyAppName(basename(workspace));
28
+ const appName = keptApp ?? toFlyAppName(basename(agentDir));
30
29
  if (keptApp)
31
30
  console.error(`[fastagent] app: ${keptApp} (from fly.toml)`);
32
31
  if (flyToml !== undefined && !flyTomlKept) {
@@ -58,28 +57,30 @@ export const flyHost = {
58
57
  ...container,
59
58
  });
60
59
  await write(plan.artifacts, { force: !!opts.force });
60
+ const reachedAt = `https://${appName}.fly.dev`;
61
61
  if (opts.run) {
62
- return runDeployFly({
62
+ await runDeployFly({
63
63
  agentDir,
64
- workspace,
65
- agentPrefix: container.agentPrefix,
66
64
  appName,
67
65
  modelAuth,
68
66
  boxLogin,
69
- input: opts.input !== false && isInteractive(),
67
+ answerer: ctx.answerer,
68
+ rerun: ctx.rerun,
70
69
  channels,
71
70
  declaredSecrets,
72
71
  values,
73
72
  valueFile,
74
73
  });
74
+ return reachedAt;
75
75
  }
76
76
  console.log(plan.runbook.join("\n"));
77
+ return reachedAt;
77
78
  },
78
79
  };
79
80
  /** `deploy fly --run`: drive flyctl to completion (idempotent, resumable). */
80
81
  async function runDeployFly(params) {
81
- const { agentDir, workspace, agentPrefix, appName, channels } = params;
82
- const fly = spawnRunner("fly", workspace);
82
+ const { agentDir, appName, channels } = params;
83
+ const fly = spawnRunner("fly", agentDir);
83
84
  // Fail fast if flyctl is absent (spawn ENOENT → 127), with the install link — not a confusing auth gate.
84
85
  if ((await fly(["version"], { capture: true })).code === 127) {
85
86
  failStartup(new Error(`flyctl not found — install it: https://fly.io/docs/flyctl/install, then re-run`));
@@ -95,18 +96,20 @@ async function runDeployFly(params) {
95
96
  missingSecrets,
96
97
  valueFile: params.valueFile,
97
98
  channels,
98
- flyConfig: `${agentPrefix}fly.toml`,
99
- dockerfile: `${agentPrefix}Dockerfile`,
100
- ...boxLoginStep("fly", params, () => flyShell(appName, workspace)),
101
- }, fly, (m) => console.error(`[fastagent] ${m}`), registrarsFor(agentDir));
99
+ flyConfig: "fly.toml",
100
+ dockerfile: "Dockerfile",
101
+ ...boxLoginStep("fly", params, () => flyShell(appName, agentDir)),
102
+ ...hostLogin(FLY_LOGIN, params.answerer, agentDir),
103
+ forAgent: `${params.rerun} --json`,
104
+ }, fly, (m) => console.error(`[fastagent] ${m}`), registrarsFor(agentDir, params.values, params.answerer));
102
105
  if (!outcome.ok)
103
106
  failStartup(new Error(`deploy stopped: ${outcome.gate}`));
104
107
  console.error(`[fastagent] deployed → https://${appName}.fly.dev`);
105
108
  }
106
109
  /** `fly ssh console` into the app's machine, woken first: a suspended machine has no shell to open. */
107
- function flyShell(app, workspace) {
110
+ function flyShell(app, agentDir) {
108
111
  return {
109
- ...processShell("fly", (command) => ["ssh", "console", "--app", app, "--quiet", "--command", `sh -c '${command}'`], workspace),
112
+ ...processShell("fly", (command) => ["ssh", "console", "--app", app, "--quiet", "--command", `sh -c '${command}'`], agentDir),
110
113
  // `fly ssh` fails with "no started VMs" while the machine is suspended, and a request is what resumes it. The
111
114
  // answer is not the point: a machine that stays down makes the shell fail right after, with Fly's own reason.
112
115
  wake: async () => {
@@ -0,0 +1,26 @@
1
+ import type { Answerer } from "./shared.ts";
2
+ export interface BrowserLogin {
3
+ bin: string;
4
+ /** The login at a terminal: it opens the browser itself. */
5
+ atTerminal: string[];
6
+ /** The login with no terminal: it prints the address, and exits 0 once the person approved there. */
7
+ unattended: string[];
8
+ /** The address in what `unattended` prints. */
9
+ address: RegExp;
10
+ /** The code a device login's address carries, for the person to check against the page. */
11
+ userCode?(address: URL): string | undefined;
12
+ /**
13
+ * The login that finishes with the code the approval page shows, for a browser that could not reach this machine
14
+ * (Fly's): `deploy --check <ticket> --code <code>` hands it over.
15
+ */
16
+ withCode?(code: string): string[];
17
+ }
18
+ export declare const FLY_LOGIN: BrowserLogin;
19
+ export declare const RAILWAY_LOGIN: BrowserLogin;
20
+ /**
21
+ * The host's sign-in, through whoever answers, as a driver's `hostLogin`; none for nobody, whose deploy stops at the
22
+ * host's own gate. Resolves undefined once signed in, else the line saying why not.
23
+ */
24
+ export declare function hostLogin(login: BrowserLogin, answerer: Answerer, cwd: string): {
25
+ hostLogin?: () => Promise<string | undefined>;
26
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Signing a host's CLI in to the person's account, for a deploy that finds it signed out. At a terminal the host's own
3
+ * login runs there and opens the browser. In a deploy's ticket it runs with no terminal, where both CLIs print the
4
+ * address to approve at and finish on their own once approved; that address is the person's step. Never a token: one
5
+ * set in .secrets/production/.env would travel to the box with every other variable there.
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ export const FLY_LOGIN = {
9
+ bin: "fly",
10
+ atTerminal: ["auth", "login"],
11
+ unattended: ["auth", "login"],
12
+ address: /https:\/\/fly\.io\/app\/auth\/cli\/[A-Za-z0-9_-]+/,
13
+ withCode: (code) => ["auth", "login", "--code", code],
14
+ };
15
+ export const RAILWAY_LOGIN = {
16
+ bin: "railway",
17
+ atTerminal: ["login"],
18
+ unattended: ["login", "--browserless"],
19
+ address: /https:\/\/railway\.com\/activate\?user_code=[A-Za-z0-9-]+/,
20
+ userCode: (address) => address.searchParams.get("user_code") ?? undefined,
21
+ };
22
+ /**
23
+ * The host's sign-in, through whoever answers, as a driver's `hostLogin`; none for nobody, whose deploy stops at the
24
+ * host's own gate. Resolves undefined once signed in, else the line saying why not.
25
+ */
26
+ export function hostLogin(login, answerer, cwd) {
27
+ if (answerer.kind === "nobody")
28
+ return {};
29
+ return { hostLogin: signIn(login, answerer, cwd) };
30
+ }
31
+ function signIn(login, answerer, cwd) {
32
+ if (answerer.kind === "terminal") {
33
+ return async () => {
34
+ const code = await exitOf(spawn(login.bin, login.atTerminal, { cwd, stdio: "inherit" }));
35
+ return code === 0 ? undefined : `\`${login.bin} ${login.atTerminal.join(" ")}\` did not sign in — re-run`;
36
+ };
37
+ }
38
+ const { step } = answerer;
39
+ return async () => {
40
+ const child = spawn(login.bin, login.unattended, { cwd, stdio: ["ignore", "pipe", "pipe"] });
41
+ let said = "";
42
+ let shown = false;
43
+ const hear = (chunk) => {
44
+ const text = String(chunk);
45
+ process.stderr.write(text);
46
+ said = `${said}${text}`.slice(-4096);
47
+ const found = shown ? null : login.address.exec(said);
48
+ if (found === null)
49
+ return;
50
+ shown = true;
51
+ const url = found[0];
52
+ const code = login.userCode?.(new URL(url));
53
+ step.waits(code ? { url, code } : login.withCode ? { url, takesAddress: true } : { url });
54
+ };
55
+ child.stdout.on("data", hear);
56
+ child.stderr.on("data", hear);
57
+ const exited = exitOf(child);
58
+ const handed = new AbortController();
59
+ const finished = await Promise.race([
60
+ exited.then((code) => ({ code })),
61
+ ...(login.withCode
62
+ ? [
63
+ step
64
+ .address(AbortSignal.any([step.signal, handed.signal]))
65
+ .then((code) => ({ handedCode: code.trim() }))
66
+ .catch(() => new Promise(() => { })),
67
+ ]
68
+ : []),
69
+ ]);
70
+ handed.abort();
71
+ if ("handedCode" in finished) {
72
+ child.kill();
73
+ const args = login.withCode(finished.handedCode);
74
+ const code = await exitOf(spawn(login.bin, args, { cwd, stdio: ["ignore", "inherit", "inherit"] }));
75
+ if (code !== 0)
76
+ return `\`${login.bin}\` refused the code the approval page showed — try the deploy again`;
77
+ }
78
+ else if (finished.code !== 0) {
79
+ return `\`${login.bin} ${login.unattended.join(" ")}\` did not sign in (exit ${finished.code}): ${said.trim()}`;
80
+ }
81
+ step.person.carriesOn();
82
+ return undefined;
83
+ };
84
+ }
85
+ function exitOf(child) {
86
+ return new Promise((resolve) => {
87
+ child.on("error", () => resolve(127));
88
+ child.on("close", (code) => resolve(code ?? 1));
89
+ });
90
+ }