@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,55 +1,26 @@
1
1
  import { webhookRunbook } from "../channel-ingress.js";
2
- import { containerArtifacts } from "../container.js";
2
+ import { containerArtifacts, imageHasGit } from "../container.js";
3
3
  import { deploymentLoginCommand } from "../box-shell.js";
4
- import { CRON_CAN_BE_EXTERNAL, WAKEUPS_WHEN_ASLEEP, residencyFor } from "../residency.js";
4
+ import { WAKEUPS_WHEN_ASLEEP, residencyFor } from "../residency.js";
5
5
  /** State root = the volume mount path, kept in lockstep. */
6
6
  const MOUNT = "/data";
7
- /** The `RAILWAY_DOCKERFILE_PATH` value for an agent under `prefix`. */
8
- export const dockerfilePathVar = (prefix) => `/${prefix}Dockerfile`;
9
- /** The name this tool gives BOTH the project and the service, derived from the workspace directory. */
7
+ /** The name this tool gives BOTH the project and the service, derived from the agent directory's name. */
10
8
  export function toRailwayName(basename) {
11
9
  return basename.replace(/[^a-zA-Z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
12
10
  }
13
- /** railway.json is JSON, so its ownership marker is a KEY rather than a comment line. */
14
- const GENERATED_RAILWAY_KEY = "x-generated-by";
15
- const GENERATED_RAILWAY_VALUE = "fastagent deploy railway";
16
- export function isGeneratedRailwayJson(content) {
17
- try {
18
- return JSON.parse(content)[GENERATED_RAILWAY_KEY] === GENERATED_RAILWAY_VALUE;
19
- }
20
- catch {
21
- return false;
22
- }
23
- }
24
- /** railway.json — build/deploy only (Railway's config-as-code scope). */
25
- function railwayJson(prefix) {
26
- return `${JSON.stringify({
27
- $schema: "https://railway.com/railway.schema.json",
28
- [GENERATED_RAILWAY_KEY]: GENERATED_RAILWAY_VALUE,
29
- // dockerfilePath is relative to the workspace root (`railway up`'s upload context).
30
- build: { builder: "DOCKERFILE", dockerfilePath: `${prefix}Dockerfile` },
31
- deploy: { healthcheckPath: "/health", restartPolicyType: "ON_FAILURE" },
32
- }, null, 2)}\n`;
33
- }
34
11
  /** Compute the Railway deploy plan from the resolved definition. */
35
12
  export function planRailwayDeploy(input) {
36
13
  const { serviceName, channels } = input;
37
- // railway.json is namespaced under the agent dir too (the workspace may carry its own railway.toml/json for the
38
- // product).
39
- const configPath = `${input.agentPrefix}railway.json`;
40
- const artifacts = [
41
- { path: configPath, content: railwayJson(input.agentPrefix) },
42
- ...containerArtifacts(input),
43
- ];
14
+ const artifacts = containerArtifacts(input);
44
15
  const secrets = input.secrets ?? [];
45
16
  // Order matters, not cosmetics: `railway init` creates a PROJECT with no service, but the volume and variables are
46
17
  // service-scoped and `railway up` deploys THE service.
47
18
  const runbook = [
48
- `# Deploy to Railway. ${configPath} / Dockerfile(.dockerignore) are generated above.`,
19
+ `# Deploy to Railway. Dockerfile(.dockerignore) are generated above.`,
49
20
  `# Prereqs: the Railway CLI (https://docs.railway.com/guides/cli) and \`railway login\`.`,
50
21
  ``,
51
22
  `# One-time setup (init → service → volume → variables). Skip it on redeploy: repeating it creates`,
52
- `# another project/service/volume, splitting the persistent workspace.`,
23
+ `# another project/service/volume, splitting the agent's state.`,
53
24
  ``,
54
25
  `# Create + link a project (writes .railway link state in this dir; the project — not a committed`,
55
26
  `# file — is Railway's source of truth for identity, variables, and the volume).`,
@@ -64,22 +35,21 @@ export function planRailwayDeploy(input) {
64
35
  `railway volume add --mount-path ${MOUNT}`,
65
36
  ``,
66
37
  `# Variables — set BEFORE the first deploy so the box boots with them. Railway injects PORT itself.`,
67
- `# RAILWAY_DOCKERFILE_PATH points the build at the agent's Dockerfile — a service variable,`,
68
- `# Railway's documented route to a non-root Dockerfile (no dashboard step needed for the build).`,
69
- `railway variables set FASTAGENT_STATE_DIR=${MOUNT}/.state FASTAGENT_SECRETS_DIR=${MOUNT}/.secrets RAILWAY_DOCKERFILE_PATH=${dockerfilePathVar(input.agentPrefix)}`,
38
+ `railway variables set FASTAGENT_STATE_DIR=${MOUNT}/.state FASTAGENT_SECRETS_DIR=${MOUNT}/.secrets`,
70
39
  ];
71
40
  if (secrets.length > 0) {
72
41
  runbook.push(`# Secrets:`, `# ${secrets.map((s) => `${s.name}: ${s.hint}`).join("\n# ")}`, `railway variables set ${secrets.map((s) => `${s.name}=<value>`).join(" ")}`);
73
42
  }
74
- runbook.push(``, `# OPTIONAL — the build already uses the agent's Dockerfile via RAILWAY_DOCKERFILE_PATH (set above),`, `# and Railway's default restart policy equals what ${configPath} declares (ON_FAILURE).`, `# Pointing the service at ${configPath} (Service → Settings → Config-as-code — dashboard-only) adds`, `# the /health healthcheck gate: a boot-crashing deploy is marked FAILED instead of going live dead.`, `# (Zero-downtime switching doesn't apply either way — the ${MOUNT} volume allows one active deployment.)`);
75
- runbook.push(``, `# Before a new definition release, run \`fastagent deploy railway\` to refresh the release manifest.`, `# Upload this workspace and build on Railway (no local Docker needed):`, `railway up`);
43
+ runbook.push(``, `# Before a new definition release, run \`fastagent deploy railway\` to refresh the release manifest.`, `# Upload the agent directory and build on Railway (no local Docker needed): Railway builds the`, `# Dockerfile at its root. It marks the deploy live once the container starts, without waiting for`, `# /health: \`railway logs\` shows a box that crashes on boot.`, `railway up`);
76
44
  // The credential is created on the box, never carried: the box is then the only holder of its grant.
77
45
  if (input.boxLogin) {
78
- runbook.push(``, `# Model auth: once it is up, the deployment logs in to ${input.boxLogin} itself (from this workspace):`, `${deploymentLoginCommand("railway", input.boxLogin)}`);
46
+ runbook.push(``, `# Model auth: once it is up, the deployment logs in to ${input.boxLogin} itself (from the agent directory):`, `${deploymentLoginCommand("railway", input.boxLogin)}`);
79
47
  }
80
- runbook.push(``, `# The volume keeps /data/base (including uncommitted work), .state and .secrets across restarts and deploys.`, `# Each generated release replaces only /data/base/${input.agentPrefix}; other workspace files are initialized once.`, input.shipsGit
81
- ? `# Railway uploads may strip .git. Clone inside the persistent workspace when collaboration needs history.`
82
- : `# To use Git for collaboration, add deploy: { apt: ["git"] }. Storage durability does not require Git.`);
48
+ runbook.push(``, `# The volume keeps .state and .secrets across restarts and deploys; each release replaces /data/definition.`, input.shipsGit
49
+ ? `# Railway uploads may strip .git, so the deployed definition may have no history.`
50
+ : imageHasGit(input)
51
+ ? `# Git is installed (github content is cloned here, or context.json declares it in aptPackages).`
52
+ : `# To give the agent git, declare it: fastagent env add apt:git`);
83
53
  // The public URL is minted, not deterministic (unlike Fly's <app>.fly.dev).
84
54
  const steps = webhookRunbook(`https://<your-domain>`, channels);
85
55
  if (steps.length > 0) {
@@ -90,19 +60,6 @@ export function planRailwayDeploy(input) {
90
60
  const residency = residencyFor({ channels, hasCron: input.hasCron });
91
61
  runbook.push(``, residency
92
62
  ? `# Scale-to-zero: do NOT enable App Sleeping — ${residency.why}.`
93
- : `# Scale-to-zero (optional, dashboard-only — no CLI/API): Settings → Deploy → Serverless → App Sleeping —\n# ${WAKEUPS_WHEN_ASLEEP}.`, ...(residency?.reason === CRON_CAN_BE_EXTERNAL
94
- ? [
95
- `# To sleep anyway: keep the time in a Railway CRON SERVICE (Settings -> Cron Schedule, >= 5 min) that`,
96
- `# calls this service's \`POST /run\` over the private network — traffic from another service in the`,
97
- `# project wakes a slept one. A cron service must EXIT, so it cannot be this service.`,
98
- `# set a variable on the cron service and curl it (Railway resolves the reference at deploy):`,
99
- `# AGENT_RUN=http://${serviceName}.railway.internal:\${{${serviceName}.PORT}}/run`,
100
- `# curl --retry 3 -fsS -X POST "$AGENT_RUN" -H 'content-type: application/json' -d '{"name":"<routine>"}'`,
101
- `# --retry because the FIRST request to a slept service may answer 502 (Railway documents it). The`,
102
- `# route has no dedup, so decide for yourself whether a retry that may duplicate work is what you want.`,
103
- `# \`POST /run\` is an API, not a clock: read its contract — docs/api-reference.md#post-run.`,
104
- `# Asleep, ${WAKEUPS_WHEN_ASLEEP}.`,
105
- ]
106
- : []), `# Keep this a SINGLE service: the ${MOUNT} volume is tied to one service; extra replicas split state.`);
63
+ : `# Scale-to-zero (optional, dashboard-only — no CLI/API): Settings → Deploy → Serverless → App Sleeping —\n# ${WAKEUPS_WHEN_ASLEEP}.`, `# Keep this a SINGLE service: the ${MOUNT} volume is tied to one service; extra replicas split state.`);
107
64
  return { artifacts, runbook };
108
65
  }
@@ -15,19 +15,24 @@ export interface RailwayRunPlan {
15
15
  secrets: Record<string, string>;
16
16
  /** Declared names the value file supplies no value for — the run gates on these before any side effect. */
17
17
  missingSecrets: string[];
18
- /** That value file, workspace-relative, so the gate names the file this deploy actually read. */
18
+ /** That value file, agent-dir-relative, so the gate names the file this deploy actually read. */
19
19
  valueFile: string;
20
20
  /** Every declared channel and its ingress — the driver asks which of them have a webhook. */
21
21
  channels: readonly DeclaredChannel[];
22
22
  /** Opt-in (CLI `--into-linked`) to provision INTO the project this directory is already linked to. */
23
23
  intoLinked: boolean;
24
- /** `RAILWAY_DOCKERFILE_PATH` value (`/fastagent/Dockerfile`). */
25
- dockerfilePath: string;
26
24
  /**
27
25
  * Log the box in (`fastagent login --deployment`) once it is up and before any webhook is pointed at it: a channel pointed at
28
26
  * a box with no model credential answers every message with a failure. Resolves a gate line, or undefined.
29
27
  */
30
28
  boxLogin?: BoxLoginStep;
29
+ /**
30
+ * Sign the host's CLI in when it is signed out, through whoever can (host-login.ts): resolves a gate line, or
31
+ * undefined once signed in. Absent: nobody can, and the run stops naming how.
32
+ */
33
+ hostLogin?: () => Promise<string | undefined>;
34
+ /** The command a coding agent runs to deploy this, its person's steps handed to its user (`--run --json`). */
35
+ forAgent: string;
31
36
  }
32
37
  /**
33
38
  * Done (with the live URL), or a gate the operator must clear before re-running (printed + non-zero exit by the CLI).
@@ -1,6 +1,6 @@
1
1
  import { loginGate, publicHealthGate, registerWebhooks, } from "../channel-ingress.js";
2
2
  import { readOutput } from "../runner.js";
3
- import { missingValuesGate } from "../secrets.js";
3
+ import { hostSignedOutGate, missingValuesGate } from "../secrets.js";
4
4
  /** Whether `railway status --json` shows a linked project: non-empty stdout. */
5
5
  export function isLinked(stdout) {
6
6
  return stdout.trim() !== "";
@@ -77,14 +77,19 @@ export async function deployRailwayRun(plan, railway, log, registrars, healthPro
77
77
  // Every --service below targets plan.name — the name this tool gives BOTH the project and the service (`init
78
78
  // --name` + `add --service`).
79
79
  const svc = ["--service", plan.name];
80
- // 1.
81
- if ((await railway(["whoami"], { capture: true })).code !== 0) {
82
- return gate("not logged in to Railway — run `railway login`, or set RAILWAY_API_TOKEN (an account token), then re-run");
83
- }
84
- // 2. Gate missing required secret VALUES before any side effect (no half-created infra).
80
+ // 1. Gate missing required secret VALUES before any side effect (no half-created infra), and before asking a
81
+ // person to sign in for a deploy that would stop here anyway.
85
82
  const missingValues = missingValuesGate(plan.missingSecrets, plan.valueFile);
86
83
  if (missingValues)
87
84
  return gate(missingValues);
85
+ // 2.
86
+ if ((await railway(["whoami"], { capture: true })).code !== 0) {
87
+ const failed = plan.hostLogin
88
+ ? await plan.hostLogin()
89
+ : hostSignedOutGate("Railway", "railway login", "RAILWAY_API_TOKEN (an account token)", plan.forAgent);
90
+ if (failed)
91
+ return gate(failed);
92
+ }
88
93
  // 3.
89
94
  const status = await railway(["status", "--json"], { capture: true });
90
95
  if (isLinked(status.stdout)) {
@@ -124,7 +129,6 @@ export async function deployRailwayRun(plan, railway, log, registrars, healthPro
124
129
  const machineryVars = [
125
130
  `FASTAGENT_STATE_DIR=${plan.mountPath}/.state`,
126
131
  `FASTAGENT_SECRETS_DIR=${plan.mountPath}/.secrets`,
127
- `RAILWAY_DOCKERFILE_PATH=${plan.dockerfilePath}`,
128
132
  ];
129
133
  // The secret NAMES, not a count: `--run` uploads the whole value file, so the operator has to be able to see the
130
134
  // list on every host.
@@ -153,7 +157,7 @@ export async function deployRailwayRun(plan, railway, log, registrars, healthPro
153
157
  return gate("`railway volume add` failed — see the railway output above");
154
158
  }
155
159
  // Deploying before the volume is attached boots a container with no volume, which `start` refuses (it must
156
- // never seed a workspace onto a disk that vanishes on restart).
160
+ // never publish a definition onto a disk that vanishes on restart).
157
161
  let status;
158
162
  for (let waited = 0;; waited += VOLUME_POLL_MS) {
159
163
  const read = await volumeStatus();
@@ -4,9 +4,8 @@
4
4
  * A host may only phrase its own REMEDY — the setting it owns (`min_machines_running`, App Sleeping) — never
5
5
  * re-derive the reason.
6
6
  *
7
- * ORDER IS THE POINT, not just precedence: the reason reported has to be the one an operator cannot work around,
8
- * because the message it produces differs. A declared schedule has a substitute, because the clock does not have to
9
- * be ours — see {@link CRON_CAN_BE_EXTERNAL}; a long connection has none.
7
+ * A schedule is the agent's own clock: it keeps one machine up, and nothing here offers a way around that. Scaling to
8
+ * zero with timed work means not declaring the schedule and keeping the time elsewhere (docs/deploy.md), never both.
10
9
  *
11
10
  * A WAKE-UP IS NOT A REASON. Every serve mounts `wake`, so counting it would pin every deployment up. It does not
12
11
  * need to: the wake-up store is on the volume and the poll drains what is due when the process starts, so a box that
@@ -15,23 +14,18 @@
15
14
  */
16
15
  import type { DeclaredChannel } from "../channels/discover.ts";
17
16
  /** Why one machine has to stay up. Also the order they are checked in. */
18
- export type ResidencyReason = "long-connection" | "cron";
17
+ type ResidencyReason = "long-connection" | "cron";
19
18
  export interface Residency {
20
19
  reason: ResidencyReason;
21
20
  /** The host-neutral cause, one clause, for a generated comment or a runbook line. */
22
21
  why: string;
23
22
  }
24
- /**
25
- * The ONE reason a caller may offer a way out of: a cron is a TIME, and a time can be kept elsewhere — a platform
26
- * scheduler, a CI cron, a crontab — which then calls this agent. Nothing else here can be moved out: a long
27
- * connection is one this process holds.
28
- */
29
- export declare const CRON_CAN_BE_EXTERNAL: ResidencyReason;
30
23
  /** What scaling to zero costs the agent's own wake-ups — said next to every setting that allows it. */
31
24
  export declare const WAKEUPS_WHEN_ASLEEP = "the agent's own wake-ups then fire when a request next wakes the machine, not on time";
32
25
  /** What forbids scale-to-zero for this deployment, or `undefined` when nothing does. */
33
26
  export declare function residencyFor(facts: {
34
27
  channels: readonly DeclaredChannel[];
35
- /** `routines/` declares at least one cron. */
28
+ /** `schedules/` declares at least one schedule. */
36
29
  hasCron: boolean;
37
30
  }): Residency | undefined;
31
+ export {};
@@ -1,9 +1,3 @@
1
- /**
2
- * The ONE reason a caller may offer a way out of: a cron is a TIME, and a time can be kept elsewhere — a platform
3
- * scheduler, a CI cron, a crontab — which then calls this agent. Nothing else here can be moved out: a long
4
- * connection is one this process holds.
5
- */
6
- export const CRON_CAN_BE_EXTERNAL = "cron";
7
1
  /** What scaling to zero costs the agent's own wake-ups — said next to every setting that allows it. */
8
2
  export const WAKEUPS_WHEN_ASLEEP = "the agent's own wake-ups then fire when a request next wakes the machine, not on time";
9
3
  /** What forbids scale-to-zero for this deployment, or `undefined` when nothing does. */
@@ -29,7 +29,7 @@ export declare function readOutput<T>(run: CliRunner, bin: string, args: string[
29
29
  gate: string;
30
30
  }>;
31
31
  /**
32
- * Production {@link CliRunner}: spawn `bin` in `cwd` (the workspace, so a build/upload context is the agent). stderr
32
+ * Production {@link CliRunner}: spawn `bin` in `cwd` (the agent directory, which is the build/upload context). stderr
33
33
  * is always inherited to the terminal.
34
34
  */
35
35
  export declare function spawnRunner(bin: string, cwd: string): CliRunner;
@@ -24,7 +24,7 @@ export async function readOutput(run, bin, args, read) {
24
24
  }
25
25
  }
26
26
  /**
27
- * Production {@link CliRunner}: spawn `bin` in `cwd` (the workspace, so a build/upload context is the agent). stderr
27
+ * Production {@link CliRunner}: spawn `bin` in `cwd` (the agent directory, which is the build/upload context). stderr
28
28
  * is always inherited to the terminal.
29
29
  */
30
30
  export function spawnRunner(bin, cwd) {
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * The environment a deployed agent runs with — host-neutral.
3
3
  *
4
- * The value file (`.secrets/.env`) IS the deployed environment: every variable in it travels, on every host. What the
5
- * definition declares (`defineTool`/`defineChannel`/`defineRoutine({ secrets })`, plus the model's env key) only says
4
+ * The production value file (`.secrets/production/.env`) IS the deployed environment: every variable in it travels, on every host. What the
5
+ * definition declares (`defineTool`/`defineChannel`, the `variables` of `context.json`, plus the model's env key) only says
6
6
  * which of those must have a value, so a missing one stops a deploy before its first side effect instead of a boot.
7
7
  */
8
8
  import { type DeclaredSecret } from "../declared-secrets.ts";
9
9
  /** Is this local auth source an env-var API key (→ becomes a deploy secret) vs OAuth / stored / none? */
10
10
  export declare function isEnvKey(source: string | undefined): source is string;
11
+ /** Is `name` one the deployment sets itself (see {@link DEPLOY_OWNED}), including the chunked carriers? */
12
+ export declare function isDeployOwned(name: string): boolean;
11
13
  /** One variable a runbook tells the operator to set, and where to look for its value. */
12
14
  export interface DeploymentSecret {
13
15
  name: string;
@@ -38,12 +40,24 @@ export declare function assembleSecrets(input: {
38
40
  secrets: Record<string, string>;
39
41
  missingSecrets: string[];
40
42
  };
43
+ /**
44
+ * The stop for a host CLI signed out with nobody to sign it in. A token goes in the shell's (or CI's) environment,
45
+ * never in the production value file: everything there travels to the box, which runs an agent with a shell.
46
+ */
47
+ export declare function hostSignedOutGate(host: string, login: string, token: string, forAgent: string): string;
41
48
  /**
42
49
  * The ONE refusal for declared names the deployed environment does not supply a value for — every host reaches it
43
50
  * before its first side effect. Host-neutral because the reason is: the value file IS the deployed environment, so a
44
51
  * name missing from it is missing from the deployment, whatever platform receives it.
45
52
  */
46
53
  export declare function missingValuesGate(missing: readonly string[], valueFile: string): string | undefined;
54
+ /**
55
+ * The refusal for a setting that shapes what `deploy` plans (a channel's ingress, read when `deploy` imports the
56
+ * channel here) and differs between this process and the value file. The box imports the channel with the file's
57
+ * value, so planning with this one would deploy a different channel than the one planned for: an export in this shell
58
+ * is the usual cause.
59
+ */
60
+ export declare function divergentSettingsGate(names: readonly string[], env: NodeJS.ProcessEnv, values: ReadonlyMap<string, string>, valueFile: string): string | undefined;
47
61
  /**
48
62
  * Expand `FASTAGENT_ENV` into the process environment — what `start` does first on a host that carries the value file
49
63
  * that way. The platform's own variables still win, as they do over a value file anywhere.
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * The environment a deployed agent runs with — host-neutral.
3
3
  *
4
- * The value file (`.secrets/.env`) IS the deployed environment: every variable in it travels, on every host. What the
5
- * definition declares (`defineTool`/`defineChannel`/`defineRoutine({ secrets })`, plus the model's env key) only says
4
+ * The production value file (`.secrets/production/.env`) IS the deployed environment: every variable in it travels, on every host. What the
5
+ * definition declares (`defineTool`/`defineChannel`, the `variables` of `context.json`, plus the model's env key) only says
6
6
  * which of those must have a value, so a missing one stops a deploy before its first side effect instead of a boot.
7
7
  */
8
8
  import { dedupeSecrets } from "../declared-secrets.js";
9
+ import { DEV_SERVE_ENV } from "../serving-command.js";
10
+ import { AGENT_ENVIRONMENT_ENV } from "../paths.js";
9
11
  /** Is this local auth source an env-var API key (→ becomes a deploy secret) vs OAuth / stored / none? */
10
12
  export function isEnvKey(source) {
11
13
  return source !== undefined && /^[A-Z][A-Z0-9_]*$/.test(source);
@@ -16,7 +18,6 @@ export function isEnvKey(source) {
16
18
  */
17
19
  const DEPLOY_OWNED = new Set([
18
20
  "PORT",
19
- "FASTAGENT_AGENT",
20
21
  "FASTAGENT_MODEL",
21
22
  "FASTAGENT_STATE_DIR",
22
23
  "FASTAGENT_SECRETS_DIR",
@@ -27,9 +28,11 @@ const DEPLOY_OWNED = new Set([
27
28
  "FASTAGENT_INGRESS_SECRET",
28
29
  "FASTAGENT_WAKE_SECRET",
29
30
  "FASTAGENT_DEV_WORKER",
31
+ DEV_SERVE_ENV,
32
+ AGENT_ENVIRONMENT_ENV,
30
33
  ]);
31
34
  /** Is `name` one the deployment sets itself (see {@link DEPLOY_OWNED}), including the chunked carriers? */
32
- function isDeployOwned(name) {
35
+ export function isDeployOwned(name) {
33
36
  return DEPLOY_OWNED.has(name) || /^FASTAGENT_ENV(_\d+)?$/.test(name);
34
37
  }
35
38
  /** The value-file variables that travel: everything with a value that the deployment does not set itself. */
@@ -52,8 +55,9 @@ export function deploymentSecrets(modelAuth, declared, values, valueFile) {
52
55
  };
53
56
  if (isEnvKey(modelAuth))
54
57
  add(modelAuth, "your model provider key");
55
- for (const { name, source } of dedupeSecrets(declared))
56
- add(name, `required by ${source}`);
58
+ for (const { name, source, description } of dedupeSecrets(declared)) {
59
+ add(name, `required by ${source}${description ? `: ${description}` : ""}`);
60
+ }
57
61
  for (const name of Object.keys(carriedValues(values)))
58
62
  add(name, `from ${valueFile}`);
59
63
  return secrets;
@@ -79,6 +83,15 @@ export function assembleSecrets(input) {
79
83
  }
80
84
  return { secrets, missingSecrets };
81
85
  }
86
+ /**
87
+ * The stop for a host CLI signed out with nobody to sign it in. A token goes in the shell's (or CI's) environment,
88
+ * never in the production value file: everything there travels to the box, which runs an agent with a shell.
89
+ */
90
+ export function hostSignedOutGate(host, login, token, forAgent) {
91
+ return (`not logged in to ${host} — run \`${login}\` in a terminal (it opens a browser), or set ${token} in this ` +
92
+ `shell's environment, never in .secrets/production/.env (everything there travels to the box); then re-run. ` +
93
+ `From a coding agent, \`${forAgent}\` hands the sign-in to its user`);
94
+ }
82
95
  /**
83
96
  * The ONE refusal for declared names the deployed environment does not supply a value for — every host reaches it
84
97
  * before its first side effect. Host-neutral because the reason is: the value file IS the deployed environment, so a
@@ -90,6 +103,20 @@ export function missingValuesGate(missing, valueFile) {
90
103
  return (`no value for: ${missing.join(", ")} — the deployed environment is declared by ${valueFile}, and this deploy ` +
91
104
  `reads only that file (exporting the variable here does not reach the deployment). Add them there and re-run`);
92
105
  }
106
+ /**
107
+ * The refusal for a setting that shapes what `deploy` plans (a channel's ingress, read when `deploy` imports the
108
+ * channel here) and differs between this process and the value file. The box imports the channel with the file's
109
+ * value, so planning with this one would deploy a different channel than the one planned for: an export in this shell
110
+ * is the usual cause.
111
+ */
112
+ export function divergentSettingsGate(names, env, values, valueFile) {
113
+ const differing = names.filter((name) => (env[name] ?? "") !== (values.get(name) ?? ""));
114
+ if (differing.length === 0)
115
+ return undefined;
116
+ const said = differing.map((name) => `${name} is "${env[name] ?? ""}" here and "${values.get(name) ?? ""}" in ${valueFile}`);
117
+ return (`${said.join("; ")} — the deployment receives only ${valueFile}, and this deploy would plan for a different ` +
118
+ `channel than the one it starts. Set it in ${valueFile} (or unset it here) and re-run`);
119
+ }
93
120
  /**
94
121
  * Collect a (possibly CHUNKED) carrier from the environment: `<name>` plus numbered continuations (`_2`, `_3`, …)
95
122
  * concatenated in order. A host whose env values have a length cap splits a long value this way.
@@ -1,4 +1,6 @@
1
1
  export declare const RELEASE_FILE = "fastagent.release.json";
2
+ /** Where the storage holds the deployed definition, the agent's working directory on the host. */
3
+ export declare const DEPLOYED_DEFINITION_DIR = "definition";
2
4
  export interface DeploymentRelease {
3
5
  version: 1;
4
6
  id: string;
@@ -11,14 +13,14 @@ export interface DeploymentRelease {
11
13
  */
12
14
  model?: string;
13
15
  }
14
- /** The manifest names a directory the container joins onto the workspace root, so the spelling is
15
- * constrained. `deploy` asks BEFORE generating artifacts — a name `init` accepts but this rejects
16
- * is the author's directory name, not a corrupt manifest. */
16
+ /** The manifest names the agent the storage belongs to, and that name is also a content entry's name rule (one path
17
+ * segment). `deploy` asks BEFORE generating artifacts — a name `init` accepts but this rejects is the author's
18
+ * directory name, not a corrupt manifest. */
17
19
  export declare function isReleaseAgentName(name: string): boolean;
18
20
  export declare function parseDeploymentRelease(raw: string): DeploymentRelease;
19
21
  /**
20
22
  * Project a release's own declarations into the environment it was resolved FOR — the receiving half of the model
21
- * chain, run by `prepareStartWorkspace` BEFORE the workspace's `.env` is read (so either source outranks a value
23
+ * chain, run by `prepareStartWorkspace` BEFORE the agent's `.env` is read (so either source outranks a value
22
24
  * edited on the box).
23
25
  *
24
26
  * `||=`: a variable the platform already holds still wins, because the deployment declares only the half of this
@@ -29,7 +31,10 @@ export declare function applyReleaseEnv(release: DeploymentRelease, env?: NodeJS
29
31
  /** A `provider/modelId` spec. The id itself may carry `/` and `~` (`baseten/zai-org/GLM-5.3`, openrouter aliases);
30
32
  * resolution only ever splits on the first slash. */
31
33
  export declare function isModelSpec(value: string): boolean;
32
- /** Call while holding the workspace lease. No deployment replaces existing work outside the definition. */
34
+ /**
35
+ * Call while holding the deployment lease. Replaces the deployed definition with the release's (`source`, the image's
36
+ * copy) and returns where it now is; nothing else on the storage is touched.
37
+ */
33
38
  export declare function applyDeploymentRelease(source: string, root: string, release: DeploymentRelease): Promise<string>;
34
39
  /** An unmounted directory must never look like a new disk. */
35
40
  export declare function assertStorageMounted(root: string, paths?: string[]): Promise<void>;
@@ -37,7 +42,7 @@ export declare function assertStorageMounted(root: string, paths?: string[]): Pr
37
42
  * passes a short one rather than waiting out the deployment default. */
38
43
  export declare function leaseDeployment(metadata: string, waitSeconds?: number): Promise<() => Promise<void>>;
39
44
  /**
40
- * Prepare the deployed workspace and return its path.
45
+ * Prepare the deployed definition and return its path.
41
46
  *
42
47
  * The lease is NOT returned: it stays held for the process lifetime, because channel activation can
43
48
  * start background writers that `close()` does not drain. The kernel drops it when the process exits.
@@ -1,19 +1,25 @@
1
- /** Deployment-owned initialization; the workspace itself belongs to the running agent. */
2
- import { cp, lstat, mkdir, readFile, rename, rm } from "node:fs/promises";
1
+ /**
2
+ * Deployment-owned initialization of a host's storage: the lease, the release journal, and the definition each
3
+ * release replaces. The instance's state, credentials and content (`.state/`, `.secrets/`, `content/`) sit beside the
4
+ * definition and outlive every release; each release links its `content/` to the storage's.
5
+ */
6
+ import { cp, lstat, mkdir, readFile, rename, rm, symlink } from "node:fs/promises";
3
7
  import { join, resolve } from "node:path";
4
8
  import { spawn } from "node:child_process";
5
9
  import { closeSync, openSync } from "node:fs";
6
10
  import { once } from "node:events";
7
11
  import { writeFileAtomic } from "../atomic-write.js";
8
12
  import { log } from "../log.js";
9
- import { exists } from "../paths.js";
13
+ import { CONTENT_DIRNAME, exists } from "../paths.js";
10
14
  import { detectRuntime, readPackageJson } from "../runtime.js";
11
15
  export const RELEASE_FILE = "fastagent.release.json";
16
+ /** Where the storage holds the deployed definition, the agent's working directory on the host. */
17
+ export const DEPLOYED_DEFINITION_DIR = "definition";
12
18
  /** The deployment's own bookkeeping on the storage: the lease, the release journal, the install marker. */
13
19
  const deploymentMeta = (root) => join(root, ".deployment");
14
- /** The manifest names a directory the container joins onto the workspace root, so the spelling is
15
- * constrained. `deploy` asks BEFORE generating artifacts — a name `init` accepts but this rejects
16
- * is the author's directory name, not a corrupt manifest. */
20
+ /** The manifest names the agent the storage belongs to, and that name is also a content entry's name rule (one path
21
+ * segment). `deploy` asks BEFORE generating artifacts — a name `init` accepts but this rejects is the author's
22
+ * directory name, not a corrupt manifest. */
17
23
  export function isReleaseAgentName(name) {
18
24
  return /^[a-zA-Z0-9_-]+$/.test(name);
19
25
  }
@@ -31,7 +37,7 @@ export function parseDeploymentRelease(raw) {
31
37
  }
32
38
  /**
33
39
  * Project a release's own declarations into the environment it was resolved FOR — the receiving half of the model
34
- * chain, run by `prepareStartWorkspace` BEFORE the workspace's `.env` is read (so either source outranks a value
40
+ * chain, run by `prepareStartWorkspace` BEFORE the agent's `.env` is read (so either source outranks a value
35
41
  * edited on the box).
36
42
  *
37
43
  * `||=`: a variable the platform already holds still wins, because the deployment declares only the half of this
@@ -47,7 +53,7 @@ export function applyReleaseEnv(release, env = process.env) {
47
53
  return;
48
54
  log.info(`[fastagent] model ${effective} — from ${fromPlatform
49
55
  ? `a FASTAGENT_MODEL already set in this environment${release.model ? ", which outranks the release manifest" : ""}`
50
- : "the release manifest (redeploy to change it)"}; editing FASTAGENT_MODEL in this workspace's .env does not override it`);
56
+ : "the release manifest (redeploy to change it)"}; editing FASTAGENT_MODEL in the deployed agent's .env does not override it`);
51
57
  }
52
58
  /** A `provider/modelId` spec. The id itself may carry `/` and `~` (`baseten/zai-org/GLM-5.3`, openrouter aliases);
53
59
  * resolution only ever splits on the first slash. */
@@ -55,12 +61,12 @@ export function isModelSpec(value) {
55
61
  return /^\S+\/\S+$/.test(value);
56
62
  }
57
63
  /** A completed staging tree is published before its journal; readers start only after recovery. */
58
- async function finishRelease(root, pending) {
64
+ async function finishRelease(root, release) {
59
65
  const meta = deploymentMeta(root), staged = join(meta, "staged"), previous = join(meta, "previous");
60
- const target = pending.initial ? join(root, "base") : join(root, "base", pending.release.agent);
66
+ const target = join(root, DEPLOYED_DEFINITION_DIR);
61
67
  if (await exists(staged)) {
62
68
  if (await exists(target)) {
63
- if (pending.initial || (await exists(previous)))
69
+ if (await exists(previous))
64
70
  throw new Error(`deployment recovery conflict at ${target}`);
65
71
  await rename(target, previous);
66
72
  }
@@ -69,48 +75,59 @@ async function finishRelease(root, pending) {
69
75
  else if (!(await exists(target))) {
70
76
  throw new Error(`deployment recovery has neither staged nor active content: ${target}`);
71
77
  }
72
- writeFileAtomic(join(meta, "applied.json"), JSON.stringify(pending.release));
78
+ writeFileAtomic(join(meta, "applied.json"), JSON.stringify(release));
73
79
  await rm(previous, { recursive: true, force: true });
74
80
  await rm(join(meta, "pending.json"));
75
81
  }
76
- /** Call while holding the workspace lease. No deployment replaces existing work outside the definition. */
82
+ /**
83
+ * Call while holding the deployment lease. Replaces the deployed definition with the release's (`source`, the image's
84
+ * copy) and returns where it now is; nothing else on the storage is touched.
85
+ */
77
86
  export async function applyDeploymentRelease(source, root, release) {
78
87
  const meta = deploymentMeta(root), staged = join(meta, "staged"), pendingPath = join(meta, "pending.json");
79
88
  await mkdir(meta, { recursive: true });
89
+ await mkdir(join(root, CONTENT_DIRNAME), { recursive: true });
90
+ // A storage an earlier FastAgent laid out holds the agent's whole workspace there, which this one would leave
91
+ // behind unread: said, never silently abandoned.
92
+ const formerWorkspace = join(root, "base");
93
+ if (await exists(formerWorkspace)) {
94
+ throw new Error(`${formerWorkspace} holds a workspace from an earlier FastAgent, which no longer copies one to the host — ` +
95
+ `move out what you need and delete it, or deploy onto fresh storage`);
96
+ }
80
97
  if (await exists(pendingPath)) {
81
- const pending = JSON.parse(await readFile(pendingPath, "utf8"));
82
- pending.release = parseDeploymentRelease(JSON.stringify(pending.release));
83
- if (typeof pending.initial !== "boolean")
84
- throw new Error("invalid pending deployment journal");
98
+ const pending = parseDeploymentRelease(await readFile(pendingPath, "utf8"));
85
99
  await finishRelease(root, pending);
86
100
  }
87
101
  const appliedPath = join(meta, "applied.json");
88
102
  const applied = (await exists(appliedPath)) ? parseDeploymentRelease(await readFile(appliedPath, "utf8")) : undefined;
89
- const workspace = join(root, "base");
103
+ const definition = join(root, DEPLOYED_DEFINITION_DIR);
90
104
  if (applied?.agent !== undefined && applied.agent !== release.agent) {
91
- throw new Error(`deployment selects ${release.agent}, but this workspace belongs to ${applied.agent}`);
105
+ throw new Error(`deployment selects ${release.agent}, but this storage belongs to ${applied.agent}`);
92
106
  }
93
107
  // Logged on BOTH paths: rebuilding an image without regenerating the manifest keeps the id, and a
94
108
  // deployment that silently kept the old definition looks identical to one that took the new one.
95
109
  if (applied?.id === release.id) {
96
- log.info(`[fastagent] release ${release.id} is already applied — keeping the workspace's definition`);
97
- return workspace;
110
+ log.info(`[fastagent] release ${release.id} is already applied — keeping the deployed definition`);
111
+ return definition;
112
+ }
113
+ if (applied === undefined && (await exists(definition))) {
114
+ throw new Error(`refusing to initialize over an existing unowned definition: ${definition}`);
115
+ }
116
+ if (!(await exists(source)) || !(await lstat(source)).isDirectory()) {
117
+ throw new Error(`the release must contain its agent directory: ${source}`);
98
118
  }
99
- const initial = applied === undefined;
100
- if (initial && (await exists(workspace)))
101
- throw new Error(`refusing to initialize over an existing unowned workspace: ${workspace}`);
102
- const definition = join(source, release.agent);
103
- if (!(await exists(definition)) || !(await lstat(definition)).isDirectory())
104
- throw new Error(`the release must contain its agent directory: ${definition}`);
105
- log.info(initial
106
- ? `[fastagent] seeding the workspace from release ${release.id}`
107
- : `[fastagent] publishing release ${release.id} over base/${release.agent}`);
119
+ log.info(`[fastagent] publishing release ${release.id} as ${DEPLOYED_DEFINITION_DIR}/`);
108
120
  await rm(staged, { recursive: true, force: true });
109
- await cp(initial ? source : join(source, release.agent), staged, { recursive: true, verbatimSymlinks: true });
110
- const pending = { release, initial };
111
- writeFileAtomic(pendingPath, JSON.stringify(pending));
112
- await finishRelease(root, pending);
113
- return workspace;
121
+ await cp(source, staged, { recursive: true, verbatimSymlinks: true });
122
+ // Where the agent reaches each content entry, on every instance: here, the storage's, which no release replaces.
123
+ await symlink(join("..", CONTENT_DIRNAME), join(staged, CONTENT_DIRNAME)).catch((error) => {
124
+ if (error.code !== "EEXIST")
125
+ throw error;
126
+ throw new Error(`the release holds ${CONTENT_DIRNAME}/, which a deploy keeps out of the image — rebuild it`);
127
+ });
128
+ writeFileAtomic(pendingPath, JSON.stringify(release));
129
+ await finishRelease(root, release);
130
+ return definition;
114
131
  }
115
132
  async function mountedPaths() {
116
133
  const mounts = await readFile("/proc/self/mountinfo", "utf8");
@@ -142,7 +159,7 @@ export async function leaseDeployment(metadata, waitSeconds = 35) {
142
159
  });
143
160
  const [code, signal] = await once(child, "close");
144
161
  if (code !== 0)
145
- throw new Error(`could not acquire workspace lease at ${metadata}: flock ${signal ?? code} ${stderr.trim()}`);
162
+ throw new Error(`could not acquire deployment lease at ${metadata}: flock ${signal ?? code} ${stderr.trim()}`);
146
163
  return async () => {
147
164
  closeSync(fd);
148
165
  };
@@ -152,12 +169,12 @@ export async function leaseDeployment(metadata, waitSeconds = 35) {
152
169
  // A custom base image without util-linux fails every boot; the bare spawn error names neither
153
170
  // the binary nor why a lease needs one.
154
171
  if (error.code === "ENOENT")
155
- throw new Error(`the deployed workspace lease needs the \`flock\` binary (util-linux): ${String(error)}`);
172
+ throw new Error(`the deployment lease needs the \`flock\` binary (util-linux): ${String(error)}`);
156
173
  throw error;
157
174
  }
158
175
  }
159
176
  /**
160
- * Prepare the deployed workspace and return its path.
177
+ * Prepare the deployed definition and return its path.
161
178
  *
162
179
  * The lease is NOT returned: it stays held for the process lifetime, because channel activation can
163
180
  * start background writers that `close()` does not drain. The kernel drops it when the process exits.