@fastagent-sh/fastagent 0.21.1 → 0.22.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 (388) hide show
  1. package/README.md +9 -12
  2. package/dist/agent.d.ts +24 -37
  3. package/dist/agent.js +7 -15
  4. package/dist/atomic-write.d.ts +13 -17
  5. package/dist/atomic-write.js +18 -29
  6. package/dist/bind.d.ts +12 -22
  7. package/dist/bind.js +18 -27
  8. package/dist/channel.d.ts +11 -33
  9. package/dist/channels/agentcore-limits.d.ts +2 -13
  10. package/dist/channels/agentcore-limits.js +2 -13
  11. package/dist/channels/agentcore-protocol.d.ts +67 -58
  12. package/dist/channels/agentcore-protocol.js +23 -15
  13. package/dist/channels/agentcore-service.d.ts +19 -33
  14. package/dist/channels/agentcore-service.js +176 -59
  15. package/dist/channels/agentcore.d.ts +39 -33
  16. package/dist/channels/agentcore.js +90 -224
  17. package/dist/channels/body.d.ts +15 -2
  18. package/dist/channels/body.js +24 -2
  19. package/dist/channels/busy.d.ts +1 -21
  20. package/dist/channels/busy.js +2 -39
  21. package/dist/channels/control.d.ts +17 -62
  22. package/dist/channels/control.js +74 -182
  23. package/dist/channels/define-channel.d.ts +39 -0
  24. package/dist/channels/define-channel.js +9 -0
  25. package/dist/channels/discover.d.ts +10 -31
  26. package/dist/channels/discover.js +37 -31
  27. package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
  28. package/dist/channels/feishu/bootstrap-token.js +7 -23
  29. package/dist/channels/feishu/card.d.ts +14 -28
  30. package/dist/channels/feishu/card.js +16 -30
  31. package/dist/channels/feishu/cloud.d.ts +1 -5
  32. package/dist/channels/feishu/context-buffer.d.ts +4 -23
  33. package/dist/channels/feishu/context-buffer.js +8 -31
  34. package/dist/channels/feishu/crypto.d.ts +10 -5
  35. package/dist/channels/feishu/crypto.js +12 -19
  36. package/dist/channels/feishu/feishu-api.d.ts +2 -1
  37. package/dist/channels/feishu/feishu-api.js +5 -4
  38. package/dist/channels/feishu/feishu.d.ts +4 -10
  39. package/dist/channels/feishu/feishu.js +113 -227
  40. package/dist/channels/feishu/invoke-turn.d.ts +5 -4
  41. package/dist/channels/feishu/invoke-turn.js +36 -54
  42. package/dist/channels/feishu/model.d.ts +4 -21
  43. package/dist/channels/feishu/model.js +1 -8
  44. package/dist/channels/feishu/normalize.d.ts +2 -10
  45. package/dist/channels/feishu/normalize.js +7 -42
  46. package/dist/channels/feishu/parse.d.ts +9 -29
  47. package/dist/channels/feishu/parse.js +8 -26
  48. package/dist/channels/feishu/preview.d.ts +12 -14
  49. package/dist/channels/feishu/preview.js +108 -207
  50. package/dist/channels/feishu/register-app.d.ts +8 -23
  51. package/dist/channels/feishu/register-app.js +7 -20
  52. package/dist/channels/feishu/register-webhook.d.ts +2 -9
  53. package/dist/channels/feishu/register-webhook.js +9 -33
  54. package/dist/channels/feishu/scaffold/channel.ts +27 -19
  55. package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
  56. package/dist/channels/feishu/setup-mode.d.ts +11 -36
  57. package/dist/channels/feishu/setup-mode.js +7 -26
  58. package/dist/channels/feishu/ws-ingress.d.ts +1 -2
  59. package/dist/channels/feishu/ws-ingress.js +6 -8
  60. package/dist/channels/http.d.ts +15 -15
  61. package/dist/channels/http.js +19 -9
  62. package/dist/channels/kit/attachment-path.d.ts +4 -5
  63. package/dist/channels/kit/attachment-path.js +10 -34
  64. package/dist/channels/kit/context-buffer.d.ts +14 -21
  65. package/dist/channels/kit/context-buffer.js +25 -38
  66. package/dist/channels/kit/delivery.d.ts +35 -0
  67. package/dist/channels/kit/delivery.js +129 -0
  68. package/dist/channels/kit/event-stream.d.ts +5 -0
  69. package/dist/channels/kit/event-stream.js +29 -0
  70. package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
  71. package/dist/channels/kit/invoke-turn-kit.js +89 -32
  72. package/dist/channels/kit/preview-kit.d.ts +18 -72
  73. package/dist/channels/kit/preview-kit.js +22 -88
  74. package/dist/channels/kit/signature.d.ts +3 -9
  75. package/dist/channels/kit/signature.js +3 -9
  76. package/dist/channels/kit/state.d.ts +1 -2
  77. package/dist/channels/kit/state.js +5 -14
  78. package/dist/channels/kit/stop-command.d.ts +1 -2
  79. package/dist/channels/kit/stop-command.js +6 -9
  80. package/dist/channels/kit/tasks.d.ts +1 -1
  81. package/dist/channels/kit/tasks.js +8 -21
  82. package/dist/channels/kit/text.d.ts +2 -16
  83. package/dist/channels/kit/text.js +23 -31
  84. package/dist/channels/kit/thread-participants.d.ts +4 -17
  85. package/dist/channels/kit/thread-participants.js +8 -55
  86. package/dist/channels/kit/transport.d.ts +26 -0
  87. package/dist/channels/kit/transport.js +20 -0
  88. package/dist/channels/kit/turn-queue.d.ts +7 -11
  89. package/dist/channels/kit/turn-queue.js +21 -39
  90. package/dist/channels/kit/turn-runner.d.ts +32 -18
  91. package/dist/channels/kit/turn-runner.js +98 -53
  92. package/dist/channels/kit/turn-store.d.ts +32 -32
  93. package/dist/channels/kit/turn-store.js +45 -88
  94. package/dist/channels/lark/lark.d.ts +1 -5
  95. package/dist/channels/lark/onboard.d.ts +3 -12
  96. package/dist/channels/lark/onboard.js +3 -13
  97. package/dist/channels/lark/scaffold/channel.ts +27 -19
  98. package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
  99. package/dist/channels/registration.d.ts +9 -35
  100. package/dist/channels/registration.js +6 -37
  101. package/dist/channels/secret.js +2 -5
  102. package/dist/channels/serve.d.ts +47 -44
  103. package/dist/channels/serve.js +165 -101
  104. package/dist/channels/slack/config-api.d.ts +4 -27
  105. package/dist/channels/slack/config-api.js +5 -34
  106. package/dist/channels/slack/context-buffer.d.ts +2 -3
  107. package/dist/channels/slack/context-buffer.js +5 -5
  108. package/dist/channels/slack/invoke-turn.d.ts +3 -1
  109. package/dist/channels/slack/invoke-turn.js +25 -34
  110. package/dist/channels/slack/manifest.js +1 -3
  111. package/dist/channels/slack/model.d.ts +1 -2
  112. package/dist/channels/slack/model.js +1 -1
  113. package/dist/channels/slack/onboard.d.ts +4 -3
  114. package/dist/channels/slack/onboard.js +8 -11
  115. package/dist/channels/slack/onboarding-state.d.ts +3 -5
  116. package/dist/channels/slack/onboarding-state.js +9 -6
  117. package/dist/channels/slack/parse.d.ts +4 -13
  118. package/dist/channels/slack/parse.js +7 -28
  119. package/dist/channels/slack/preview.d.ts +12 -2
  120. package/dist/channels/slack/preview.js +292 -269
  121. package/dist/channels/slack/reaction.d.ts +11 -3
  122. package/dist/channels/slack/reaction.js +19 -8
  123. package/dist/channels/slack/register-webhook.js +4 -7
  124. package/dist/channels/slack/scaffold/channel.ts +20 -13
  125. package/dist/channels/slack/scaffold/slack-send.ts +2 -2
  126. package/dist/channels/slack/setup-server.d.ts +1 -5
  127. package/dist/channels/slack/setup-server.js +1 -5
  128. package/dist/channels/slack/shared-api.d.ts +1 -4
  129. package/dist/channels/slack/shared-api.js +2 -13
  130. package/dist/channels/slack/slack-api.d.ts +2 -1
  131. package/dist/channels/slack/slack-api.js +6 -4
  132. package/dist/channels/slack/slack.d.ts +11 -14
  133. package/dist/channels/slack/slack.js +121 -122
  134. package/dist/channels/sse.d.ts +1 -1
  135. package/dist/channels/sse.js +1 -1
  136. package/dist/channels/telegram/context-buffer.d.ts +7 -22
  137. package/dist/channels/telegram/context-buffer.js +6 -23
  138. package/dist/channels/telegram/invoke-turn.d.ts +7 -7
  139. package/dist/channels/telegram/invoke-turn.js +24 -43
  140. package/dist/channels/telegram/parse.d.ts +24 -51
  141. package/dist/channels/telegram/parse.js +32 -84
  142. package/dist/channels/telegram/preview.d.ts +10 -1
  143. package/dist/channels/telegram/preview.js +79 -100
  144. package/dist/channels/telegram/register-webhook.js +5 -23
  145. package/dist/channels/telegram/scaffold/channel.ts +25 -18
  146. package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
  147. package/dist/channels/telegram/telegram-api.d.ts +4 -3
  148. package/dist/channels/telegram/telegram-api.js +9 -7
  149. package/dist/channels/telegram/telegram.d.ts +4 -18
  150. package/dist/channels/telegram/telegram.js +38 -94
  151. package/dist/channels/telegram/turn-store.d.ts +11 -14
  152. package/dist/channels/telegram/turn-store.js +8 -6
  153. package/dist/channels/wait-health.d.ts +4 -6
  154. package/dist/channels/wait-health.js +10 -14
  155. package/dist/cli/add-feishu.d.ts +2 -12
  156. package/dist/cli/add-feishu.js +24 -71
  157. package/dist/cli/add-slack.d.ts +5 -3
  158. package/dist/cli/add-slack.js +53 -76
  159. package/dist/cli/auth-view.d.ts +2 -16
  160. package/dist/cli/auth-view.js +2 -16
  161. package/dist/cli/commands/add.js +25 -40
  162. package/dist/cli/commands/chat.d.ts +0 -1
  163. package/dist/cli/commands/chat.js +5 -6
  164. package/dist/cli/commands/deploy/agentcore.js +60 -65
  165. package/dist/cli/commands/deploy/docker.js +50 -23
  166. package/dist/cli/commands/deploy/fly.js +23 -61
  167. package/dist/cli/commands/deploy/railway.js +23 -18
  168. package/dist/cli/commands/deploy/shared.d.ts +38 -57
  169. package/dist/cli/commands/deploy/shared.js +40 -59
  170. package/dist/cli/commands/deploy.d.ts +10 -27
  171. package/dist/cli/commands/deploy.js +38 -60
  172. package/dist/cli/commands/destroy.d.ts +4 -0
  173. package/dist/cli/commands/destroy.js +56 -0
  174. package/dist/cli/commands/dev.d.ts +2 -1
  175. package/dist/cli/commands/dev.js +20 -23
  176. package/dist/cli/commands/info.d.ts +0 -2
  177. package/dist/cli/commands/info.js +83 -49
  178. package/dist/cli/commands/init.d.ts +1 -2
  179. package/dist/cli/commands/init.js +16 -55
  180. package/dist/cli/commands/invoke.d.ts +0 -1
  181. package/dist/cli/commands/invoke.js +5 -7
  182. package/dist/cli/commands/login.d.ts +2 -1
  183. package/dist/cli/commands/login.js +33 -38
  184. package/dist/cli/commands/logs.js +3 -4
  185. package/dist/cli/commands/models.js +1 -5
  186. package/dist/cli/commands/routine-run.d.ts +6 -0
  187. package/dist/cli/commands/routine-run.js +47 -0
  188. package/dist/cli/commands/routine.d.ts +25 -0
  189. package/dist/cli/commands/routine.js +122 -0
  190. package/dist/cli/commands/start.d.ts +31 -3
  191. package/dist/cli/commands/start.js +131 -81
  192. package/dist/cli/commands/tool.js +26 -12
  193. package/dist/cli/fail.d.ts +13 -23
  194. package/dist/cli/fail.js +20 -23
  195. package/dist/cli/invoke-stream.d.ts +1 -8
  196. package/dist/cli/kernel.d.ts +6 -38
  197. package/dist/cli/kernel.js +14 -77
  198. package/dist/cli/models-view.d.ts +1 -9
  199. package/dist/cli/models-view.js +11 -22
  200. package/dist/cli/program.d.ts +5 -6
  201. package/dist/cli/program.js +120 -157
  202. package/dist/cli/serve.d.ts +57 -41
  203. package/dist/cli/serve.js +94 -121
  204. package/dist/cli/shared.d.ts +15 -61
  205. package/dist/cli/shared.js +55 -181
  206. package/dist/cli.js +1 -8
  207. package/dist/collect.d.ts +6 -0
  208. package/dist/collect.js +6 -0
  209. package/dist/core.d.ts +4 -1
  210. package/dist/core.js +9 -7
  211. package/dist/declared-secrets.d.ts +55 -0
  212. package/dist/declared-secrets.js +88 -0
  213. package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
  214. package/dist/deploy/agentcore/aws-cli.js +64 -0
  215. package/dist/deploy/agentcore/destroy.d.ts +51 -0
  216. package/dist/deploy/agentcore/destroy.js +273 -0
  217. package/dist/deploy/agentcore/forwarder.js +28 -79
  218. package/dist/deploy/agentcore/logs.d.ts +2 -18
  219. package/dist/deploy/agentcore/logs.js +27 -51
  220. package/dist/deploy/agentcore/plan.d.ts +85 -105
  221. package/dist/deploy/agentcore/plan.js +152 -314
  222. package/dist/deploy/agentcore/run.d.ts +28 -28
  223. package/dist/deploy/agentcore/run.js +227 -306
  224. package/dist/deploy/agentcore/zip.d.ts +2 -15
  225. package/dist/deploy/agentcore/zip.js +3 -17
  226. package/dist/deploy/build-context.d.ts +21 -0
  227. package/dist/deploy/build-context.js +137 -0
  228. package/dist/deploy/channel-ingress.d.ts +26 -39
  229. package/dist/deploy/channel-ingress.js +57 -28
  230. package/dist/deploy/container.d.ts +20 -34
  231. package/dist/deploy/container.js +49 -100
  232. package/dist/deploy/docker/plan.d.ts +12 -11
  233. package/dist/deploy/docker/plan.js +43 -24
  234. package/dist/deploy/docker/run.d.ts +22 -25
  235. package/dist/deploy/docker/run.js +39 -37
  236. package/dist/deploy/fly/plan.d.ts +15 -44
  237. package/dist/deploy/fly/plan.js +41 -73
  238. package/dist/deploy/fly/run.d.ts +16 -51
  239. package/dist/deploy/fly/run.js +42 -95
  240. package/dist/deploy/hosts.d.ts +24 -3
  241. package/dist/deploy/hosts.js +24 -3
  242. package/dist/deploy/preflight.d.ts +47 -41
  243. package/dist/deploy/preflight.js +282 -383
  244. package/dist/deploy/railway/plan.d.ts +16 -42
  245. package/dist/deploy/railway/plan.js +42 -60
  246. package/dist/deploy/railway/run.d.ts +30 -68
  247. package/dist/deploy/railway/run.js +86 -100
  248. package/dist/deploy/registration-gate.d.ts +6 -13
  249. package/dist/deploy/residency.d.ts +37 -0
  250. package/dist/deploy/residency.js +24 -0
  251. package/dist/deploy/runner.d.ts +10 -9
  252. package/dist/deploy/runner.js +7 -18
  253. package/dist/deploy/secrets.d.ts +40 -41
  254. package/dist/deploy/secrets.js +117 -103
  255. package/dist/deploy/workspace.d.ts +50 -0
  256. package/dist/deploy/workspace.js +197 -0
  257. package/dist/dev-supervisor.d.ts +1 -9
  258. package/dist/dev-supervisor.js +21 -64
  259. package/dist/effect-port.d.ts +50 -0
  260. package/dist/effect-port.js +77 -0
  261. package/dist/engines/pi/agent-session-factory.d.ts +50 -69
  262. package/dist/engines/pi/agent-session-factory.js +249 -146
  263. package/dist/engines/pi/auth.d.ts +15 -11
  264. package/dist/engines/pi/auth.js +96 -99
  265. package/dist/engines/pi/chat.d.ts +1 -5
  266. package/dist/engines/pi/chat.js +2 -14
  267. package/dist/engines/pi/config.d.ts +53 -51
  268. package/dist/engines/pi/config.js +83 -92
  269. package/dist/engines/pi/create.d.ts +60 -75
  270. package/dist/engines/pi/create.js +108 -141
  271. package/dist/engines/pi/definition.d.ts +14 -58
  272. package/dist/engines/pi/definition.js +21 -85
  273. package/dist/engines/pi/invoke-session.d.ts +5 -25
  274. package/dist/engines/pi/invoke-session.js +209 -244
  275. package/dist/engines/pi/login.d.ts +95 -35
  276. package/dist/engines/pi/login.js +259 -107
  277. package/dist/engines/pi/machine.d.ts +31 -0
  278. package/dist/engines/pi/machine.js +106 -0
  279. package/dist/engines/pi/models.d.ts +70 -89
  280. package/dist/engines/pi/models.js +188 -81
  281. package/dist/engines/pi/open.d.ts +65 -53
  282. package/dist/engines/pi/open.js +129 -69
  283. package/dist/engines/pi/report.d.ts +3 -12
  284. package/dist/engines/pi/report.js +2 -16
  285. package/dist/engines/pi/search-tools.d.ts +1 -9
  286. package/dist/engines/pi/search-tools.js +26 -57
  287. package/dist/engines/pi/service.d.ts +2 -16
  288. package/dist/engines/pi/service.js +2 -16
  289. package/dist/engines/pi/session-builder.d.ts +5 -9
  290. package/dist/engines/pi/session-builder.js +44 -82
  291. package/dist/engines/pi/session-control.js +286 -269
  292. package/dist/engines/pi/session-effects.d.ts +14 -0
  293. package/dist/engines/pi/session-effects.js +30 -0
  294. package/dist/engines/pi/session-inheritance.d.ts +9 -18
  295. package/dist/engines/pi/session-inheritance.js +103 -160
  296. package/dist/engines/pi/session-markers.d.ts +31 -30
  297. package/dist/engines/pi/session-markers.js +26 -25
  298. package/dist/engines/pi/session-settings.d.ts +6 -22
  299. package/dist/engines/pi/session-settings.js +10 -21
  300. package/dist/engines/pi/session-store.d.ts +26 -92
  301. package/dist/engines/pi/session-store.js +96 -310
  302. package/dist/engines/pi/tool-context.d.ts +17 -45
  303. package/dist/engines/pi/tool-context.js +7 -26
  304. package/dist/engines/pi/tool.d.ts +45 -36
  305. package/dist/engines/pi/tool.js +43 -57
  306. package/dist/engines/pi/turn-kit.d.ts +14 -52
  307. package/dist/engines/pi/turn-kit.js +18 -67
  308. package/dist/engines/pi/wake-tool.d.ts +2 -10
  309. package/dist/engines/pi/wake-tool.js +7 -27
  310. package/dist/env.d.ts +27 -28
  311. package/dist/env.js +45 -50
  312. package/dist/index.js +0 -2
  313. package/dist/lark.d.ts +1 -2
  314. package/dist/lark.js +1 -2
  315. package/dist/loader.d.ts +14 -55
  316. package/dist/loader.js +24 -43
  317. package/dist/log.d.ts +2 -12
  318. package/dist/log.js +3 -24
  319. package/dist/node.d.ts +2 -15
  320. package/dist/node.js +2 -18
  321. package/dist/observe.d.ts +2 -8
  322. package/dist/open-url.d.ts +1 -1
  323. package/dist/open-url.js +1 -1
  324. package/dist/paths.d.ts +70 -134
  325. package/dist/paths.js +165 -232
  326. package/dist/pi.d.ts +4 -6
  327. package/dist/pi.js +3 -3
  328. package/dist/proxy.d.ts +16 -6
  329. package/dist/proxy.js +52 -16
  330. package/dist/runtime.d.ts +18 -12
  331. package/dist/runtime.js +18 -11
  332. package/dist/scaffold/add-channel.d.ts +14 -31
  333. package/dist/scaffold/add-channel.js +48 -112
  334. package/dist/scaffold/init.d.ts +9 -31
  335. package/dist/scaffold/init.js +45 -138
  336. package/dist/scaffold/templates/env.example +6 -4
  337. package/dist/scaffold/templates/fastagent.config.ts +36 -0
  338. package/dist/scaffold/templates.d.ts +3 -3
  339. package/dist/scaffold/templates.js +5 -8
  340. package/dist/scaffold/vendor-skill.d.ts +2 -5
  341. package/dist/scaffold/vendor-skill.js +19 -29
  342. package/dist/schedule/cron.d.ts +9 -5
  343. package/dist/schedule/cron.js +9 -11
  344. package/dist/schedule/discover.d.ts +12 -12
  345. package/dist/schedule/discover.js +43 -34
  346. package/dist/schedule/routine.d.ts +43 -0
  347. package/dist/schedule/routine.js +32 -0
  348. package/dist/schedule/run.d.ts +55 -0
  349. package/dist/schedule/run.js +146 -0
  350. package/dist/schedule/scheduler.d.ts +44 -55
  351. package/dist/schedule/scheduler.js +291 -265
  352. package/dist/schedule/state.d.ts +105 -10
  353. package/dist/schedule/state.js +293 -19
  354. package/dist/schedule/wake-alarm.d.ts +10 -23
  355. package/dist/schedule/wake-alarm.js +91 -121
  356. package/dist/schedule/wakeups.d.ts +23 -39
  357. package/dist/schedule/wakeups.js +33 -75
  358. package/dist/secrets-gate.d.ts +32 -0
  359. package/dist/secrets-gate.js +33 -0
  360. package/dist/service.d.ts +137 -98
  361. package/dist/service.js +250 -252
  362. package/dist/session-remote.d.ts +33 -44
  363. package/dist/session-remote.js +203 -192
  364. package/dist/session.d.ts +153 -228
  365. package/dist/session.js +33 -55
  366. package/dist/tunnel.d.ts +8 -36
  367. package/dist/tunnel.js +24 -65
  368. package/dist/version.d.ts +2 -2
  369. package/dist/version.js +2 -2
  370. package/package.json +10 -16
  371. package/dist/channels/agentcore-state.d.ts +0 -77
  372. package/dist/channels/agentcore-state.js +0 -276
  373. package/dist/channels/github/github.d.ts +0 -31
  374. package/dist/channels/github/github.js +0 -89
  375. package/dist/channels/github/scaffold/channel.ts +0 -25
  376. package/dist/cli/commands/attach.d.ts +0 -108
  377. package/dist/cli/commands/attach.js +0 -637
  378. package/dist/cli/commands/fire.d.ts +0 -7
  379. package/dist/cli/commands/fire.js +0 -38
  380. package/dist/cli/commands/schedule.d.ts +0 -12
  381. package/dist/cli/commands/schedule.js +0 -87
  382. package/dist/github.d.ts +0 -3
  383. package/dist/github.js +0 -2
  384. package/dist/scaffold/templates/fastagent.config.mjs +0 -23
  385. package/dist/schedule/audit.d.ts +0 -18
  386. package/dist/schedule/audit.js +0 -57
  387. package/dist/schedule/schedule.d.ts +0 -35
  388. package/dist/schedule/schedule.js +0 -23
@@ -1,71 +1,32 @@
1
- /**
2
- * Init: scaffold a runnable fastagent agent, offline. Default = a COMPLETE agent (persona.md +
3
- * the writing-great-skills skill + a fetch-url code tool + fastagent.config.mjs + package.json +
4
- * .gitignore + .secrets/); `--minimal` drops the code tool and package.json. persona.md is the agent's
5
- * identity (prompt segment ①); an existing AGENTS.md is never written or touched — it is project
6
- * context (②), kept as-is. skills/ and tools/ are the agent's self-editable capabilities (re-read each
7
- * turn).
8
- *
9
- * Placement — no detection and no prompt, just a default and one flag. By DEFAULT the whole agent —
10
- * definition, config, `.secrets/`, machinery — lands in `<dir>/fastagent/`; the surrounding tree gets
11
- * ZERO writes and becomes the workspace the agent works on. `--agent-dir <name>` picks another name for
12
- * that directory (`fastagent.config.*` is the marker of what IS an agent, never the name), and `--agent-dir .`
13
- * (spelled `--flat`) lands the identical shape in `dir` itself, for the case where the directory IS the
14
- * agent (a standalone agent repo, a monorepo package).
15
- *
16
- * Which of the two a served agent turns out to be is NOT decided here: the workspace is whatever
17
- * fastagent is later pointed at (resolvePlacement). This module only chooses where the files land — so
18
- * its one placement duty is to refuse a target that the lookup would not return (see below).
19
- *
20
- * Scope: init is best-effort atomic for ORDINARY inputs — it never overwrites existing files,
21
- * preflights non-directory scaffold parents, and rolls back a partial write (files AND the
22
- * `fastagent/` tree it created for them, so a retry sees a clean slate). It does not defend against
23
- * every pathological target state (TOCTOU, FIFOs, disk-full): recover by delete-and-retry.
24
- *
25
- * Sibling scaffold modules: add-channel.ts (`add <channel>`), vendor-skill.ts (`add skill`). The files
26
- * this module writes are real templates under templates/, read through templates.ts.
27
- */
1
+ /** Init: scaffold a runnable fastagent agent, offline. */
28
2
  import { lstat, mkdir, readdir, rm, rmdir, writeFile } from "node:fs/promises";
29
3
  import { basename, dirname, join, resolve, sep } from "node:path";
30
- import { AGENT_CONFIG_NAMES, DEFAULT_AGENT_DIRNAME, SECRETS_DIRNAME, agentDefinitionOwner, agentsAt, displayPath, ensureSecretsDir, exists, } from "../paths.js";
4
+ import { AGENT_CONFIG_FILE, DEFAULT_AGENT_DIRNAME, SECRETS_DIRNAME, agentDefinitionOwner, agentsAt, displayPath, exists, } from "../paths.js";
31
5
  import { baseTemplate, packageJson, toPackageName } from "./templates.js";
32
6
  import { fastagentVersion } from "../version.js";
33
- /** The agent directory name for a raw `--agent-dir` value: the default when unset, and `./bot` read as
34
- * `bot` — `basename` already says that is what it means, so rejecting the spelling would be pedantry. */
7
+ /** The agent directory name for a raw `--agent-dir` value: the default when unset, and `./bot` read as `bot`. */
35
8
  export function agentDirName(raw) {
36
9
  if (raw === undefined)
37
10
  return DEFAULT_AGENT_DIRNAME;
38
11
  const trimmed = raw.replace(/^\.[/\\]/, "");
39
12
  return trimmed === "" ? raw : trimmed;
40
13
  }
41
- /** Why `name` cannot be an agent directory name, or undefined when it can. It must stay ONE segment
42
- * inside the target: anything else (a separator, `..`, an absolute path) would land the agent where the
43
- * one-level lookup cannot see it — an agent nothing would ever serve.
14
+ /**
15
+ * Why `name` cannot be an agent directory name, or undefined when it can.
44
16
  *
45
- * Returns the CONSTRAINT, not a sentence: the CLI prefixes the flag it owns and reports it as the usage
46
- * error it is (exit 2), while {@link scaffoldAgent} prefixes the option name for a programmatic caller,
47
- * who never passed a flag and should not be told to fix one. */
17
+ * `"."` is refused along with every path: the definition always lands in a SUBDIRECTORY, and the directory around it
18
+ * is the workspace. The subdirectory also carries its own `package.json`, so a `fastagent.config.ts` never loads under
19
+ * the host repo's module settings.
20
+ */
48
21
  export function agentDirNameError(name) {
49
- if (name === "." || (name !== "" && name !== ".." && name === basename(name)))
22
+ if (name !== "" && name !== "." && name !== ".." && name === basename(name))
50
23
  return undefined;
51
- return (`must be a single directory name (or "." for the target itself) — a path would put the agent outside ` +
52
- `the target directory, where fastagent would not find it`);
24
+ return (`must be a single directory name — the definition lives in a subdirectory, and the directory around it is ` +
25
+ `the workspace the agent works on (a path, or ".", would put it elsewhere)`);
53
26
  }
54
- /**
55
- * Scaffold a runnable agent into `<dir>/<agentDir>/` — or into `dir` itself when `agentDir` is `"."`
56
- * (both created if missing). Default is a complete agent (persona.md + the writing-great-skills skill +
57
- * a code tool + package.json); `--minimal` drops the code tool and package.json.
58
- *
59
- * A SUBDIRECTORY target must be empty (any content there is an unfinished agent or something unrelated,
60
- * and landing persona.md beside it would be a silent mix), while `"."` is a directory being adopted —
61
- * content is expected. So `"."` KEEPS every file that already exists (reported, never overwritten, never
62
- * verified) and refuses only on a config, which means the directory is already an agent. An existing
63
- * AGENTS.md is untouched either way: that is the project's context, adopted as-is.
64
- */
27
+ /** Scaffold a runnable agent into `<dir>/<agentDir>/` (both created if missing). */
65
28
  export async function scaffoldAgent(dir, options = {}) {
66
- const minimal = options.minimal ?? false;
67
29
  const root = agentDirName(options.agentDir);
68
- const flat = root === ".";
69
30
  const invalid = agentDirNameError(root);
70
31
  if (invalid)
71
32
  throw new Error(`agentDir "${root}" ${invalid}`);
@@ -74,45 +35,35 @@ export async function scaffoldAgent(dir, options = {}) {
74
35
  content: baseTemplate(`skills/writing-great-skills/${name}`),
75
36
  });
76
37
  const files = [
77
- // ① identity. AGENTS.md is deliberately NOT scaffolded: a fresh agent has no project context, and
78
- // an existing repo already owns its AGENTS.md (kept untouched, read as ② context from the workspace).
38
+ // ① identity.
79
39
  { rel: join(root, "persona.md"), content: baseTemplate("persona.md") },
80
- // The example skill: how to author skills well — the core of self-iteration. Markdown, so it
81
- // ships in --minimal too. Vendored verbatim from mattpocock/skills (MIT); LICENSE sits beside it.
40
+ // The example skill: how to author skills well — the core of self-iteration.
82
41
  skill("SKILL.md"),
83
42
  skill("GLOSSARY.md"),
84
43
  skill("LICENSE"),
85
- { rel: join(root, "fastagent.config.mjs"), content: baseTemplate("fastagent.config.mjs") },
86
- // Two ignore files, scaffolded ONCE and owned by the author from then on — no command rewrites,
87
- // reads or verifies them. The agent's own covers node_modules/machinery/a stray .env; `.secrets/`
88
- // carries its own because the root file is the one the author has reason to edit, and git's
89
- // nested-ignore precedence keeps the credentials protected whatever happens up there.
44
+ { rel: join(root, AGENT_CONFIG_FILE), content: baseTemplate(AGENT_CONFIG_FILE) },
45
+ // Two ignore files, scaffolded ONCE and owned by the author from then on — no command rewrites, reads or verifies
46
+ // them.
90
47
  { rel: join(root, ".gitignore"), content: baseTemplate("gitignore") },
91
48
  { rel: join(root, SECRETS_DIRNAME, ".gitignore"), content: baseTemplate("secrets.gitignore") },
92
49
  { rel: join(root, SECRETS_DIRNAME, ".env.example"), content: baseTemplate("env.example") },
93
- ];
94
- if (!minimal) {
95
- files.push({ rel: join(root, "tools", "fetch-url.ts"), content: baseTemplate("tools/fetch-url.ts") },
96
- // The agent's own manifest, named after the directory it serves (`<dir>-agent`) — except when it
97
- // IS that directory, where it takes the name straight.
50
+ { rel: join(root, "tools", "fetch-url.ts"), content: baseTemplate("tools/fetch-url.ts") },
51
+ // The agent's own manifest, named after the directory it serves. Its `type: module` is why the definition gets a
52
+ // subdirectory of its own: a config file under a host repo's CommonJS manifest does not load.
98
53
  {
99
54
  rel: join(root, "package.json"),
100
- content: packageJson(flat ? toPackageName(dir) : `${toPackageName(dir)}-agent`, await fastagentVersion()),
101
- });
102
- }
103
- // Inside another agent's DEFINITION (its `skills/`, `tools/`, `channels/` or `schedules/`): the outer
104
- // agent would load the new one as its own content. Note what this deliberately ALLOWS — a package
105
- // inside an agent's repository, which is the author's tree and not part of what that agent loads.
55
+ content: packageJson(`${toPackageName(dir)}-agent`, await fastagentVersion()),
56
+ },
57
+ ];
58
+ // Inside another agent's DEFINITION (its `skills/`, `tools/`, `channels/` or `routines/`): the outer agent would
59
+ // load the new one as its own content.
106
60
  const owner = agentDefinitionOwner(dir);
107
61
  if (owner) {
108
62
  throw new Error(`"${dir}" is inside the definition of the agent at ${owner} — an agent scaffolded here would be ` +
109
63
  `part of THAT agent's surface, not one of its own. Init outside it.`);
110
64
  }
111
- // Preflight scaffold parent dirs FIRST: a pre-existing non-directory there would make mkdir fail
112
- // mid-loop AFTER the first write, leaving a half-scaffold — and a file or symlink named `fastagent`
113
- // must be named as such here, before the occupancy check below tries to read it as a directory
114
- // (lstat, not stat: a symlinked parent must be rejected, not followed — it would write outside the
115
- // agent dir).
65
+ // Preflight scaffold parent dirs FIRST: a pre-existing non-directory there would make mkdir fail mid-loop AFTER the
66
+ // first write, leaving a half-scaffold.
116
67
  const parents = new Set();
117
68
  for (const file of files) {
118
69
  let p = dirname(file.rel);
@@ -127,52 +78,32 @@ export async function scaffoldAgent(dir, options = {}) {
127
78
  throw new Error(`cannot scaffold: "${rel}" exists and is not a directory (a regular file or symlink) — remove it, or init elsewhere`);
128
79
  }
129
80
  }
130
- // Refuse an occupied agent dir. A config inside means it IS an agent already (name the marker so the
131
- // message is actionable); any other content means an unfinished agent or something unrelated, and
132
- // landing persona.md beside it would be a silent mix. Only ENOENT reads as "empty" — any other fault
133
- // (EACCES…) must surface here rather than as a raw errno mid-write.
134
- // (AGENTS.md outside is NOT a marker — it is context, adopted untouched.)
135
- // `.DS_Store`/`.gitkeep`/`.keep` are not evidence of anyone's content: Finder noise, and the standard
136
- // way to commit an empty directory (someone reserving the name in git ahead of init).
81
+ // Refuse an occupied agent dir.
137
82
  const occupants = (await readdir(join(dir, root)).catch((e) => {
138
83
  if (e.code === "ENOENT")
139
84
  return [];
140
85
  throw e;
141
86
  })).filter((f) => ![".DS_Store", ".gitkeep", ".keep"].includes(f));
142
- // Could `dir` still SELECT the agent this run creates? SIBLINGS are fine — several agents at one level
143
- // is a supported shape (different roles driving one repository), picked between by FASTAGENT_AGENT or
144
- // the default name. A SHADOW is not: a config AT `dir` wins over everything inside it, so scaffolding
145
- // under one (or scaffolding one over existing children) makes an agent the lookup can never return.
146
- // The target itself is never in the way — a config already there means "already an agent", which the
147
- // refusal below says in those words.
87
+ // Could `dir` still SELECT the agent this run creates?
148
88
  const existing = agentsAt(dir).filter((a) => a !== resolve(dir, root));
149
- const shadowed = flat
150
- ? existing // the new agent lands AT `dir` and hides everything inside it
151
- : existing.filter((a) => a === resolve(dir)); // an agent AT `dir` hides the new one inside it
89
+ const shadowed = existing.filter((a) => a === resolve(dir)); // an agent AT `dir` hides the new one inside it
152
90
  if (shadowed.length > 0) {
153
91
  throw new Error(`"${dir}" already resolves to ${shadowed.map((a) => displayPath(process.cwd(), a) ?? a).join(", ")} — ` +
154
- `an agent scaffolded ${flat ? "here" : `in ./${root}/`} would be hidden by it and never served ` +
92
+ `an agent scaffolded in ./${root}/ would be hidden by it and never served ` +
155
93
  `from "${dir}" (an agent AT a directory wins over any inside it). Use that agent, move it away, ` +
156
94
  `or init in a different directory.`);
157
95
  }
158
- // ONE coordinate system for both refusals — `displayPath` is the shared policy (relative inside the
159
- // cwd, absolute when it climbs out); the earlier pair mixed an absolute path with a basename-relative
160
- // one, in adjacent branches of the same command.
96
+ // ONE coordinate system for both refusals — `displayPath` is the shared policy (relative inside the cwd, absolute
97
+ // when it climbs out).
161
98
  const target = displayPath(process.cwd(), join(dir, root)) ?? join(dir, root);
162
- const config = occupants.filter((f) => AGENT_CONFIG_NAMES.includes(f));
163
- if (config.length > 0) {
164
- throw new Error(`"${target}" already has ${config.join(", ")} — already a fastagent agent`);
99
+ if (occupants.includes(AGENT_CONFIG_FILE)) {
100
+ throw new Error(`"${target}" already has ${AGENT_CONFIG_FILE} — already a fastagent agent`);
165
101
  }
166
- // Only a SUBDIRECTORY target must be empty. `.` is a directory being adopted — content is expected,
167
- // and every existing file is kept below.
168
- if (!flat && occupants.length > 0) {
102
+ if (occupants.length > 0) {
169
103
  throw new Error(`"${target}" already holds ${occupants.join(", ")} — move it away first, or run ` +
170
104
  `\`fastagent init\` in a different directory`);
171
105
  }
172
- // Which directories were OURS to create? "Empty" is not ownership: a user may have pre-created any of
173
- // them (`--agent-dir .` adopts a directory that can already carry an empty `skills/` or `.secrets/`),
174
- // and the rollback below must delete only what THIS run made. Recorded before the first write, since
175
- // afterwards the two are indistinguishable.
106
+ // Which directories were OURS to create?
176
107
  const preexisting = new Set();
177
108
  for (const rel of parents)
178
109
  if (await exists(join(dir, rel)))
@@ -180,42 +111,18 @@ export async function scaffoldAgent(dir, options = {}) {
180
111
  const agentDirExisted = await exists(join(dir, root));
181
112
  await mkdir(dir, { recursive: true });
182
113
  const created = [];
183
- const kept = [];
184
- // ONE rollback scope: any failure removes what THIS run created — files AND the directories it made
185
- // for them. Leaving the empty dirs behind would be worse than untidy: the occupancy refusal above
186
- // would then report the next `init` as occupied, blaming the user for our own debris.
187
- //
188
- // `wx` never clobbers. A subdirectory target was proven empty, so EEXIST means a concurrent writer —
189
- // an error. `.` is a directory being adopted, so an existing `.gitignore`/`package.json` is the
190
- // author's — keep it and report it.
114
+ // ONE rollback scope: any failure removes what THIS run created — files AND the directories it made for them.
115
+ // `wx` so a collision is a failure: the target was checked empty above, so anything here appeared mid-run.
191
116
  try {
192
117
  for (const file of files) {
193
118
  const abs = join(dir, file.rel);
194
- // The secrets dir carries a mode; every other directory here is ordinary. `init` is usually the
195
- // FIRST of the four writers to create it, and whoever creates it decides the mode for the rest.
196
- if (basename(dirname(abs)) === SECRETS_DIRNAME)
197
- await ensureSecretsDir(dirname(abs));
198
- else
199
- await mkdir(dirname(abs), { recursive: true });
200
- try {
201
- await writeFile(abs, file.content, { flag: "wx" });
202
- created.push(file.rel);
203
- }
204
- catch (e) {
205
- if (!flat || e.code !== "EEXIST")
206
- throw e;
207
- kept.push(file.rel);
208
- }
119
+ await mkdir(dirname(abs), { recursive: true });
120
+ await writeFile(abs, file.content, { flag: "wx" });
121
+ created.push(file.rel);
209
122
  }
210
123
  }
211
124
  catch (error) {
212
- // Best-effort rollback of a partial scaffold: anything that won't delete is left behind (the
213
- // original error below is the one worth surfacing — a cleanup failure must not mask it). Files
214
- // first, then the directories THIS RUN created, deepest first; `rmdir` additionally removes only
215
- // what is empty, so a pre-existing sibling inside one of ours is never touched. The agent root goes
216
- // last, and only when this run created it — "empty" was never proof of ownership. (Reaching here at all takes a
217
- // real fs fault: the occupancy refusal above proved the target empty, so nothing else can fail
218
- // mid-loop. The covered case is a permission fault before the first write.)
125
+ // Best-effort rollback of a partial scaffold.
219
126
  for (const rel of created.reverse())
220
127
  await rm(join(dir, rel), { force: true }).catch(() => { });
221
128
  for (const rel of [...parents].sort((a, b) => b.split(sep).length - a.split(sep).length)) {
@@ -226,5 +133,5 @@ export async function scaffoldAgent(dir, options = {}) {
226
133
  await rmdir(join(dir, root)).catch(() => { });
227
134
  throw error;
228
135
  }
229
- return { dir, complete: !minimal, agentDir: root, created, kept };
136
+ return { dir, agentDir: root, created };
230
137
  }
@@ -14,8 +14,12 @@
14
14
  # command: `FASTAGENT_AGENT=pm fastagent dev`.
15
15
 
16
16
  # --- Model selection (overrides fastagent.config) ---
17
- # Precedence: --model flag > FASTAGENT_MODEL > config. `fastagent dev` writes your first-run pick to
18
- # fastagent.config; set it here to override per-environment.
17
+ # Precedence: --model flag > FASTAGENT_MODEL > config — one chain, evaluated in whichever environment
18
+ # is running. `fastagent dev` writes your first-run pick to fastagent.config; set it here to override
19
+ # it. THIS FILE also declares the environment `fastagent deploy` resolves in, so a value here rides the
20
+ # release manifest into the deployment and a deployment can run a model the committed config does not
21
+ # name. That recorded value outranks the deployed box's own .env, so switching models there means
22
+ # editing this line and redeploying.
19
23
  # FASTAGENT_MODEL=openai-codex/gpt-5.5
20
24
 
21
25
  # --- Serving (fastagent start) ---
@@ -29,5 +33,3 @@
29
33
  # the volume so a rotated OAuth credential persists across restarts. Set HERE it moves only
30
34
  # auth.json — a value inside .env cannot relocate the .env being read.
31
35
  # FASTAGENT_SECRETS_DIR=/data/.secrets
32
- # Override just the sessions path (default: <state root>/sessions):
33
- # FASTAGENT_SESSIONS_DIR=/data/sessions
@@ -0,0 +1,36 @@
1
+ // fastagent.config.ts — deployment choices only (model / http; code tools auto-discover from tools/).
2
+ // Your agent's identity lives in persona.md; its capabilities in skills/ + tools/ — never here.
3
+ // An AGENTS.md in the WORKSPACE (the directory the agent is started in) is read as project context.
4
+ // Model precedence: `--model` flag > FASTAGENT_MODEL env > this default.
5
+ // No model is preset: `fastagent dev` shows the full model catalog (models you already have
6
+ // credentials for come first; picking one that needs auth logs you in inline) and writes your choice
7
+ // below. Or set it by hand to a "provider/modelId" (`fastagent models` lists them).
8
+ // Self-hosted model (vLLM/Ollama/…) or your own gateway? Declare it in a models.json next to this
9
+ // file and select it like any other spec — see docs/configuration.md "Custom model endpoints".
10
+ //
11
+ // `satisfies` is what makes your editor complete these keys and describe them on hover — and what
12
+ // turns a typo into an error you see while writing rather than one `fastagent dev` reports. The
13
+ // import is TYPE-only: nothing is loaded at runtime. Every key is optional; the commented lines
14
+ // below carry the default.
15
+ import type { FastagentConfig } from "@fastagent-sh/fastagent";
16
+
17
+ export default {
18
+ // model: "openai-codex/gpt-5.5",
19
+ // thinkingLevel: "high", // reasoning effort (off|minimal|low|medium|high|xhigh|max); default "medium" (pi TUI parity)
20
+ // add `host: "127.0.0.1"` here to pin the bind address; default: `start` all interfaces (what containers
21
+ // need), `dev` loopback. `cors: ["https://your-app.example.com"]` pins which origins a browser may call
22
+ // this serve from. The default is `*` on every bind — any page your users visit can call this port and
23
+ // read the reply, a loopback bind included (it stops another machine, not your own browser).
24
+ // `invoke: false` withholds POST /invoke, for a serve meant to be reached only through its channels
25
+ // (POST /run goes with it; `run: true` keeps that one for a scheduler of yours that calls routines by name).
26
+ http: { port: 8787 },
27
+ // sessionControl: true, // serve /control/* for remote observation + steering (a Web panel, a desktop app)
28
+ // tools: [], // programmatically defined tools, appended after the coding ones — tools/ is the usual way
29
+ // deploy: what the agent needs on the box (so `fastagent deploy` doesn't need a hand-written Dockerfile
30
+ // or hand-set host variables). Uncomment as needed:
31
+ // deploy: {
32
+ // secrets: ["GH_TOKEN"], // extra secret env vars your tools use — deploy reads their values from .secrets/.env
33
+ // apt: ["git"], // extra apt packages baked into the image (git, ripgrep, …; default repos only)
34
+ // agentcore: { idleTimeoutSeconds: 180 }, // `deploy agentcore` only: idle microVM tail, 60–1209600s
35
+ // },
36
+ } satisfies FastagentConfig;
@@ -2,10 +2,10 @@
2
2
  export declare const baseTemplate: (name: string) => string;
3
3
  /** Read one file from a channel's scaffold bundle (src/channels/<kind>/scaffold/<name>). */
4
4
  export declare const channelTemplate: (kind: string, name: string) => string;
5
- /** The .ts files in a channel's bundle: `channel.ts` is the channel adapter; the rest are companion tools. */
5
+ /** The .ts files in a channel's bundle: `channel.*` are the channel templates (one per ingress —
6
+ * `channel.ts`, `channel.websocket.ts`); the rest are companion tools. */
6
7
  export declare const channelBundleFiles: (kind: string) => string[];
7
- /** package.json for the complete agent. The @fastagent-sh/fastagent range tracks THIS build's
8
- * version, and tool authors use its `z` re-export rather than installing a second zod copy. */
8
+ /** package.json for the complete agent. */
9
9
  export declare function packageJson(name: string, version: string): string;
10
10
  /** Sanitize a directory basename into a valid npm package name (lowercase, safe chars). */
11
11
  export declare function toPackageName(dir: string): string;
@@ -1,9 +1,6 @@
1
1
  /**
2
- * Scaffold template ACCESS (data, not logic): readers for the real files `init`/`add` write into an
3
- * agent dir, plus the parametric pieces. Base agent templates live under ./templates/; each
4
- * channel's bundle lives WITH the channel at ../channels/<kind>/scaffold/ (so a channel owns its
5
- * starter kit and could ship as its own package). Both trees are excluded from this package's tsc +
6
- * biome (they import the published @fastagent-sh/fastagent, not this source) and copied into dist/ by the build.
2
+ * Scaffold template ACCESS (data, not logic): readers for the real files `init`/`add` write into an agent dir, plus
3
+ * the parametric pieces.
7
4
  */
8
5
  import { readFileSync, readdirSync } from "node:fs";
9
6
  import { basename, resolve } from "node:path";
@@ -12,10 +9,10 @@ export const baseTemplate = (name) => readFileSync(new URL(`./templates/${name}`
12
9
  const channelScaffoldDir = (kind) => new URL(`../channels/${kind}/scaffold/`, import.meta.url);
13
10
  /** Read one file from a channel's scaffold bundle (src/channels/<kind>/scaffold/<name>). */
14
11
  export const channelTemplate = (kind, name) => readFileSync(new URL(name, channelScaffoldDir(kind)), "utf8");
15
- /** The .ts files in a channel's bundle: `channel.ts` is the channel adapter; the rest are companion tools. */
12
+ /** The .ts files in a channel's bundle: `channel.*` are the channel templates (one per ingress —
13
+ * `channel.ts`, `channel.websocket.ts`); the rest are companion tools. */
16
14
  export const channelBundleFiles = (kind) => readdirSync(channelScaffoldDir(kind)).filter((f) => f.endsWith(".ts"));
17
- /** package.json for the complete agent. The @fastagent-sh/fastagent range tracks THIS build's
18
- * version, and tool authors use its `z` re-export rather than installing a second zod copy. */
15
+ /** package.json for the complete agent. */
19
16
  export function packageJson(name, version) {
20
17
  return `${JSON.stringify({
21
18
  name,
@@ -13,11 +13,8 @@ export interface VendoredSkill {
13
13
  overwritten: boolean;
14
14
  }
15
15
  /**
16
- * Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a
17
- * local path, or a bare name (resolved against the local global skill dirs). Copy-in, git-tracked.
18
- * Refuses to overwrite unless `options.update` (then a plain git-tracked overwrite, never a merge).
19
- * Validates a staging copy with the runtime loader BEFORE replacing, so a bad fetch never destroys an
20
- * existing skill.
16
+ * Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a local path, or a
17
+ * bare name (resolved against the local global skill dirs).
21
18
  */
22
19
  export declare function vendorSkill(agentDir: string, source: string, options?: {
23
20
  update?: boolean;
@@ -1,9 +1,6 @@
1
1
  /**
2
- * `fastagent add skill <source>`: vendor an Agent Skills skill into `<agent dir>/skills/<name>/` —
3
- * copy-in, git-tracked, never a runtime registry. Source is a giget ref (github default), a local
4
- * path, or a bare name (resolved against the local global skill dirs as an add-time copy source only).
5
- * Fetch → staging → validate → rollback-protected replace, so a bad fetch never destroys an existing
6
- * skill.
2
+ * `fastagent add skill <source>`: vendor an Agent Skills skill into `<agent dir>/skills/<name>/` — copy-in,
3
+ * git-tracked, never a runtime registry.
7
4
  */
8
5
  import { cp, mkdir, readdir, rename, rm, stat } from "node:fs/promises";
9
6
  import { existsSync } from "node:fs";
@@ -18,8 +15,8 @@ function skillNameFromSource(source) {
18
15
  return basename(noRef.replace(/\/+$/, ""));
19
16
  }
20
17
  /**
21
- * The skills/ path must be safe to write through: never follow a symlink that escapes the agent dir
22
- * (mkdir would), and reject a plain file with one clear message.
18
+ * The skills/ path must be safe to write through: never follow a symlink that escapes the agent dir (mkdir would), and
19
+ * reject a plain file with one clear message.
23
20
  */
24
21
  async function assertSkillsDirUsable(agentDir) {
25
22
  await assertInsideAgentDir(agentDir, "skills");
@@ -37,8 +34,10 @@ function isLocalSource(source) {
37
34
  function isBareName(source) {
38
35
  return !source.includes("/") && !/^[a-z][a-z0-9+.-]*:/i.test(source);
39
36
  }
40
- /** Local "global" skill dirs, used ONLY as an add-time vendoring source (a bare `add skill <name>`
41
- * copies the match in, git-tracked) — nothing is loaded from here at run time. */
37
+ /**
38
+ * Local "global" skill dirs, used ONLY as an add-time vendoring source (a bare `add skill <name>` copies the match in,
39
+ * git-tracked).
40
+ */
42
41
  function findGlobalSkillSource(name) {
43
42
  for (const root of [join(homedir(), ".agents", "skills"), join(homedir(), ".pi", "agent", "skills")]) {
44
43
  if (existsSync(join(root, name, "SKILL.md")))
@@ -47,11 +46,8 @@ function findGlobalSkillSource(name) {
47
46
  return undefined;
48
47
  }
49
48
  /**
50
- * Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a
51
- * local path, or a bare name (resolved against the local global skill dirs). Copy-in, git-tracked.
52
- * Refuses to overwrite unless `options.update` (then a plain git-tracked overwrite, never a merge).
53
- * Validates a staging copy with the runtime loader BEFORE replacing, so a bad fetch never destroys an
54
- * existing skill.
49
+ * Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a local path, or a
50
+ * bare name (resolved against the local global skill dirs).
55
51
  */
56
52
  export async function vendorSkill(agentDir, source, options = {}) {
57
53
  const name = skillNameFromSource(source);
@@ -61,8 +57,7 @@ export async function vendorSkill(agentDir, source, options = {}) {
61
57
  const skillsDir = join(agentDir, "skills");
62
58
  await assertSkillsDirUsable(agentDir);
63
59
  const dest = join(skillsDir, name);
64
- // A process can die between moving the old skill aside and installing staging. Never guess that an
65
- // arbitrary hidden directory is ours or delete it: stop with the exact backup path for manual restore.
60
+ // A process can die between moving the old skill aside and installing staging.
66
61
  const skillEntries = await readdir(skillsDir).catch((error) => {
67
62
  if (error.code === "ENOENT")
68
63
  return [];
@@ -72,16 +67,15 @@ export async function vendorSkill(agentDir, source, options = {}) {
72
67
  if (interrupted.length > 0) {
73
68
  throw new Error(`found an interrupted skill update backup (${interrupted.join(", ")}) — inspect it, then restore it to skills/${name} or remove it before retrying`);
74
69
  }
75
- // Refuse to clobber unless --update; a git-tracked overwrite is safe (review with `git diff`, undo
76
- // with `git checkout`).
70
+ // Refuse to clobber unless --update; a git-tracked overwrite is safe (review with `git diff`, undo with `git
71
+ // checkout`).
77
72
  const overwritten = existsSync(dest);
78
73
  if (overwritten && !options.update) {
79
74
  throw new Error(`skills/${name} already exists — re-run with --update to overwrite it (git tracks the change), or remove it`);
80
75
  }
81
76
  await mkdir(skillsDir, { recursive: true });
82
- // Fetch into a STAGING dir (same filesystem → atomic rename), validate, and only THEN replace dest,
83
- // so a failed/invalid fetch never destroys an existing skill. The leading "." keeps the loader from
84
- // treating staging as a skill.
77
+ // Fetch into a STAGING dir (same filesystem → atomic rename), validate, and only THEN replace dest, so a
78
+ // failed/invalid fetch never destroys an existing skill.
85
79
  const staging = join(skillsDir, `.${name}.vendoring`);
86
80
  await rm(staging, { recursive: true, force: true }); // clear any leftover from a prior crash
87
81
  try {
@@ -102,9 +96,7 @@ export async function vendorSkill(agentDir, source, options = {}) {
102
96
  await cp(src, staging, { recursive: true });
103
97
  }
104
98
  else {
105
- // giget defaults a BARE ref to its own template registry, not github — so default the provider to
106
- // github for a plain `owner/repo/path` (an explicit `github:`/`gh:`/`gitlab:`… scheme is kept).
107
- // Supports a subdir + #ref, fetched via the tar API (no git binary).
99
+ // giget defaults a BARE ref to its own template registry, not github.
108
100
  const ref = /^[a-z][a-z0-9+.-]*:/i.test(source) ? source : `github:${source}`;
109
101
  // Lazy import: giget is only needed for a git ref, so the serve path (index.ts → init.ts) and the
110
102
  // local/bare-name sources never load it.
@@ -119,9 +111,7 @@ export async function vendorSkill(agentDir, source, options = {}) {
119
111
  await rm(staging, { recursive: true, force: true }); // failed/invalid: drop staging, leave dest intact
120
112
  throw error;
121
113
  }
122
- // Install with rollback: move the old skill to a unique backup, then put staging in place. A failed
123
- // second rename restores it; a process crash leaves a backup the next invocation reports without
124
- // touching it.
114
+ // Install with rollback: move the old skill to a unique backup, then put staging in place.
125
115
  const previous = join(skillsDir, `.${name}.previous-${randomUUID()}`);
126
116
  if (overwritten)
127
117
  await rename(dest, previous);
@@ -141,8 +131,8 @@ export async function vendorSkill(agentDir, source, options = {}) {
141
131
  }
142
132
  if (overwritten)
143
133
  await rm(previous, { recursive: true, force: true });
144
- // Report via the runtime loader, matching THIS skill by EXACT directory (a substring match would
145
- // prefix-pollute a sibling `<name>-x` and break on Windows path separators).
134
+ // Report via the runtime loader, matching THIS skill by EXACT directory (a substring match would prefix-pollute a
135
+ // sibling `<name>-x` and break on Windows path separators).
146
136
  const def = await loadAgentDefinition(agentDir);
147
137
  const rel = join("skills", name);
148
138
  const skill = def.skills.find((sk) => relative(agentDir, dirname(sk.filePath)) === rel);
@@ -1,7 +1,11 @@
1
- /** The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the
2
- * expression will never fire again. */
1
+ /**
2
+ * The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the expression will never
3
+ * fire again.
4
+ */
3
5
  export declare function nextRun(cron: string, tz: string | undefined, from: Date): Date | undefined;
4
- /** Why `cron`/`tz` is invalid (for load-time validation), or undefined if valid. croner validates the
5
- * timezone lazily (not at construction), so check it explicitly via Intl (throws on an unknown IANA
6
- * zone); the pattern is validated by constructing the Cron. Both turned into a message, never a throw. */
6
+ /**
7
+ * Why `cron`/`tz` is invalid (for load-time validation), or undefined if valid. croner validates the timezone lazily
8
+ * (not at construction), so check it explicitly via Intl (throws on an unknown IANA zone); the pattern is validated by
9
+ * constructing the Cron.
10
+ */
7
11
  export declare function cronError(cron: string, tz: string | undefined): string | undefined;
@@ -1,19 +1,17 @@
1
+ /** The one place that touches the cron library (`croner`). */
2
+ import { Cron } from "croner";
1
3
  /**
2
- * The one place that touches the cron library (`croner`). The scheduler owns FIRE control (for
3
- * durability / overdue catch-up), so it borrows only croner's next-instant computation, not its
4
- * scheduling. croner is chosen for zero transitive dependencies + built-in IANA timezone/DST handling
5
- * (wall-clock schedules like "daily 9am America/New_York" need DST-correct arithmetic that hand-rolling
6
- * gets wrong at the spring-forward / fall-back boundary).
4
+ * The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the expression will never
5
+ * fire again.
7
6
  */
8
- import { Cron } from "croner";
9
- /** The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the
10
- * expression will never fire again. */
11
7
  export function nextRun(cron, tz, from) {
12
8
  return new Cron(cron, { timezone: tz ?? "UTC" }).nextRun(from) ?? undefined;
13
9
  }
14
- /** Why `cron`/`tz` is invalid (for load-time validation), or undefined if valid. croner validates the
15
- * timezone lazily (not at construction), so check it explicitly via Intl (throws on an unknown IANA
16
- * zone); the pattern is validated by constructing the Cron. Both turned into a message, never a throw. */
10
+ /**
11
+ * Why `cron`/`tz` is invalid (for load-time validation), or undefined if valid. croner validates the timezone lazily
12
+ * (not at construction), so check it explicitly via Intl (throws on an unknown IANA zone); the pattern is validated by
13
+ * constructing the Cron.
14
+ */
17
15
  export function cronError(cron, tz) {
18
16
  if (tz !== undefined) {
19
17
  try {
@@ -1,17 +1,17 @@
1
1
  import { type ModuleLoadFailure } from "../loader.ts";
2
- import type { LoadedSchedule } from "./schedule.ts";
3
- /** Schedule file basenames under `<dir>/schedules/` — an existence probe listed WITHOUT importing
4
- * (deploy pre-flight's time-trigger detection; `fastagent info` uses {@link loadSchedules} instead,
5
- * since it also reports broken files and next instants). */
6
- export declare function discoverScheduleFiles(dir: string): Promise<string[]>;
2
+ import { type DeclaredSecret } from "../declared-secrets.ts";
3
+ import type { LoadedRoutine } from "./routine.ts";
7
4
  /**
8
- * Discover schedules in `<dir>/schedules/`: each file default-exports a `defineSchedule({...})`, named
9
- * from its filename. A file broken for ANY reason — a failed import, not a schedule (no cron/prompt), or
10
- * an invalid cron/tz — is ISOLATED into `failures` (skipped + reported, never a crash-loop, G2), the same
11
- * way `loadTools`/`loadChannels` isolate theirs. A duplicate name (foo.ts + foo.js) keeps the first and
12
- * reports the rest.
5
+ * Discover routines in `<dir>/routines/`: each file default-exports a `defineRoutine({...})`, named from its
6
+ * filename.
13
7
  */
14
- export declare function loadSchedules(dir: string): Promise<{
15
- schedules: LoadedSchedule[];
8
+ export declare function loadRoutines(dir: string): Promise<{
9
+ routines: LoadedRoutine[];
10
+ /** What each loaded routine declared it needs, BY ROUTINE NAME and attributed to its file. Per
11
+ * routine because a caller that runs exactly ONE of them (`fastagent routine run`) must not be stopped
12
+ * by a sibling's credential, and because two routines declaring the same variable would
13
+ * otherwise blame whichever file was read first. The clock flattens it (it runs all of them).
14
+ * Data, not a check — only a serving path asserts it (src/declared-secrets.ts). */
15
+ secrets: Map<string, DeclaredSecret[]>;
16
16
  failures: ModuleLoadFailure[];
17
17
  }>;