@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
@@ -6,27 +6,82 @@ import { readFile } from "node:fs/promises";
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { basename, join, relative } from "node:path";
8
8
  import { isModelSpec, isReleaseAgentName } from "./workspace.js";
9
- import { providerOf } from "../engines/pi/config.js";
10
- import { resolveAuthPath } from "../engines/pi/auth.js";
11
9
  import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
12
- import { AGENT_MODEL_CATALOG_FILE, AGENT_MODELS_FILE, exists } from "../paths.js";
13
- import { inspectChannels } from "../channels/discover.js";
14
- import { loadRoutines } from "../schedule/discover.js";
15
- import { resolveAgentTools } from "../engines/pi/create.js";
16
- import { loadAgentDefinition } from "../engines/pi/definition.js";
17
- import { agentModels } from "../engines/pi/agent-models.js";
18
- import { allSecrets } from "../declared-secrets.js";
19
- import { createPiModelRuntime, definitionKeyOf, environmentAuthSource, literalKeyProviders, isBuiltinProvider, machineModels, globalCatalogPath, inGlobalCatalog, interactiveLoginKind, loginProviders, } from "../engines/pi/models.js";
10
+ import { resolveAuthPath } from "../harnesses/models/credentials.js";
11
+ import { environmentAuth, interactiveLoginKind, loginProviders, modelsOver, providerOf, } from "../harnesses/models/models.js";
12
+ import { modelEnvironment } from "../harnesses/models/environment.js";
13
+ import { buildDefinitionShape, definitionSecrets, prepareDefinition, } from "../harnesses/pi-durable/open.js";
14
+ import { LOCAL_CONTEXT_FILE, exists } from "../paths.js";
15
+ import { channelIngress } from "../channels/discover.js";
16
+ import { loadContent, loadEnvironment } from "../content/file.js";
20
17
  import { CHANNEL_KINDS } from "../scaffold/add-channel.js";
21
- import { detectRuntime, readPackageJson } from "../runtime.js";
18
+ import { RUNTIME_FLOOR, belowFloor, detectRuntime, listsFastagent, readPackageJson } from "../runtime.js";
22
19
  import { fastagentVersion } from "../version.js";
23
20
  import { isGeneratedDockerfile } from "./container.js";
24
21
  import { buildContextPaths, checkKeptIgnoreFiles } from "./build-context.js";
25
- import { CRON_CAN_BE_EXTERNAL, residencyFor } from "./residency.js";
26
- import { dotEnvPath, loadEnvValues } from "../env.js";
22
+ import { dotEnvPath, loadEnvValues } from "../value-file.js";
27
23
  import { deploymentSecrets, isEnvKey } from "./secrets.js";
28
- import { DEFAULT_HTTP_PORT, describeAnonymousSurface, shouldServeRun } from "../service.js";
29
- import { CONTROL_PREFIX } from "../channels/control.js";
24
+ import { DEFAULT_HTTP_PORT } from "../service.js";
25
+ import { declaredVariables, declaresTool, runtimeAdvice } from "../environment/declare.js";
26
+ import { checkPackages, MISE_PACKAGE_NAMES, miseBinary, packageSpecs } from "../environment/mise.js";
27
+ /**
28
+ * What each content entry becomes on the host, one line each, so none is missing there unsaid (agent-model.md §3). A
29
+ * repository `context.json` declares is cloned there, as on this machine without a checkout bound. A directory only
30
+ * this machine binds stays here: the deployed agent works without it, which the line says and says how to change. One
31
+ * the agent works on is a warning, since the deployed agent lacks data it was meant to work on; one it only knows, a
32
+ * note.
33
+ */
34
+ function checkContent({ declared, bound }, storageResets, report) {
35
+ for (const entry of declared) {
36
+ const fate = storageResets
37
+ ? "cloned afresh on every deployment, since the host's storage starts over; what the agent did not push is lost"
38
+ : "cloned on the host, and brought up to date in place at each start";
39
+ report.note(`${roleOf(entry)} ${entry.name}: github ${entry.repo}${entry.ref ? `@${entry.ref}` : ""}, ${fate}`);
40
+ }
41
+ for (const binding of bound) {
42
+ if (binding.kind !== "local")
43
+ continue;
44
+ const stays = `${roleOf(binding)} ${binding.name}: a directory of this machine (${binding.path}, bound in ` +
45
+ `${LOCAL_CONTEXT_FILE}), stays here, and the deployed agent works without it`;
46
+ if (binding.readonly) {
47
+ report.note(`${stays}; to ship what the agent reads there, copy it into the agent directory, which every release carries`);
48
+ }
49
+ else {
50
+ report.warn(`${stays}; to work on it from a host, move it to a GitHub repository and add it as github:owner/repo`);
51
+ }
52
+ }
53
+ }
54
+ function roleOf(entry) {
55
+ return entry.readonly ? "knows" : "works on";
56
+ }
57
+ /**
58
+ * What the image needs to install the packages the agent declares: the agent's own mise for linux in its package.json,
59
+ * so the image's install brings it, packages FastAgent installs from, and a PyPI package's runtime. A machine without
60
+ * the agent's mise cannot check them, which is an issue (a warning without `--run`), as a code input that cannot load
61
+ * is.
62
+ */
63
+ async function checkEnvironment(agentDir, packages, hasPackageJson, pkg, report) {
64
+ if (Object.keys(packages).length === 0)
65
+ return;
66
+ const linux = MISE_PACKAGE_NAMES.filter((name) => name.includes("-linux-"));
67
+ const missing = linux.filter((name) => !(name in (pkg.optionalDependencies ?? {})));
68
+ if (!hasPackageJson || missing.length > 0) {
69
+ report.issue(`context.json declares packages, and the image would have no mise to install them with: the agent's ` +
70
+ `package.json does not list ${missing.join(", ")}. Run \`fastagent env install\` in the agent directory.`);
71
+ return;
72
+ }
73
+ const bin = miseBinary(agentDir);
74
+ if (!bin) {
75
+ report.issue(`the agent's mise is not installed here, so the packages context.json declares cannot be checked. Run ` +
76
+ `\`fastagent env install\` in ${agentDir}, then deploy again.`);
77
+ return;
78
+ }
79
+ const { refusals, runtime } = await checkPackages(agentDir, bin, packages);
80
+ for (const refusal of refusals)
81
+ report.issue(refusal);
82
+ if (runtime)
83
+ report.issue(runtimeAdvice(runtime));
84
+ }
30
85
  /** The pre-flight's one early exit: thrown by a check, turned into `{ ok: false, gate }` by {@link preflightDeploy}. */
31
86
  class DeployGate {
32
87
  text;
@@ -56,7 +111,7 @@ export async function preflightDeploy(input) {
56
111
  }
57
112
  }
58
113
  async function gatherFacts(input, report) {
59
- const { placement: { agentDir, workspace }, config, force, externalClock, publicUrl = true, } = input;
114
+ const { agentDir, config, force, noResidentProcess, storageResets = false, servesApi = false } = input;
60
115
  // The release manifest carries this name into the container, where it is joined onto the storage root — so `init`'s
61
116
  // "one path segment" is not enough here.
62
117
  if (!isReleaseAgentName(basename(agentDir))) {
@@ -64,18 +119,26 @@ async function gatherFacts(input, report) {
64
119
  `may use only letters, digits, "-" and "_"; rename it (the fastagent.config.ts inside is what ` +
65
120
  `makes it an agent, never its name)`);
66
121
  }
67
- const agentPrefix = `${basename(agentDir)}/`;
68
- // The definition the box will load on every start, loaded here first: a refusal in it (a leftover persona.md, a
69
- // skill named with a slash) builds a perfectly good image that crash-loops on fly/railway/docker and fails every
122
+ const content = loadContent(agentDir);
123
+ checkContent(content, storageResets, report);
124
+ // The definition the box will load on every start, read here first: a refusal in it (a module that fails to import,
125
+ // two tools under one name) builds a perfectly good image that crash-loops on fly/railway/docker and fails every
70
126
  // invocation on AgentCore. This is the boundary that turns that refusal into a deploy finding, so the error is
71
127
  // carried whole into the issue rather than rethrown.
72
- await loadAgentDefinition(agentDir, { cwd: workspace }).catch((error) => {
73
- report.issue(`the definition does not load, so the deployed agent would not start: ${error instanceof Error ? error.message : String(error)}`);
128
+ const definition = await prepareDefinition(agentDir).catch((error) => {
129
+ report.issue(`the definition does not load, so the deployed agent would not start: ${messageOf(error)}`);
130
+ return undefined;
74
131
  });
75
132
  // The model this deployment will run on, and where it came from. Resolved HERE so the plan side and the run side
76
133
  // cannot disagree about it.
77
- const valueFile = relative(workspace, dotEnvPath(agentDir));
134
+ const valueFile = relative(agentDir, dotEnvPath(agentDir));
78
135
  const values = loadEnvValues(dotEnvPath(agentDir));
136
+ // A host has no credential of the author's: what it clones with is GITHUB_TOKEN, which travels like every value.
137
+ if (content.declared.length > 0 && !values.get("GITHUB_TOKEN")) {
138
+ report.note(`no GITHUB_TOKEN in ${valueFile}: the host clones without a credential, which reaches public repositories ` +
139
+ `only, and the agent cannot push from its clones. Set one there to give it access; it travels with the ` +
140
+ `other values`);
141
+ }
79
142
  const model = resolveDeployModel(config, values, valueFile);
80
143
  if (model.invalid !== undefined) {
81
144
  // A gate rather than a warning even without `--run`: the release manifest validates the spec on the way out, so
@@ -99,32 +162,12 @@ async function gatherFacts(input, report) {
99
162
  report.note(`model ${model.spec} (source: ${model.source})`);
100
163
  }
101
164
  const modelSpec = model.spec;
102
- // Time triggers (static routines or self-scheduling) need a machine kept running, and a declared schedule also
103
- // decides whether `POST /run` mounts — so the load happens HERE, before the warning that has to name it.
104
- // Loaded, not just listed: the same load answers "are there time triggers" AND "what did they declare they need".
105
- // A file that FAILED to load still counts as a trigger — the author will fix it, and a plan that scaled to zero
106
- // because a cron was broken on deploy day would sleep through it afterwards.
107
- const loadedRoutines = await loadRoutines(agentDir);
108
- // What the PUBLIC host URL answers with no authentication of ours, NAMED FROM WHAT WILL ACTUALLY MOUNT (through the
109
- // same `shouldServeRun` the assembly uses), never from the host alone: listing an endpoint this deployment does not
110
- // serve is how an operator learns to skim past every deploy warning — the same reason `publicUrl` exists.
111
- const unauthenticated = describeAnonymousSurface({
112
- invoke: config.http?.invoke !== false,
113
- run: loadedRoutines.routines.length > 0 && shouldServeRun(config.http),
114
- ...(config.sessionControl === true ? { controlPrefix: CONTROL_PREFIX } : {}),
115
- });
116
- if (publicUrl && unauthenticated.length > 0) {
117
- report.warn(`the deployed box answers ${unauthenticated.join(" and ")} at its public URL, UNAUTHENTICATED — ` +
118
- `fastagent authenticates nothing. Put a gateway, an IdP-backed proxy or a private network in front of that ` +
119
- `URL (docs/design/session-control.md §14)`);
120
- }
121
- // Known channel kinds only — a custom channel's webhook (and, unless it declared them, its secrets) are unknown to
122
- // us; note and let the author wire them.
123
- const inspected = await inspectChannels(agentDir);
124
- if (inspected.failures.length > 0) {
125
- throw new Error(`cannot inspect channel modules: ${inspected.failures.map((failure) => `${failure.label}: ${failure.message}`).join("; ")}`);
126
- }
127
- const channels = inspected.channels;
165
+ const declaredSecrets = definition ? definitionSecrets(definition) : declaredVariables(loadEnvironment(agentDir));
166
+ const code = definition ? buildShapes(definition, values, report) : undefined;
167
+ const channels = [...(code?.channels ?? [])].map(([name, module]) => ({
168
+ name,
169
+ ingress: channelIngress(module),
170
+ }));
128
171
  for (const { name, ingress } of channels) {
129
172
  if (CHANNEL_KINDS.includes(name))
130
173
  continue;
@@ -134,54 +177,34 @@ async function gatherFacts(input, report) {
134
177
  : `route channel "${name}" is custom — ${secretsPart}; configure its webhook yourself`);
135
178
  }
136
179
  const longConnectionChannels = channels.filter((c) => c.ingress === "long-connection").map((c) => c.name);
137
- // A ROUTINE IS NOT A CRON. `cron` is a field, so counting routine FILES answered a different question: a
138
- // definition whose only routine is reached by name (`POST /run`) would pin one machine up forever and print a
139
- // note about a cron instant it does not have. `deploy agentcore` already filtered the same way when it turned
140
- // routines into EventBridge rules; this is the other reader of that fact, and they must agree.
141
- //
142
- // A FAILED file still counts, on the conservative side: it may well declare a cron, and a plan that scaled to
143
- // zero because the file did not parse would hide that behind silence.
144
- const hasCron = loadedRoutines.routines.some((r) => r.cron !== undefined) || loadedRoutines.failures.length > 0;
145
- if (longConnectionChannels.length > 0 && !externalClock) {
180
+ // Counted from the registry when the code did not build: a plan that scaled to zero because a schedule failed to
181
+ // build on deploy day would sleep through it once it is fixed.
182
+ const hasCron = (code?.schedules.length ?? definition?.registry.schedules.length ?? 0) > 0;
183
+ if (longConnectionChannels.length > 0 && !noResidentProcess) {
146
184
  report.note(`long-connection channel present (${longConnectionChannels.join(", ")}) — a GENERATED plan keeps one machine running ` +
147
185
  `(an outbound connection cannot wake a scaled-to-zero service).`);
148
186
  }
149
- // A cron has an external substitute, and an operator who is paying for an idle box should be told so — unless a
150
- // long connection pins the box anyway, which residency.ts decides.
151
- if (hasCron && !externalClock) {
152
- const wayOut = residencyFor({ channels, hasCron })?.reason === CRON_CAN_BE_EXTERNAL;
153
- report.note(`routines/ present — a GENERATED plan keeps one machine running (nothing wakes this box at a cron ` +
154
- `instant).` +
155
- (wayOut
156
- ? ` To scale to zero instead, keep the time in a scheduler you own and let it call ` +
157
- `\`POST /run\` (an API that runs one declared unit of work by name — docs/api-reference.md#post-run).`
158
- : ""));
187
+ if (hasCron && !noResidentProcess) {
188
+ report.note(`src/schedules/ present — a GENERATED plan keeps one machine running (nothing wakes this box at a cron instant).`);
159
189
  }
160
- await checkMachineModels(agentDir, modelSpec, report);
161
- // The registry the DEPLOYED agent has (its own models.json and model catalog, nothing of the machine's), built first
162
- // so a malformed file stops the deploy with pi's own reason before anything below reads the file raw. No credential
163
- // is read for that.
164
- const deployed = await createPiModelRuntime({
165
- agentDir,
166
- credentials: new InMemoryCredentialStore(),
167
- machineLayer: false,
168
- });
169
- if (modelSpec)
170
- await checkGlobalCatalogModel(modelSpec, deployed, report);
171
- const authPath = resolveAuthPath(agentDir);
172
- // A model only the definition's extensions declare (a virtual model, or a provider one registers) is authenticated
173
- // by that code on the box, per request: a virtual model's credential is whichever physical model it routes to.
174
- // Asked of the catalog the box builds, extensions included, so deploy judges the model the box will run.
175
- const fromExtension = modelSpec ? await extensionDeclared(agentDir, workspace, modelSpec, deployed) : false;
176
- if (fromExtension && modelSpec) {
177
- report.note(`${modelSpec} is declared by the definition's extensions, which resolve its credentials on the box — deploy ` +
178
- `cannot check them. Set the keys the models it uses need in ${valueFile}`);
190
+ if (code !== undefined && channels.length === 0 && !hasCron && !servesApi) {
191
+ report.warn(`no channel in src/channels/, no schedule in src/schedules/, and no Agent API on this deployment: nothing ` +
192
+ `reaches it but its health check. Add a channel (\`fastagent add telegram|slack|feishu|lark\`, or your ` +
193
+ `own) or a schedule; an agent used only from an app is embedded in it (openAgent), not deployed`);
179
194
  }
180
- const route = modelSpec && !fromExtension ? await credentialRoute(agentDir, modelSpec, values) : {};
195
+ const providers = code?.providers ?? [];
196
+ const authPath = resolveAuthPath(agentDir);
197
+ const route = modelSpec ? await credentialRoute(modelSpec, values, providers) : {};
181
198
  const modelAuth = route.envVar;
182
199
  const boxLogin = route.boxLogin;
200
+ if (modelSpec && code) {
201
+ await checkModelExists(agentDir, modelSpec, providers, report, {
202
+ credentialShips: boxLogin === undefined,
203
+ valueFile,
204
+ });
205
+ }
183
206
  if (boxLogin !== undefined) {
184
- if (!hasLogin(boxLogin)) {
207
+ if (!hasLogin(boxLogin, providers)) {
185
208
  report.issue(`no credential for ${modelSpec} reaches the deployment, and "${boxLogin}" has no login to run there — set ` +
186
209
  `its API key in ${valueFile}`);
187
210
  }
@@ -191,39 +214,36 @@ async function gatherFacts(input, report) {
191
214
  `(\`fastagent login ${boxLogin} --deployment\`). Or set ${boxLogin}'s API key in ${valueFile}`);
192
215
  }
193
216
  }
194
- // Reported, not refused. Whether a string is a credential is the AUTHOR's knowledge: pi's docs prescribe
195
- // `"apiKey": "ollama"` for a keyless local server, and no static rule separates that from a leaked key. The
196
- // `.dockerignore` check next to this one IS a gate because it catches a packing rule putting FastAgent's OWN
197
- // `.secrets/auth.json` into the image — the framework's doing. This is the author's own committed file.
198
- // Asked of EVERY provider, not just the selected model's: the file ships whole.
199
- const literalKeys = await literalKeyProviders(agentDir);
200
- if (literalKeys.length > 0) {
201
- report.warn(`${AGENT_MODELS_FILE} carries a literal apiKey for ${literalKeys.map((id) => `"${id}"`).join(", ")} — that ` +
202
- `file ships inside the image, where anyone who can pull it reads the layer. If it is a credential, use ` +
203
- `"$YOUR_ENV_VAR" (deploy carries it like any provider key) or "!command" (it runs on the box and never ` +
204
- `travels); a placeholder for a keyless local server is fine as it is.`);
205
- }
206
217
  // Container facts (shared by every host) + the warnings that follow.
207
218
  const hasPackageJson = await exists(join(agentDir, "package.json"));
208
219
  const pkg = await readPackageJson(agentDir);
209
220
  const { runtime, bunVersion, hasLockfile } = detectRuntime(agentDir, pkg);
221
+ const kept = await keptDockerfile(agentDir);
222
+ // Only the generated image runs the pinned Bun: a hand-written Dockerfile names its own base.
223
+ if (kept === undefined &&
224
+ runtime === "bun" &&
225
+ bunVersion !== undefined &&
226
+ belowFloor(bunVersion, RUNTIME_FLOOR.bun)) {
227
+ report.issue(`package.json pins bun@${bunVersion}, which the image runs (oven/bun:${bunVersion}), and the agent's storage is ` +
228
+ `node:sqlite, which Bun ships from ${RUNTIME_FLOOR.bun}: the container would fail at start. Pin ` +
229
+ `bun@${RUNTIME_FLOOR.bun} or later`);
230
+ }
210
231
  const install = runtime === "bun" ? "bun install" : "npm install";
211
232
  const runner = runtime === "bun" ? "bun run fastagent" : "./node_modules/.bin/fastagent";
212
233
  const hasOtherLock = runtime === "node" &&
213
234
  ((await exists(join(agentDir, "pnpm-lock.yaml"))) || (await exists(join(agentDir, "yarn.lock"))));
214
- // Does the baked workspace ship a `.git`?
215
- const shipsGit = await exists(join(workspace, ".git"));
235
+ // Does the baked agent directory ship a `.git`?
236
+ const shipsGit = await exists(join(agentDir, ".git"));
216
237
  // After the facts: the deps sentence must match the agent's actual shape (a markdown-only agent has no package.json
217
238
  // and installs nothing — the note must not point at a file that doesn't exist).
218
239
  const deps = hasPackageJson
219
- ? `only the agent's deps (${agentPrefix}package.json) are installed — the workspace's own deps are the agent's runtime concern`
240
+ ? `its package.json dependencies are installed in the image`
220
241
  : `the agent has no package.json, so no deps are installed (the pinned global CLI serves the directory)`;
221
242
  // What a RELEASE does — host-neutral, because how long the storage under it lives is the host's own answer and its
222
243
  // runbook gives it (a Fly volume outlives every deploy; AgentCore's mount does not).
223
- report.note(`the whole directory is baked as the agent's workspace (WYSIWYG — what you see is what ships, ` +
224
- `git or not, clean or not); ${deps}. The image seeds the storage once; a later release replaces ` +
225
- `only ${agentPrefix} and leaves the rest of the workspace, state and credentials in place — for ` +
226
- `how long, see this host's storage note below`);
244
+ report.note(`the agent directory is baked as the definition (WYSIWYG — what you see is what ships, git or not, clean or ` +
245
+ `not); ${deps}. Every release replaces the deployed definition and leaves the instance's state and ` +
246
+ `credentials in place — for how long, see this host's storage note below`);
227
247
  // A code agent with no lockfile builds via a non-frozen install (ranges resolve at build time) — not reproducible.
228
248
  if (hasPackageJson && !hasLockfile) {
229
249
  const lock = runtime === "bun" ? "bun.lock" : "package-lock.json";
@@ -234,17 +254,31 @@ async function gatherFacts(input, report) {
234
254
  `Run \`${install}\` and commit the lockfile for pinned redeploys.`);
235
255
  }
236
256
  // The code-path Dockerfile runs `${runner}`.
237
- if (hasPackageJson && !("@fastagent-sh/fastagent" in { ...pkg.dependencies, ...pkg.devDependencies })) {
257
+ if (hasPackageJson && !listsFastagent(pkg)) {
238
258
  report.warn(`package.json does not list @fastagent-sh/fastagent — the image's \`${runner}\` has no local bin to run, ` +
239
259
  `so the container fails at start. Add it to dependencies and re-run \`${install}\`.`);
240
260
  }
241
- const paths = await buildContextPaths(workspace, agentDir, agentPrefix, authPath);
242
- await checkKeptIgnoreFiles({ workspace, agentDir, agentPrefix, force, paths }, report);
243
- // Write-back mechanics are fastagent's (the policy is the agent's prompt's).
244
- const apt = shipsGit ? [...new Set(["git", ...(config.deploy?.apt ?? [])])] : config.deploy?.apt;
261
+ const paths = await buildContextPaths(agentDir, authPath);
262
+ await checkKeptIgnoreFiles({ agentDir, force, paths }, report);
263
+ const environment = loadEnvironment(agentDir);
264
+ const packages = environment?.packages ?? {};
265
+ await checkEnvironment(agentDir, packages, hasPackageJson, pkg, report);
266
+ const installsPackages = Object.keys(packages).length > 0;
267
+ // Write-back mechanics are fastagent's (the policy is the agent's prompt's). A repository content entry is cloned on the
268
+ // host, which takes git too. mise downloads over TLS, and the slim base image has no CA certificates; rustup, which
269
+ // mise installs rust with, downloads with curl. The agent's own apt packages join them, in one layer.
270
+ const needsGit = shipsGit || content.declared.length > 0;
271
+ const apt = [
272
+ ...new Set([
273
+ ...(needsGit ? ["git"] : []),
274
+ ...(installsPackages ? ["ca-certificates"] : []),
275
+ ...(declaresTool(packages, "rust") ? ["curl"] : []),
276
+ ...(environment?.aptPackages ?? []),
277
+ ]),
278
+ ];
245
279
  const container = {
246
280
  releaseId: randomUUID(),
247
- agentPrefix,
281
+ agent: basename(agentDir),
248
282
  machineryPaths: paths.machineryPaths,
249
283
  hasPackageJson,
250
284
  runtime,
@@ -252,30 +286,13 @@ async function gatherFacts(input, report) {
252
286
  hasLockfile,
253
287
  version: await fastagentVersion(),
254
288
  apt,
289
+ ...(installsPackages ? { packages: packageSpecs(packages) } : {}),
255
290
  ...(model.envValue !== undefined ? { modelSpec: model.envValue } : {}),
256
291
  shipsGit,
257
292
  };
258
293
  const port = config.http?.port ?? DEFAULT_HTTP_PORT;
259
- // EVERYTHING the definition declared it needs, from wherever it was declared. Read through the SAME resolver
260
- // dev/start mount with, so "which tool declarations count" has one answer (config.tools declare too; a shadowed
261
- // file's declaration is dropped in both places).
262
- const resolvedTools = await resolveAgentTools(config, agentDir, workspace);
263
- // A code input we could not READ is a code input whose declarations we cannot carry — and the box
264
- // WILL read it (its deps are installed there), so its gate fires after the deploy reported success:
265
- // a crash loop, which is the failure mode this whole mechanism exists to move to build time. Under
266
- // `--run` that is a gate, like a channel that fails to inspect; generate-only warns, since the
267
- // operator may be producing artifacts from a machine that never installed the agent's deps.
268
- for (const failure of [...resolvedTools.toolFailures, ...loadedRoutines.failures]) {
269
- const issue = `${failure.label} failed to load (${failure.message}) — any secrets it declares cannot be carried ` +
270
- `to the host, so the deployed box would refuse to start`;
271
- report.issue(issue);
272
- }
273
- const declaredSecrets = [
274
- ...allSecrets(resolvedTools.toolSecrets),
275
- ...allSecrets(loadedRoutines.secrets),
276
- ...allSecrets(inspected.secrets),
277
- ];
278
- await checkKeptDockerfile(agentDir, config, model.envValue, valueFile, report);
294
+ if (kept !== undefined)
295
+ checkKeptDockerfile(kept, agentDir, model.envValue, valueFile, report);
279
296
  return {
280
297
  channels,
281
298
  hasCron,
@@ -289,84 +306,89 @@ async function gatherFacts(input, report) {
289
306
  secrets: deploymentSecrets(modelAuth, declaredSecrets, values, valueFile),
290
307
  };
291
308
  }
309
+ /**
310
+ * The definition's code built for its SHAPE with the value file's values (`buildDefinitionShape`): a generate-only
311
+ * deploy runs on a machine that may hold none of them, and whether the value file supplies them is the values gate's
312
+ * question, asked by name. A build that fails is an issue, as a module that fails to load is.
313
+ */
314
+ function buildShapes(definition, values, report) {
315
+ try {
316
+ return buildDefinitionShape(definition, Object.fromEntries(values));
317
+ }
318
+ catch (error) {
319
+ report.issue(`the definition does not build, so the deployed agent would not start: ${messageOf(error)}`);
320
+ return undefined;
321
+ }
322
+ }
323
+ function messageOf(error) {
324
+ return error instanceof Error ? error.message : String(error);
325
+ }
326
+ /**
327
+ * A model no provider of the deployed agent offers fails every message there. Its lists are this machine's: what it
328
+ * kept and what it fetches now, a provider's own list with whatever credential this machine's environment holds. A
329
+ * model only a fetched list has is the box's only once the box fetches that list, which a box with nothing kept does as
330
+ * it opens: pi's published catalog with no credential, a provider's own list with the one this deploy carries, since a
331
+ * login on the box comes after the start it would refuse. Whether a model is only in a provider's own list cannot be
332
+ * told here: a provider in code keeps what it fetched in its own object, which every `Models` over it then lists.
333
+ */
334
+ async function checkModelExists(agentDir, spec, providers, report, box) {
335
+ const provider = providerOf(spec);
336
+ const modelId = spec.slice(provider.length + 1);
337
+ const { models } = await modelEnvironment(agentDir, { credentialStore: new InMemoryCredentialStore() }, providers);
338
+ if (models.getModel(provider, modelId)) {
339
+ if (models.getProvider(provider)?.refreshModels !== undefined) {
340
+ if (box.credentialShips)
341
+ return;
342
+ report.warn(`provider "${provider}" fetches its own model list, which a box with no list kept, as on a first deploy, ` +
343
+ `fetches as it starts, with a credential this deploy does not carry (a login on the box comes after the ` +
344
+ `start): if "${spec}" is only in that list, the box refuses to start on it until ${provider}'s API key is ` +
345
+ `in ${box.valueFile}`);
346
+ return;
347
+ }
348
+ if (modelsOver(new InMemoryCredentialStore(), providers).getModel(provider, modelId))
349
+ return;
350
+ report.warn(`"${spec}" is in pi's published catalog, not in the pi-ai this FastAgent carries: a box with no catalog kept, ` +
351
+ `as on a first deploy, fetches it from pi.dev as it starts, and refuses to start on "${spec}" if it cannot`);
352
+ return;
353
+ }
354
+ report.issue(`no provider of the deployed agent offers "${spec}" — pi-ai's built-ins, pi's published catalog and the ` +
355
+ `providers fastagent.config.ts declares are all it has`);
356
+ }
292
357
  /**
293
358
  * HOW THE MODEL'S CREDENTIAL REACHES THE BOX, from what the deploy ships and nothing else. The box is the one authority
294
359
  * on what it authenticates with (pi ranks a stored login above the environment, and only the box knows what it has
295
360
  * stored, which platform variables it was given, or what role it runs as), so this never asks what authenticates the
296
361
  * model on THIS machine: its stored logins and the shell running deploy do not travel. In order:
297
362
  *
298
- * 1. the definition's own models.json: a `"$NAME"` reference is a variable the value file must hold (the values gate
299
- * asks for it by name), and a literal or `"!command"` travels in the image;
300
- * 2. a credential pi reads for the provider from the value file alone: its key variable (which the values gate then
301
- * requires, as it does every declared name), or a keyless one such as `AWS_ACCESS_KEY_ID` — never one that needs a
302
- * file, which would be this machine's;
303
- * 3. otherwise the box answers — `boxLogin`: after readiness, `--run` asks it, and it logs in only if it cannot
363
+ * 1. a credential the provider reads from the value file alone (a definition's provider included, which may resolve
364
+ * its key from a secret it declared): its key variable (which the values gate then requires, as it does every
365
+ * declared name), or a keyless one such as `AWS_ACCESS_KEY_ID` — never one that needs a file, which would be this
366
+ * machine's;
367
+ * 2. otherwise the box answers — `boxLogin`: after readiness, `--run` asks it, and it logs in only if it cannot
304
368
  * already authenticate the provider.
305
369
  */
306
- async function credentialRoute(agentDir, spec, values) {
370
+ async function credentialRoute(spec, values, providers) {
307
371
  const provider = providerOf(spec);
308
- const declared = await definitionKeyOf(agentDir, provider);
309
- if (declared)
310
- return "reference" in declared ? { envVar: declared.reference } : {};
311
372
  // No file of this machine's travels, so a source that needs one (Google ADC, an AWS profile) is the box's to find.
312
- const fromValues = await environmentAuthSource(provider, Object.fromEntries(values), async () => false);
313
- if (fromValues !== undefined)
314
- return isEnvKey(fromValues) ? { envVar: fromValues } : {};
373
+ const fromValues = await environmentAuth(provider, Object.fromEntries(values), async () => false, providers);
374
+ if (fromValues !== undefined) {
375
+ return fromValues.source !== undefined && isEnvKey(fromValues.source) ? { envVar: fromValues.source } : {};
376
+ }
315
377
  return { boxLogin: provider };
316
378
  }
317
- /** Whether `spec` exists only once the definition's extensions have registered their models. */
318
- async function extensionDeclared(agentDir, workspace, spec, deployed) {
319
- const provider = providerOf(spec);
320
- const id = spec.slice(provider.length + 1);
321
- if (deployed.getModel(provider, id))
322
- return false;
323
- const catalog = await agentModels(agentDir, { credentialStore: new InMemoryCredentialStore() }, { machineLayer: false, cwd: workspace }).runtime();
324
- return catalog.getModel(provider, id) !== undefined;
325
- }
326
379
  /** Does `provider` offer an interactive login, i.e. can `fastagent login --deployment` authenticate it on the box? */
327
- function hasLogin(providerId) {
328
- const provider = loginProviders().find((p) => p.id === providerId);
380
+ function hasLogin(providerId, providers) {
381
+ const provider = loginProviders(providers).find((p) => p.id === providerId);
329
382
  return provider !== undefined && interactiveLoginKind(provider) !== "none";
330
383
  }
331
- /**
332
- * The machine's models.json is this box's environment, not the artifact: whatever the model takes from it is
333
- * absent wherever the agent is deployed. Said in so many words here; the credential probe already reads the deployed
334
- * registry.
335
- */
336
- async function checkMachineModels(agentDir, modelSpec, report) {
337
- const machine = await machineModels(agentDir);
338
- const provider = modelSpec ? providerOf(modelSpec) : undefined;
339
- if (provider === undefined || !machine?.inherited.includes(provider))
340
- return;
341
- if (isBuiltinProvider(provider)) {
342
- report.warn(`model "${modelSpec}" takes its "${provider}" entry from ${machine.path}, the machine's models.json, which ` +
343
- `does not ship — the deployed agent runs pi's built-in "${provider}" without it. Declare the entry in the ` +
344
- `agent's own ${AGENT_MODELS_FILE} to deploy it.`);
345
- }
346
- else {
347
- // No built-in to fall back on: the deployed agent cannot resolve the model at all.
348
- const issue = `model "${modelSpec}" exists only in ${machine.path}, the machine's models.json, which does not ship — the ` +
349
- `deployed agent would fail with an unknown model. Declare "${provider}" in the agent's own ` +
350
- `${AGENT_MODELS_FILE} to deploy it.`;
351
- report.issue(issue);
352
- }
384
+ /** The agent's hand-written Dockerfile, which the deploy keeps; undefined when there is none, or it is the generated one. */
385
+ async function keptDockerfile(agentDir) {
386
+ const path = join(agentDir, "Dockerfile");
387
+ const text = (await exists(path)) ? await readFile(path, "utf8") : undefined;
388
+ return text === undefined || isGeneratedDockerfile(text) ? undefined : text;
353
389
  }
354
390
  /**
355
- * The machine's model catalog is this box's environment, like its models.json: a model only it knows (newer than the
356
- * catalog bundled with pi) is unknown wherever the agent is deployed. The agent's own catalog ships, so the remedy is
357
- * one refresh in the agent.
358
- */
359
- async function checkGlobalCatalogModel(modelSpec, deployed, report) {
360
- const provider = providerOf(modelSpec);
361
- const id = modelSpec.slice(provider.length + 1);
362
- if (deployed.getModel(provider, id) || !(await inGlobalCatalog(provider, id)))
363
- return;
364
- report.issue(`model "${modelSpec}" is known only from ${globalCatalogPath()}, the machine's model catalog, which does not ` +
365
- "ship — the deployed agent would fail with an unknown model. Run `fastagent models --refresh` in the agent " +
366
- `to record it in its own ${AGENT_MODEL_CATALOG_FILE}, which ships.`);
367
- }
368
- /**
369
- * What a KEPT hand-written Dockerfile drops. `deploy.apt` is the obvious one; the resolved model is the one that
391
+ * What a KEPT hand-written Dockerfile drops. The environment (context.json's) is the obvious one; the resolved model is the one that
370
392
  * looks safe and is not: the manifest is always written, but only the generated Dockerfile sets
371
393
  * FASTAGENT_RELEASE_FILE, and without it `prepareStartWorkspace` never reads the manifest — so a model that lives
372
394
  * ONLY in the value file would be reported here and absent on the box.
@@ -375,16 +397,15 @@ async function checkGlobalCatalogModel(modelSpec, deployed, report) {
375
397
  * hand-written Dockerfile survives `--force` and drops exactly the same things. Short-circuiting here let
376
398
  * `--run --force` ship the crash-loop this gate exists to stop.
377
399
  */
378
- async function checkKeptDockerfile(agentDir, config,
400
+ function checkKeptDockerfile(dockerfileText, agentDir,
379
401
  /** The model the release manifest carries (set only when the value file named it). */
380
402
  modelFromValueFile, valueFile, report) {
381
- const dockerfileHome = join(agentDir, "Dockerfile");
382
- const dockerfileText = (await exists(dockerfileHome)) ? await readFile(dockerfileHome, "utf8") : undefined;
383
- if (dockerfileText === undefined || isGeneratedDockerfile(dockerfileText))
384
- return;
385
- if (config.deploy?.apt?.length) {
386
- report.warn(`kept your hand-written Dockerfile — deploy.apt (${config.deploy.apt.join(", ")}) is ` +
387
- `NOT applied; install those packages in your Dockerfile.`);
403
+ // What the image installs: the packages and apt packages. Variables travel with the value file, whatever the image.
404
+ const environment = loadEnvironment(agentDir);
405
+ if (Object.keys(environment?.packages ?? {}).length > 0 || (environment?.aptPackages.length ?? 0) > 0) {
406
+ report.warn(`kept your hand-written Dockerfile — it installs the environment context.json declares only if it says so: ` +
407
+ `a generated Dockerfile shows the layers (the apt packages, then \`mise install --system\` of the packages ` +
408
+ `context.json declares).`);
388
409
  }
389
410
  // The INSTRUCTION is the question, not the file's authorship: `prepareStartWorkspace` returns early without
390
411
  // FASTAGENT_RELEASE_FILE, so a Dockerfile that sets it reads the manifest whoever wrote it. This gates rather
@@ -20,19 +20,21 @@ export interface RailwayPlanInput extends ContainerInput {
20
20
  * carries.
21
21
  */
22
22
  secrets?: readonly DeploymentSecret[];
23
- /** `routines/` declares a cron — one of the things that forbids App Sleeping (deploy/residency.ts). */
23
+ /** `schedules/` declares a schedule — one of the things that forbids App Sleeping (deploy/residency.ts). */
24
24
  hasCron: boolean;
25
25
  }
26
26
  export interface RailwayPlan {
27
- /** railway.json / Dockerfile / .dockerignore — written by the CLI (skipped if present unless --force). */
27
+ /**
28
+ * Dockerfile / .dockerignore — written by the CLI (skipped if present unless --force). No Railway config file: Railway
29
+ * builds from a root `Dockerfile` and restarts on failure by default, and the `/health` check that `railway.json`
30
+ * carried is the `--run` driver's own public probe now (Railway retires `railway.json`, and its replacement,
31
+ * `.railway/railway.ts`, would make every agent install Railway's SDK).
32
+ */
28
33
  artifacts: Artifact[];
29
34
  /** The ordered, values-resolved deploy runbook — printed to stdout for the coding agent to execute. */
30
35
  runbook: string[];
31
36
  }
32
- /** The `RAILWAY_DOCKERFILE_PATH` value for an agent under `prefix`. */
33
- export declare const dockerfilePathVar: (prefix: string) => string;
34
- /** The name this tool gives BOTH the project and the service, derived from the workspace directory. */
37
+ /** The name this tool gives BOTH the project and the service, derived from the agent directory's name. */
35
38
  export declare function toRailwayName(basename: string): string;
36
- export declare function isGeneratedRailwayJson(content: string): boolean;
37
39
  /** Compute the Railway deploy plan from the resolved definition. */
38
40
  export declare function planRailwayDeploy(input: RailwayPlanInput): RailwayPlan;