@fastagent-sh/fastagent 0.24.3 → 0.25.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 (480) hide show
  1. package/README.md +53 -82
  2. package/dist/agent-api.d.ts +452 -0
  3. package/dist/agent-api.js +201 -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 +77 -0
  9. package/dist/authoring.js +238 -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 +115 -63
  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 +17 -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 +252 -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 +190 -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 +131 -16
  164. package/dist/cli/commands/login.d.ts +31 -2
  165. package/dist/cli/commands/login.js +413 -64
  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 +3 -2
  184. package/dist/cli/login-relay.js +18 -6
  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 +68 -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 +217 -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 +222 -201
  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 +51 -26
  260. package/dist/effect-port.d.ts +4 -4
  261. package/dist/effect-port.js +4 -4
  262. package/dist/environment/apt.d.ts +4 -0
  263. package/dist/environment/apt.js +32 -0
  264. package/dist/environment/declare.d.ts +59 -0
  265. package/dist/environment/declare.js +210 -0
  266. package/dist/environment/mise.d.ts +50 -0
  267. package/dist/environment/mise.js +268 -0
  268. package/dist/feishu.d.ts +2 -2
  269. package/dist/feishu.js +2 -2
  270. package/dist/git.d.ts +13 -0
  271. package/dist/git.js +55 -0
  272. package/dist/harnesses/models/catalog.d.ts +20 -0
  273. package/dist/harnesses/models/catalog.js +254 -0
  274. package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
  275. package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
  276. package/dist/harnesses/models/environment.d.ts +43 -0
  277. package/dist/harnesses/models/environment.js +82 -0
  278. package/dist/{engines/pi → harnesses/models}/login.d.ts +57 -26
  279. package/dist/{engines/pi → harnesses/models}/login.js +187 -79
  280. package/dist/harnesses/models/models.d.ts +83 -0
  281. package/dist/harnesses/models/models.js +158 -0
  282. package/dist/harnesses/pi-durable/agent.d.ts +42 -0
  283. package/dist/harnesses/pi-durable/agent.js +579 -0
  284. package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
  285. package/dist/harnesses/pi-durable/defaults.js +26 -0
  286. package/dist/harnesses/pi-durable/define.d.ts +77 -0
  287. package/dist/harnesses/pi-durable/define.js +28 -0
  288. package/dist/harnesses/pi-durable/entries.d.ts +32 -0
  289. package/dist/harnesses/pi-durable/entries.js +126 -0
  290. package/dist/harnesses/pi-durable/events.d.ts +25 -0
  291. package/dist/harnesses/pi-durable/events.js +238 -0
  292. package/dist/harnesses/pi-durable/ids.d.ts +16 -0
  293. package/dist/harnesses/pi-durable/ids.js +55 -0
  294. package/dist/harnesses/pi-durable/messages.d.ts +41 -0
  295. package/dist/harnesses/pi-durable/messages.js +116 -0
  296. package/dist/harnesses/pi-durable/open.d.ts +88 -0
  297. package/dist/harnesses/pi-durable/open.js +329 -0
  298. package/dist/harnesses/pi-durable/reads.d.ts +30 -0
  299. package/dist/harnesses/pi-durable/reads.js +94 -0
  300. package/dist/harnesses/pi-durable/registry.d.ts +18 -0
  301. package/dist/harnesses/pi-durable/registry.js +56 -0
  302. package/dist/harnesses/pi-durable/release.d.ts +13 -0
  303. package/dist/harnesses/pi-durable/release.js +40 -0
  304. package/dist/harnesses/pi-durable/serve.d.ts +80 -0
  305. package/dist/harnesses/pi-durable/serve.js +156 -0
  306. package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
  307. package/dist/harnesses/pi-durable/sessions.js +47 -0
  308. package/dist/harnesses/pi-durable/storage.d.ts +4 -0
  309. package/dist/harnesses/pi-durable/storage.js +49 -0
  310. package/dist/harnesses/pi-durable/stream.d.ts +20 -0
  311. package/dist/harnesses/pi-durable/stream.js +221 -0
  312. package/dist/harnesses/pi-durable/text.d.ts +30 -0
  313. package/dist/harnesses/pi-durable/text.js +292 -0
  314. package/dist/harnesses/pi-durable/wake.d.ts +4 -0
  315. package/dist/harnesses/pi-durable/wake.js +81 -0
  316. package/dist/http/client.d.ts +6 -0
  317. package/dist/http/client.js +256 -0
  318. package/dist/http/contract.d.ts +541 -0
  319. package/dist/http/contract.js +179 -0
  320. package/dist/http/server.d.ts +34 -0
  321. package/dist/http/server.js +194 -0
  322. package/dist/index.d.ts +17 -4
  323. package/dist/index.js +10 -5
  324. package/dist/lark.d.ts +2 -2
  325. package/dist/lark.js +2 -2
  326. package/dist/locked-file.d.ts +17 -0
  327. package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
  328. package/dist/once.d.ts +6 -0
  329. package/dist/once.js +23 -0
  330. package/dist/paths.d.ts +36 -52
  331. package/dist/paths.js +66 -182
  332. package/dist/runtime.d.ts +25 -2
  333. package/dist/runtime.js +27 -0
  334. package/dist/scaffold/add-channel.d.ts +14 -27
  335. package/dist/scaffold/add-channel.js +82 -180
  336. package/dist/scaffold/init.d.ts +17 -18
  337. package/dist/scaffold/init.js +87 -99
  338. package/dist/scaffold/templates/SYSTEM.md +12 -0
  339. package/dist/scaffold/templates/env.example +4 -10
  340. package/dist/scaffold/templates/fastagent.config.ts +21 -33
  341. package/dist/scaffold/templates/gitignore +8 -1
  342. package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
  343. package/dist/scaffold/templates.d.ts +9 -4
  344. package/dist/scaffold/templates.js +7 -5
  345. package/dist/scaffold/vendor-skill.d.ts +1 -4
  346. package/dist/scaffold/vendor-skill.js +5 -6
  347. package/dist/schedule/cron.d.ts +13 -0
  348. package/dist/schedule/cron.js +52 -0
  349. package/dist/schedule/eventbridge-cron.d.ts +17 -0
  350. package/dist/schedule/eventbridge-cron.js +88 -0
  351. package/dist/schedule/schedule.d.ts +23 -0
  352. package/dist/schedule/schedule.js +34 -0
  353. package/dist/schedule/scheduler.d.ts +34 -33
  354. package/dist/schedule/scheduler.js +251 -137
  355. package/dist/schedule/state.d.ts +4 -14
  356. package/dist/schedule/state.js +7 -7
  357. package/dist/schedule/wake-alarm.d.ts +22 -2
  358. package/dist/schedule/wake-alarm.js +66 -20
  359. package/dist/schedule/wakeups.d.ts +16 -15
  360. package/dist/schedule/wakeups.js +47 -45
  361. package/dist/service.d.ts +56 -169
  362. package/dist/service.js +91 -187
  363. package/dist/serving-command.d.ts +10 -0
  364. package/dist/serving-command.js +16 -0
  365. package/dist/slack.d.ts +1 -1
  366. package/dist/slack.js +1 -1
  367. package/dist/{channels/kit/state.js → state-file.js} +6 -6
  368. package/dist/telegram.d.ts +1 -0
  369. package/dist/telegram.js +1 -0
  370. package/dist/tunnel.d.ts +3 -0
  371. package/dist/tunnel.js +12 -4
  372. package/dist/value-file.d.ts +21 -0
  373. package/dist/value-file.js +108 -0
  374. package/dist/version.d.ts +5 -0
  375. package/dist/version.js +14 -1
  376. package/package.json +18 -28
  377. package/dist/agent.d.ts +0 -106
  378. package/dist/agent.js +0 -21
  379. package/dist/channels/control.d.ts +0 -31
  380. package/dist/channels/control.js +0 -329
  381. package/dist/channels/define-channel.d.ts +0 -39
  382. package/dist/channels/define-channel.js +0 -9
  383. package/dist/channels/feishu/context-buffer.d.ts +0 -35
  384. package/dist/channels/feishu/context-buffer.js +0 -65
  385. package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
  386. package/dist/channels/http.d.ts +0 -23
  387. package/dist/channels/http.js +0 -57
  388. package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
  389. package/dist/channels/slack/context-buffer.d.ts +0 -23
  390. package/dist/channels/slack/context-buffer.js +0 -37
  391. package/dist/channels/sse.d.ts +0 -4
  392. package/dist/channels/sse.js +0 -66
  393. package/dist/cli/commands/chat.d.ts +0 -3
  394. package/dist/cli/commands/chat.js +0 -14
  395. package/dist/cli/commands/routine-run.d.ts +0 -6
  396. package/dist/cli/commands/routine-run.js +0 -46
  397. package/dist/cli/commands/routine.d.ts +0 -25
  398. package/dist/cli/commands/routine.js +0 -122
  399. package/dist/cli/commands/tool.d.ts +0 -1
  400. package/dist/cli/commands/tool.js +0 -57
  401. package/dist/collect.d.ts +0 -63
  402. package/dist/collect.js +0 -90
  403. package/dist/core.d.ts +0 -13
  404. package/dist/core.js +0 -15
  405. package/dist/engines/pi/agent-models.d.ts +0 -83
  406. package/dist/engines/pi/agent-models.js +0 -120
  407. package/dist/engines/pi/agent-session-factory.d.ts +0 -121
  408. package/dist/engines/pi/agent-session-factory.js +0 -517
  409. package/dist/engines/pi/chat.d.ts +0 -3
  410. package/dist/engines/pi/chat.js +0 -8
  411. package/dist/engines/pi/config.d.ts +0 -71
  412. package/dist/engines/pi/config.js +0 -199
  413. package/dist/engines/pi/create.d.ts +0 -170
  414. package/dist/engines/pi/create.js +0 -343
  415. package/dist/engines/pi/definition.d.ts +0 -74
  416. package/dist/engines/pi/definition.js +0 -236
  417. package/dist/engines/pi/entry-images.d.ts +0 -14
  418. package/dist/engines/pi/entry-images.js +0 -23
  419. package/dist/engines/pi/invoke-session.d.ts +0 -18
  420. package/dist/engines/pi/invoke-session.js +0 -403
  421. package/dist/engines/pi/locked-file.d.ts +0 -9
  422. package/dist/engines/pi/machine.d.ts +0 -53
  423. package/dist/engines/pi/machine.js +0 -137
  424. package/dist/engines/pi/models.d.ts +0 -200
  425. package/dist/engines/pi/models.js +0 -533
  426. package/dist/engines/pi/open.d.ts +0 -180
  427. package/dist/engines/pi/open.js +0 -270
  428. package/dist/engines/pi/openai-account-models.d.ts +0 -35
  429. package/dist/engines/pi/openai-account-models.js +0 -116
  430. package/dist/engines/pi/report.d.ts +0 -18
  431. package/dist/engines/pi/report.js +0 -59
  432. package/dist/engines/pi/retry-event.d.ts +0 -6
  433. package/dist/engines/pi/retry-event.js +0 -15
  434. package/dist/engines/pi/service.d.ts +0 -9
  435. package/dist/engines/pi/service.js +0 -14
  436. package/dist/engines/pi/session-builder.d.ts +0 -13
  437. package/dist/engines/pi/session-builder.js +0 -117
  438. package/dist/engines/pi/session-control.d.ts +0 -77
  439. package/dist/engines/pi/session-control.js +0 -908
  440. package/dist/engines/pi/session-effects.d.ts +0 -14
  441. package/dist/engines/pi/session-effects.js +0 -30
  442. package/dist/engines/pi/session-inheritance.d.ts +0 -21
  443. package/dist/engines/pi/session-inheritance.js +0 -204
  444. package/dist/engines/pi/session-markers.d.ts +0 -49
  445. package/dist/engines/pi/session-markers.js +0 -60
  446. package/dist/engines/pi/session-settings.d.ts +0 -67
  447. package/dist/engines/pi/session-settings.js +0 -119
  448. package/dist/engines/pi/session-store.d.ts +0 -69
  449. package/dist/engines/pi/session-store.js +0 -430
  450. package/dist/engines/pi/tool-context.d.ts +0 -45
  451. package/dist/engines/pi/tool-context.js +0 -31
  452. package/dist/engines/pi/tool.d.ts +0 -99
  453. package/dist/engines/pi/tool.js +0 -118
  454. package/dist/engines/pi/turn-kit.d.ts +0 -83
  455. package/dist/engines/pi/turn-kit.js +0 -275
  456. package/dist/engines/pi/wake-tool.d.ts +0 -9
  457. package/dist/engines/pi/wake-tool.js +0 -92
  458. package/dist/loader.d.ts +0 -41
  459. package/dist/loader.js +0 -116
  460. package/dist/node.d.ts +0 -3
  461. package/dist/node.js +0 -4
  462. package/dist/observe.d.ts +0 -6
  463. package/dist/observe.js +0 -47
  464. package/dist/pi.d.ts +0 -25
  465. package/dist/pi.js +0 -15
  466. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  467. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  468. package/dist/schedule/discover.d.ts +0 -17
  469. package/dist/schedule/discover.js +0 -61
  470. package/dist/schedule/routine.d.ts +0 -43
  471. package/dist/schedule/routine.js +0 -32
  472. package/dist/schedule/run.d.ts +0 -55
  473. package/dist/schedule/run.js +0 -146
  474. package/dist/secrets-gate.d.ts +0 -32
  475. package/dist/secrets-gate.js +0 -33
  476. package/dist/session-remote.d.ts +0 -47
  477. package/dist/session-remote.js +0 -460
  478. package/dist/session.d.ts +0 -466
  479. package/dist/session.js +0 -57
  480. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
package/dist/service.js CHANGED
@@ -1,20 +1,19 @@
1
- /** The product, as one call: an agent directory becomes a live service. */
1
+ /** The product, as one call: an opened agent becomes a live service, its channels, schedules and wake-ups. */
2
2
  import * as Cause from "effect/Cause";
3
3
  import * as Effect from "effect/Effect";
4
4
  import * as Exit from "effect/Exit";
5
5
  import * as Fiber from "effect/Fiber";
6
6
  import * as Scope from "effect/Scope";
7
- import { createControlPlane } from "./channels/control.js";
8
- import { createInvokeHandler } from "./channels/http.js";
7
+ import { mountChannels } from "./channels/discover.js";
9
8
  import { text } from "./channels/respond.js";
10
- import { assertCorsOrigins, parseRouteKey, pathUnderPrefix, router } from "./channels/serve.js";
11
- import { loadChannels } from "./channels/discover.js";
12
- import { loadRoutines } from "./schedule/discover.js";
13
- import { createRoutineListHandler, createRunHandler } from "./schedule/run.js";
14
- import { createScheduler } from "./schedule/scheduler.js";
9
+ import { parseRouteKey, router } from "./channels/router.js";
10
+ import { serveHandler, totalFetch } from "./channels/serve.js";
11
+ import { portCleanup } from "./effect-port.js";
12
+ import { API_PREFIX } from "./http/contract.js";
13
+ import { apiHandler, isApiPath } from "./http/server.js";
15
14
  import { log } from "./log.js";
16
- import { refuseBrokenDeclarations } from "./loader.js";
17
- import { gateSecrets } from "./secrets-gate.js";
15
+ import { once } from "./once.js";
16
+ import { createScheduler } from "./schedule/scheduler.js";
18
17
  /** Default wait for a channel's `closed` before reporting it stuck. */
19
18
  const CLOSE_DEADLINE_MS = 5_000;
20
19
  /** One deadline for all connections; a failed close must not hide a sibling that is still running. */
@@ -42,98 +41,23 @@ function closeWithin(runs, names, deadlineMs) {
42
41
  }
43
42
  /** The port a serve binds when neither a flag, `PORT` (start only) nor `http.port` names one. */
44
43
  export const DEFAULT_HTTP_PORT = 8787;
45
- /**
46
- * Does this surface serve `POST /run`, given a routine to run? It FOLLOWS `invoke` unless `run` says otherwise (see
47
- * {@link HttpSurface.run}). ONE function because the assembly mounts the route and `preflightDeploy` names it; a
48
- * default spelled twice is a default that moves once.
49
- */
50
- export function shouldServeRun(http) {
51
- return http?.run ?? http?.invoke !== false;
52
- }
53
- /**
54
- * What an anonymous caller of a port can do, worst first — the ONE wording the startup report and deploy's pre-flight
55
- * both print, each from its own facts (what mounted vs what will mount).
56
- */
57
- export function describeAnonymousSurface(open) {
58
- return [
59
- ...(open.invoke ? ["POST /invoke (run a turn with this agent's tools)"] : []),
60
- ...(open.run ? ["POST /run (run any routine this agent declares; GET /routines lists them)"] : []),
61
- ...(open.controlPrefix ? [`${open.controlPrefix}/* (read, steer or delete any session)`] : []),
62
- ];
63
- }
64
- /**
65
- * The surface this deployment serves: the DATA plane (`POST /invoke`), `GET /health`, and the discovered channels.
66
- *
67
- * `/invoke` is the framework's interface, so it is served whatever channels a definition declares (unless
68
- * `http.invoke: false`). It is RESERVED for the same reason `/control/*` is: a channel taking that path would silently replace the one route every client,
69
- * every doc and the startup line all name. `/health` stays overridable — a probe is the deployment's to shape.
70
- */
71
- export async function routesFor(agentDir, agent, stateRoot, control,
72
- /**
73
- * `http.invoke: false` withholds the data plane. Two callers, two reasons: the AgentCore adapter serves the
74
- * Runtime's `/invocations` contract instead, and an author sets it to leave the channels' signature checks as the
75
- * only way into a public port.
76
- *
77
- * `routines` mounts `POST /run` and `GET /routines` (per {@link shouldServeRun}). Passed in rather than loaded here
78
- * so the routes and the resident clock cannot disagree about which routines exist (`loadServingRoutines`).
79
- */
80
- options = {}) {
81
- const { routes, longConnections, routeChannels, collisions, failures } = await loadChannels(agentDir, {
82
- agent,
83
- stateRoot,
84
- control,
85
- });
86
- for (const c of collisions) {
87
- log.warn(`[fastagent] channel route "${c.route}" (${c.source}) collides with an earlier channel — not mounted`);
88
- }
89
- refuseBrokenDeclarations(failures);
90
- if (collisions.length > 0) {
91
- throw new Error(`${collisions.length} channel route collision(s) — two channels cannot serve one route`);
44
+ /** Mount the channels and the health check. */
45
+ export function routesFor(agent, stateRoot, channels) {
46
+ const { routes, longConnections, routeChannels } = mountChannels(channels, agent, stateRoot);
47
+ for (const key of Object.keys(routes)) {
48
+ if (isApiPath(parseRouteKey(key).path)) {
49
+ throw new Error(`route "${key}" is under ${API_PREFIX}, which the Agent API serves — move it`);
50
+ }
92
51
  }
93
- /**
94
- * Which channel key already serves this path for the method a built-in would answer — the CHANNEL's spelling, not
95
- * ours. A channel may write the method-less `"/invoke"`, and an error naming `"POST /invoke"` sends its author
96
- * grepping their own file for a string that is not in it.
97
- */
98
- const covered = (path, method) => Object.keys(routes).find((key) => {
99
- const entry = parseRouteKey(key);
100
- return entry.path === path && (entry.method === undefined || entry.method === method);
101
- });
102
52
  let ready = longConnections.length === 0;
103
53
  const health = () => (ready ? text("ok\n", 200) : text("starting\n", 503));
104
- // AgentCore serves the data plane through the Runtime's own `/invocations` contract, so the adapter opts out — and
105
- // with no `/invoke` of ours on that surface there is nothing to reserve, which is why the refusal is in here.
106
- const unverified = { ...(covered("/health", "GET") ? {} : { "GET /health": health }) };
107
- if (options.http?.invoke !== false)
108
- unverified["POST /invoke"] = createInvokeHandler(agent);
109
- // `POST /run` exists only where there is something to run, and `GET /routines` exactly where that route does:
110
- // it is the catalogue OF that route, so listing names nobody can use would be a catalogue of nothing. Both ride
111
- // this table for the reason the table exists: they authenticate nobody, so they inherit the JSON body gate, the
112
- // cross-origin policy, the reserved path and the startup report's account of what is open.
113
- if (shouldServeRun(options.http)) {
114
- const routines = options.routines ?? [];
115
- const run = createRunHandler({ agent, routines });
116
- if (run)
117
- unverified["POST /run"] = run;
118
- const list = createRoutineListHandler({ routines });
119
- if (list)
120
- unverified["GET /routines"] = list;
121
- }
122
- // ONE rule over the whole table, so the next route we add is reserved by existing here rather than by someone
123
- // remembering to write a second check for it. `/health` is exempt by construction: it is only in `ours` when no
124
- // channel already serves it, because a probe is the deployment's to shape.
125
- const taken = Object.keys(unverified).flatMap((key) => {
54
+ const probed = Object.keys(routes).some((key) => {
126
55
  const entry = parseRouteKey(key);
127
- const channelKey = covered(entry.path, entry.method ?? "");
128
- return channelKey === undefined ? [] : [{ channelKey, key }];
56
+ return entry.path === "/health" && (entry.method === undefined || entry.method === "GET");
129
57
  });
130
- if (taken.length > 0) {
131
- throw new Error(`channel route(s) ${taken.map((t) => `"${t.channelKey}"`).join(", ")} take a path this serve answers on ` +
132
- `itself (${taken.map((t) => `"${t.key}"`).join(", ")}) — rename the channel route`);
133
- }
134
58
  return {
135
- unverified,
136
- selfVerifying: routes,
59
+ // A probe is the deployment's to shape, so a channel may serve `/health` itself.
60
+ routes: probed ? routes : { ...routes, "GET /health": health },
137
61
  longConnections,
138
62
  routeChannels,
139
63
  setReady(value) {
@@ -141,78 +65,71 @@ options = {}) {
141
65
  },
142
66
  };
143
67
  }
144
- /** Refuse channel routes the control plane would swallow. */
145
- export function assertNoControlPlaneCollision(channelRoutes, plane) {
146
- const collisions = Object.keys(channelRoutes).filter((key) => pathUnderPrefix(parseRouteKey(key).path, plane.prefix));
147
- if (collisions.length > 0) {
148
- throw new Error(`channel route(s) ${collisions.map((key) => `"${key}"`).join(", ")} collide with the session control plane — ` +
149
- `rename the channel route or disable sessionControl in fastagent.config`);
150
- }
151
- }
152
- export function mountSessionControl(routes, control) {
153
- if (!control)
154
- return { routes, mounts: [] };
155
- const plane = createControlPlane(control);
156
- assertNoControlPlaneCollision(routes, plane);
157
- return { routes, mounts: [plane], controlPrefix: plane.prefix };
158
- }
159
- /**
160
- * Load the agent's `routines/`, gated.
161
- *
162
- * Separate from {@link startSchedules} because two things need the SAME list and one of them runs first: the
163
- * `POST /run` route is built with the routes (so a channel cannot take that path unnoticed), and the resident
164
- * clock starts later, inside the service's scope. Loading twice would let them disagree about which routines exist.
165
- */
166
- export async function loadServingRoutines(agentDir) {
167
- // Thrown, not exited on: this runs inside an embedder's app as well as the CLI, and a library that calls
168
- // process.exit takes a decision (degrade? retry? stop?) that belongs to its host.
169
- const { routines, secrets, failures } = await loadRoutines(agentDir);
170
- // The routines' half of the serving-path gate (the opener does the tools', loadChannels the
171
- // channels'): a routine reads its env at IMPORT time, so an unset declared value has already
172
- // produced a broken prompt — refusing here is the last point where that is a startup failure.
173
- gateSecrets({ declared: secrets, failures });
174
- refuseBrokenDeclarations(failures);
175
- return routines;
176
- }
177
- /**
178
- * Start the clock over routines {@link loadServingRoutines} already gated — on every serve, routines or not: the
179
- * agent's own wake-ups are a default capability, and this is what fires them.
180
- */
181
- export function startSchedules(agent, stateRoot, routines, options = {}) {
182
- const scheduler = Effect.runSync(createScheduler({ agent, stateRoot, routines, externalClock: options.externalClock }));
68
+ /** Start the clock: the definition's schedules, and the wake-ups the agent sets for itself, on every serve. */
69
+ export function startSchedules(agent, stateRoot, schedules, options = {}) {
70
+ const scheduler = Effect.runSync(createScheduler({
71
+ agent,
72
+ stateRoot,
73
+ schedules,
74
+ ...(options.localClock === false ? { localClock: false } : {}),
75
+ }));
183
76
  scheduler.start();
184
- if (routines.length > 0) {
185
- log.info(`[fastagent] routines: ${routines.map((r) => r.name).join(", ")}${options.externalClock ? " (external clock — no resident cron timers)" : ""}`);
186
- }
187
- return { routines, stop: () => scheduler.stop() };
77
+ if (schedules.length > 0)
78
+ log.info(`[fastagent] schedules: ${schedules.map((s) => s.name).join(", ")}`);
79
+ return scheduler;
188
80
  }
189
81
  /**
190
- * The assembly itself, over an already-opened directory: channels, the control plane, routines and long connections,
191
- * composed into one handler.
82
+ * A Service over `handler`: answered in process, or served on a server of its own. `endStreams` ends what would hold
83
+ * the server's close open (a Caller watching events), and `shutdown` stops the rest once the server has.
192
84
  */
193
- export async function mountAgentService(opened, options = {}) {
194
- const { agentDir, workspace, stateRoot, sessionControl } = opened;
195
- // Wrapped BEFORE anything consumes it: routes, the control plane and routines must all drive the same agent, so
196
- // this is a hook rather than something a caller applies afterwards.
197
- const agent = options.wrapAgent?.(opened.agent) ?? opened.agent;
85
+ export function serviceOver(handler, ready, shutdown, endStreams = async () => { }) {
86
+ let listener;
87
+ let closing;
88
+ const fetch = totalFetch(handler);
89
+ return {
90
+ fetch,
91
+ listen(port, host) {
92
+ if (closing !== undefined)
93
+ return Promise.reject(new Error("the service is closed"));
94
+ if (listener !== undefined)
95
+ return Promise.reject(new Error("the service is already listening"));
96
+ const current = serveHandler(handler, { port, ...(host === undefined ? {} : { host }) });
97
+ listener = current;
98
+ return current.listening.catch(async (error) => {
99
+ // A bind that failed holds nothing once its scope is closed, so the service may listen again, elsewhere.
100
+ if (listener === current)
101
+ listener = undefined;
102
+ await current.close();
103
+ throw error;
104
+ });
105
+ },
106
+ ready,
107
+ close() {
108
+ closing ??= (async () => {
109
+ // The server first, so nothing new reaches what stops after it; each step runs, and every failure is said.
110
+ const failures = [];
111
+ for (const step of [endStreams, () => listener?.close(), shutdown]) {
112
+ await Promise.resolve()
113
+ .then(step)
114
+ .catch((error) => failures.push(error));
115
+ }
116
+ if (failures.length > 0) {
117
+ throw failures.length === 1 ? failures[0] : new AggregateError(failures, "the service did not close cleanly");
118
+ }
119
+ })();
120
+ return closing;
121
+ },
122
+ };
123
+ }
124
+ /** The assembly itself: channels, schedules and long connections, composed into one handler. */
125
+ export async function serveAgent(opened, options = {}) {
126
+ const { agent, agentDir, stateRoot, schedules } = opened;
198
127
  const closeTimeoutMs = options.closeTimeoutMs ?? CLOSE_DEADLINE_MS;
199
- // The embedder's way in, checked like the config file's: `allowedOrigin` compares exact strings, so a stray
200
- // trailing slash would refuse the front end with nothing naming the rule.
201
- const corsOrigins = opened.http?.cors;
202
- if (corsOrigins)
203
- assertCorsOrigins(corsOrigins, "mountAgentService: http.cors");
204
- // Loaded BEFORE the routes, because `POST /run` is one of them and the resident clock below must run over the
205
- // same list — two loads would be two answers to "which routines exist".
206
- const routines = await loadServingRoutines(agentDir);
207
- const routed = await routesFor(agentDir, agent, stateRoot, sessionControl, { http: opened.http, routines });
208
- const withControl = mountSessionControl(routed.selfVerifying, opened.publishControl ? sessionControl : undefined);
128
+ const routed = routesFor(agent, stateRoot, opened.channels);
209
129
  // Composed BEFORE anything starts.
210
- const handler = router({
211
- unverified: routed.unverified,
212
- selfVerifying: withControl.routes,
213
- mounts: withControl.mounts,
214
- ...(corsOrigins ? { corsOrigins } : {}),
215
- });
130
+ const channels = router(routed.routes);
131
+ const api = options.apiKeys?.length ? apiHandler(agent, { keys: options.apiKeys }) : undefined;
132
+ const handler = api === undefined ? channels : (req) => (isApiPath(new URL(req.url).pathname) ? api.fetch(req) : channels(req));
216
133
  return Effect.runPromise(Effect.gen(function* () {
217
134
  const lifetime = yield* Scope.make();
218
135
  const abort = new AbortController();
@@ -229,21 +146,21 @@ export async function mountAgentService(opened, options = {}) {
229
146
  routed.setReady(false);
230
147
  onClosed(name, error);
231
148
  };
232
- // Scope.close alone does not join concurrent closes or retain their failure.
233
- const shutdown = yield* Effect.cached(Effect.gen(function* () {
149
+ // Scope.close alone does not join concurrent closes or retain their failure. Every later call, the rollback a
150
+ // close itself triggers included, awaits the first one's outcome.
151
+ const shutdown = once(Effect.gen(function* () {
234
152
  abort.abort();
235
153
  yield* Scope.close(lifetime, Exit.void);
236
154
  }));
237
155
  const close = () => Effect.runPromise(shutdown);
238
156
  const rollback = shutdown.pipe(Effect.catchCause((cause) => Effect.sync(() => log.error(`[fastagent] cleanup after a failed start also failed: ${String(Cause.squash(cause))}`))));
239
- const onAbort = () => {
240
- void close().catch((error) => log.error(`[fastagent] service close failed: ${String(error)}`));
241
- };
242
157
  return yield* Effect.gen(function* () {
158
+ if (api !== undefined)
159
+ yield* Effect.addFinalizer(() => portCleanup("closing the Agent API", api.dispose));
243
160
  // Registered first, so subscriptions and schedule timers stop before waiting for transports.
244
161
  yield* Effect.addFinalizer(() => closeWithin(runs, names, closeTimeoutMs).pipe(Effect.orDie));
245
- const scheduled = yield* Effect.acquireRelease(Effect.try({
246
- try: () => startSchedules(agent, stateRoot, routines),
162
+ yield* Effect.acquireRelease(Effect.try({
163
+ try: () => startSchedules(agent, stateRoot, schedules),
247
164
  catch: (error) => error,
248
165
  }), (scheduled) => Effect.sync(scheduled.stop));
249
166
  const readiness = yield* Effect.forEach(routed.longConnections, (connection) => Effect.gen(function* () {
@@ -269,10 +186,6 @@ export async function mountAgentService(opened, options = {}) {
269
186
  log.info(`[fastagent] long connection ready: ${connection.name}`);
270
187
  })), Effect.forkScoped({ startImmediately: true }));
271
188
  }));
272
- if (options.signal?.aborted)
273
- yield* shutdown;
274
- else
275
- yield* Effect.acquireRelease(Effect.sync(() => options.signal?.addEventListener("abort", onAbort, { once: true })), () => Effect.sync(() => options.signal?.removeEventListener("abort", onAbort)));
276
189
  // The public readiness waiter lives outside the scope it may roll back.
277
190
  const ready = Effect.runPromise(Effect.gen(function* () {
278
191
  yield* Effect.forEach(readiness, Fiber.join, { concurrency: "unbounded", discard: true });
@@ -287,21 +200,12 @@ export async function mountAgentService(opened, options = {}) {
287
200
  // The rejection is still delivered to callers that await ready.
288
201
  ready.catch(() => { });
289
202
  return {
290
- handler,
291
- agent,
292
- routes: { ...routed.unverified, ...withControl.routes },
203
+ ...serviceOver(handler, ready, close, async () => api?.endStreams()),
204
+ routes: routed.routes,
293
205
  agentDir,
294
- workspace,
295
- channels: {
296
- routes: routed.routeChannels,
297
- longConnections: names,
298
- },
299
- unverifiedRoutes: Object.keys(routed.unverified),
300
- ...(corsOrigins ? { corsOrigins } : {}),
301
- routines: scheduled.routines,
302
- ready,
303
- ...(withControl.controlPrefix ? { controlPrefix: withControl.controlPrefix } : {}),
304
- close,
206
+ channels: { routes: routed.routeChannels, longConnections: names },
207
+ schedules,
208
+ api: api !== undefined,
305
209
  };
306
210
  }).pipe(Scope.provide(lifetime), Effect.onError(() => rollback));
307
211
  }));
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Which command serves this process: `dev` marks it, and everything else (`start`, a deployed box, an embedder) is
3
+ * the default. A channel file reads it at import through the agent's OWN installed copy of this package, which shares
4
+ * nothing with the CLI's copy but the process environment, so the mark lives there.
5
+ */
6
+ export declare const DEV_SERVE_ENV = "FASTAGENT_DEV";
7
+ export declare function markDevServe(env?: NodeJS.ProcessEnv): void;
8
+ /** For a command that is never `dev` (`start`, `deploy`), run from a shell or a tool that a `dev` process spawned. */
9
+ export declare function unmarkDevServe(env?: NodeJS.ProcessEnv): void;
10
+ export declare function servedByDev(env?: NodeJS.ProcessEnv): boolean;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Which command serves this process: `dev` marks it, and everything else (`start`, a deployed box, an embedder) is
3
+ * the default. A channel file reads it at import through the agent's OWN installed copy of this package, which shares
4
+ * nothing with the CLI's copy but the process environment, so the mark lives there.
5
+ */
6
+ export const DEV_SERVE_ENV = "FASTAGENT_DEV";
7
+ export function markDevServe(env = process.env) {
8
+ env[DEV_SERVE_ENV] = "1";
9
+ }
10
+ /** For a command that is never `dev` (`start`, `deploy`), run from a shell or a tool that a `dev` process spawned. */
11
+ export function unmarkDevServe(env = process.env) {
12
+ delete env[DEV_SERVE_ENV];
13
+ }
14
+ export function servedByDev(env = process.env) {
15
+ return env[DEV_SERVE_ENV] === "1";
16
+ }
package/dist/slack.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** `@fastagent-sh/fastagent/slack` — the first-party Slack Events API bot-channel surface. */
2
2
  export { slackChannel, defaultSlackRoute, slackEnvelope, verifySlackSignature, type SlackChannelOptions, type SlackEventEnvelope, type SlackFile, type SlackMessageEvent, type SlackRendering, type SlackRoute, type SlackFailure, } from "./channels/slack/slack.ts";
3
- export { slackTransport, type SlackTransport } from "./channels/slack/shared-api.ts";
3
+ export { slackThreads, slackTransport, type SlackTransport } from "./channels/slack/shared-api.ts";
4
4
  export type { SentSlackMessage, SlackTarget, UploadedSlackFile } from "./channels/slack/slack-api.ts";
package/dist/slack.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/slack` — the first-party Slack Events API bot-channel surface. */
2
2
  export { slackChannel, defaultSlackRoute, slackEnvelope, verifySlackSignature, } from "./channels/slack/slack.js";
3
- export { slackTransport } from "./channels/slack/shared-api.js";
3
+ export { slackThreads, slackTransport } from "./channels/slack/shared-api.js";
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Durable channel state, with no cross-instance locking: every file here is keyed by CHANNEL KIND
3
- * (`<stateRoot>/channels/<kind>/`), so what must not happen is two processes serving the SAME channel — which also
4
- * means one ingress credential in two places, and no local guard can see that. Two processes serving different
5
- * channels of one agent never touch the same file (docs/design/core.md, "the shipped file-backed implementations").
2
+ * A Caller's durable state under the state root, with no cross-instance locking: a channel's files are in its own
3
+ * directory (`<stateRoot>/channels/<the channel file's name>/`) and the schedules' in `schedule/`, so what must not
4
+ * happen is two processes serving the SAME channel — which also means one ingress credential in two places, and no
5
+ * local guard can see that (docs/design/core.md §9).
6
6
  */
7
7
  import { mkdirSync, readFileSync } from "node:fs";
8
- import { writeFileAtomic } from "../../atomic-write.js";
9
- import { log } from "../../log.js";
8
+ import { writeFileAtomic } from "./atomic-write.js";
9
+ import { log } from "./log.js";
10
10
  /** Create the channel's state home — the one shared spelling of it, so no channel invents its own. */
11
11
  export function ensureStateHome(dir) {
12
12
  mkdirSync(dir, { recursive: true });
@@ -1,2 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/telegram` — the Telegram bot channel subpath export, kept off the root surface. */
2
2
  export { telegramChannel, defaultTelegramRoute, telegramEnvelope, telegramStop, type TelegramChannelOptions, type TelegramUpdate, type TelegramMessage, type TelegramRoute, type TelegramFailure, } from "./channels/telegram/telegram.ts";
3
+ export { telegramTransport, type TelegramSendTarget, type TelegramSent, type TelegramTransport, } from "./channels/telegram/shared-api.ts";
package/dist/telegram.js CHANGED
@@ -1,2 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/telegram` — the Telegram bot channel subpath export, kept off the root surface. */
2
2
  export { telegramChannel, defaultTelegramRoute, telegramEnvelope, telegramStop, } from "./channels/telegram/telegram.js";
3
+ export { telegramTransport, } from "./channels/telegram/shared-api.js";
package/dist/tunnel.d.ts CHANGED
@@ -27,6 +27,9 @@ export declare function startCloudflareTunnel(port: number, spawnFn?: SpawnCloud
27
27
  export declare function announceWebhooks(dir: string, baseUrl: string, channels: readonly DeclaredChannel[], opts?: {
28
28
  openUrl?: (url: string) => void;
29
29
  stateRoot?: string;
30
+ env?: NodeJS.ProcessEnv;
31
+ /** {@link Registrars.leave}. */
32
+ leave?: (step: string) => void;
30
33
  }): Promise<{
31
34
  kind: string;
32
35
  outcome: RegistrationOutcome;
package/dist/tunnel.js CHANGED
@@ -7,10 +7,10 @@ import { spawn } from "node:child_process";
7
7
  import { registerFeishuWebhook } from "./channels/feishu/register-webhook.js";
8
8
  import { registerSlackWebhook } from "./channels/slack/register-webhook.js";
9
9
  import { registerTelegramWebhook } from "./channels/telegram/register-webhook.js";
10
- import { pointChannelsAt } from "./deploy/channel-ingress.js";
10
+ import { pointChannelsAt, webhookKinds } from "./deploy/channel-ingress.js";
11
11
  import { dotEnvPath, loadDotEnv } from "./env.js";
12
12
  import { installProxyFetch } from "./proxy.js";
13
- import { resolveStateRoot } from "./paths.js";
13
+ import { AGENT_ENVIRONMENT_ENV, resolveStateRoot } from "./paths.js";
14
14
  import { log } from "./log.js";
15
15
  // `(?!api\.)`: cloudflared's ERROR lines mention its request endpoint (`https://api.trycloudflare.com/tunnel`, e.g.
16
16
  // "failed to request quick Tunnel: Post ... timeout" under a flaky proxy) — without the exclusion a transient error
@@ -130,7 +130,8 @@ function lastErrorLine(tail) {
130
130
  export async function announceWebhooks(dir, baseUrl, channels, opts = {}) {
131
131
  log.info(`[fastagent] public URL: ${baseUrl}`);
132
132
  try {
133
- loadDotEnv(dir); // webhook registrars read channel credentials from .env
133
+ if (!opts.env)
134
+ loadDotEnv(dir);
134
135
  }
135
136
  catch (error) {
136
137
  // best-effort boundary: an unreadable .env (a MISSING one is already tolerated) still leaves registrars that can
@@ -141,16 +142,23 @@ export async function announceWebhooks(dir, baseUrl, channels, opts = {}) {
141
142
  // Readiness is the registrar's job: a fresh quick tunnel returns Cloudflare 530 for ~20-30s before its origin
142
143
  // connects, and each registrar absorbs that by retrying the platform call whose own URL verification reports it.
143
144
  const feishuOptions = {
145
+ env: opts.env,
144
146
  onManualRegistration: ({ consoleUrl }) => opts.openUrl?.(consoleUrl),
145
147
  };
148
+ const moving = webhookKinds(channels);
149
+ if (moving.length > 0) {
150
+ log.info(`[fastagent] pointing the selected ${moving.join(", ")} app at ${baseUrl}; only this app's Request URL changes`);
151
+ }
146
152
  return pointChannelsAt({
147
153
  baseUrl,
148
154
  channels,
149
155
  log: (message) => log.info(`[fastagent] ${message}`),
150
156
  registrars: {
151
- telegram: (url) => registerTelegramWebhook(url),
157
+ ...(opts.leave === undefined ? {} : { leave: opts.leave }),
158
+ telegram: (url) => registerTelegramWebhook(url, { env: opts.env }),
152
159
  slack: (url) => registerSlackWebhook(url, {
153
160
  stateRoot: opts.stateRoot ?? resolveStateRoot(dir),
161
+ environment: process.env[AGENT_ENVIRONMENT_ENV] === "production" ? "production" : "dev",
154
162
  log: (message) => log.info(message),
155
163
  }),
156
164
  feishu: (url, kind) => registerFeishuWebhook(url, kind, feishuOptions),
@@ -0,0 +1,21 @@
1
+ /** One variable to write: with a `value`, an active line; without one, a placeholder under its `hint`. */
2
+ export interface ValueEntry {
3
+ name: string;
4
+ value?: string;
5
+ hint?: string;
6
+ }
7
+ export interface ValueWriteResult {
8
+ /** Generated values written as active `NAME=value` lines. */
9
+ written: string[];
10
+ /** Vars already present with a non-empty active value; left untouched and omitted from next steps. */
11
+ alreadySet: string[];
12
+ /** Set when the secrets dir is an operator-chosen one (`FASTAGENT_SECRETS_DIR`) carrying no `.gitignore`. */
13
+ unprotectedSecretsDir?: string;
14
+ }
15
+ /** What a command says when it wrote a secret into a secrets directory fastagent does not own and git may track. */
16
+ export declare function unprotectedSecretsNote(dir: string): string;
17
+ /**
18
+ * Write `entries` into the selected environment's value file, under a `# --- <section> ---` line. A name with a
19
+ * non-empty active value is kept unless `overwrite` names it.
20
+ */
21
+ export declare function writeValues(dir: string, section: string, entries: readonly ValueEntry[], overwrite?: readonly string[]): Promise<ValueWriteResult>;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Writing the selected environment's value file (`.secrets/.env`, or `.secrets/production/.env` for a deployment):
3
+ * what a command generates (a channel's secret, the Agent API's key) goes in as an active `NAME=value` line, a value
4
+ * already set stays, and the secrets directory fastagent made carries its own `.gitignore`.
5
+ */
6
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
7
+ import { dirname, join } from "node:path";
8
+ import { dotEnvPath, parseEnvContent } from "./env.js";
9
+ import { SECRETS_DIRNAME, SECRET_FILE_MODE, exists } from "./paths.js";
10
+ import { baseTemplate } from "./scaffold/templates.js";
11
+ /** What a command says when it wrote a secret into a secrets directory fastagent does not own and git may track. */
12
+ export function unprotectedSecretsNote(dir) {
13
+ return (`${dir} (FASTAGENT_SECRETS_DIR) now holds a secret and has no .gitignore — that directory is yours, so ` +
14
+ "fastagent will not write one into it. Make sure git does not track what is in there.");
15
+ }
16
+ /** Whether `.env` content carries a non-empty ACTIVE value for `name`. */
17
+ function hasActiveEnvValue(content, name) {
18
+ return (parseEnvContent(content).get(name)?.trim() ?? "") !== "";
19
+ }
20
+ function mentionsEnvName(content, name) {
21
+ return content.split("\n").some((line) => new RegExp(`^\\s*#?\\s*${name}\\s*=`).test(line));
22
+ }
23
+ /**
24
+ * Write `entries` into the selected environment's value file, under a `# --- <section> ---` line. A name with a
25
+ * non-empty active value is kept unless `overwrite` names it.
26
+ */
27
+ export async function writeValues(dir, section, entries, overwrite = []) {
28
+ const file = dotEnvPath(dir);
29
+ const secretsDir = dirname(file);
30
+ await mkdir(secretsDir, { recursive: true });
31
+ // THE one exception to "fastagent has no opinion about git": the directory it writes secrets into carries its own
32
+ // `.gitignore`.
33
+ const owned = secretsDir === join(dir, SECRETS_DIRNAME) || secretsDir === join(dir, SECRETS_DIRNAME, "production");
34
+ let unprotectedSecretsDir;
35
+ if (owned) {
36
+ // Only EEXIST is tolerable (already protected, or a concurrent writer).
37
+ await writeFile(join(secretsDir, ".gitignore"), baseTemplate("secrets.gitignore"), { flag: "wx" }).catch((e) => {
38
+ if (e.code !== "EEXIST")
39
+ throw e;
40
+ });
41
+ }
42
+ else if (!(await exists(join(secretsDir, ".gitignore")))) {
43
+ unprotectedSecretsDir = secretsDir;
44
+ }
45
+ let current = "";
46
+ try {
47
+ current = await readFile(file, "utf8");
48
+ }
49
+ catch (e) {
50
+ if (e.code !== "ENOENT")
51
+ throw e;
52
+ }
53
+ const alreadySet = entries
54
+ .filter((e) => !overwrite.includes(e.name) && hasActiveEnvValue(current, e.name))
55
+ .map((e) => e.name);
56
+ const lines = [];
57
+ const written = [];
58
+ const contentLines = current.split("\n");
59
+ let replacedInPlace = false;
60
+ for (const e of entries) {
61
+ if (alreadySet.includes(e.name))
62
+ continue;
63
+ if (e.value !== undefined) {
64
+ // An ACTIVE but EMPTY assignment already in the file (an uncommented, unfilled placeholder from `cp
65
+ // .env.example .env`) must be replaced IN PLACE.
66
+ let idx = -1;
67
+ for (let i = contentLines.length - 1; i >= 0; i--) {
68
+ if (parseEnvContent(contentLines[i]).has(e.name)) {
69
+ idx = i;
70
+ break;
71
+ }
72
+ }
73
+ if (idx >= 0) {
74
+ contentLines[idx] = `${e.name}=${e.value}`;
75
+ replacedInPlace = true;
76
+ }
77
+ else {
78
+ lines.push(`${e.name}=${e.value}`);
79
+ }
80
+ written.push(e.name);
81
+ }
82
+ else if (!mentionsEnvName(current, e.name)) {
83
+ // Hint above, never inline after `=` (this IS the file loadDotEnv reads).
84
+ lines.push(...(e.hint === undefined ? [] : [`# ${e.hint}`]), `# ${e.name}=`);
85
+ }
86
+ }
87
+ // 0600 on the file this call CREATES, and nothing at all about one it did not: a `.env` the operator made with
88
+ // `cp .secrets/.env.example .secrets/.env` carries the mode that command gave it, and re-deciding it would be
89
+ // fastagent changing the permissions of a file it does not own.
90
+ const secret = { mode: SECRET_FILE_MODE };
91
+ if (replacedInPlace) {
92
+ current = contentLines.join("\n");
93
+ await writeFile(file, current, secret);
94
+ }
95
+ if (lines.length > 0) {
96
+ const marker = `# --- ${section} ---`;
97
+ if (current.includes(marker)) {
98
+ // A marker already present (e.g. a .env copied from .env.example) — slot the new lines under it instead of
99
+ // orphaning them at the end of the file.
100
+ await writeFile(file, current.replace(marker, `${marker}\n${lines.join("\n")}`), secret);
101
+ }
102
+ else {
103
+ const prefix = current === "" ? "" : current.endsWith("\n") ? "\n" : "\n\n";
104
+ await appendFile(file, `${prefix}${marker}\n${lines.join("\n")}\n`, secret);
105
+ }
106
+ }
107
+ return { written, alreadySet, ...(unprotectedSecretsDir === undefined ? {} : { unprotectedSecretsDir }) };
108
+ }
package/dist/version.d.ts CHANGED
@@ -3,3 +3,8 @@
3
3
  * scaffolded dependency range (`^${version}`), so `init` must never fall back and scaffold an uninstallable `^0.0.0`.
4
4
  */
5
5
  export declare function fastagentVersion(): Promise<string>;
6
+ /** This build's version, and the exact versions of the harness packages it runs, for a new agent to depend on. */
7
+ export declare function fastagentBuild(): Promise<{
8
+ version: string;
9
+ harness: Record<string, string>;
10
+ }>;