@fastagent-sh/fastagent 0.21.1 → 0.22.1

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 +233 -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 +166 -229
  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,276 +0,0 @@
1
- /**
2
- * Cross-deploy durability for the state root on AgentCore.
3
- *
4
- * WHY THIS EXISTS: AgentCore's managed SessionStorage — the `/mnt/state` mount — is reset on every
5
- * runtime VERSION UPDATE, i.e. on EVERY deploy ("On runtime version update: Data wiped — fresh file
6
- * system on next invoke", AWS file-system configuration docs), and again after 14 idle days. The
7
- * mount is therefore a fast LOCAL disk, not the source of truth. Without this module a deploy
8
- * silently resurrects the agent with no sessions, no channel dedup, no pending wake-ups — and the
9
- * wake ALARM, which lives in the operator's EventBridge and survives independently, still fires into
10
- * that empty store: a miss with no error anywhere (exactly the silent-failure class the repo's
11
- * fail-visibly rule exists to prevent).
12
- *
13
- * The durable copy is ONE S3 object reached through PRESIGNED URLS minted per-envelope by the
14
- * forwarder Lambda. The container holds no AWS credentials (the platform injects none — verified on
15
- * a live deployment) and stays AWS-SDK-free: a snapshot is one `fetch` GET and one `fetch` PUT.
16
- *
17
- * Format: gzip(JSON `{ v, files: { relPath: base64 } }`). Deliberately NOT tar — the state root is a
18
- * handful of small JSON/JSONL files, and a single self-describing object makes restore ATOMIC: a
19
- * half-applied state root is far worse than a slightly stale one.
20
- */
21
- import { Buffer } from "node:buffer";
22
- import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
23
- import { dirname, join, relative, sep } from "node:path";
24
- import { gunzipSync, gzipSync } from "node:zlib";
25
- import { log } from "../log.js";
26
- import { beginWork } from "./busy.js";
27
- /** Snapshot envelope version — an unknown version fails the restore loudly (never a silent skip). */
28
- export const SNAPSHOT_VERSION = 1;
29
- /** Refuse to pack beyond this (before gzip): a runaway state root would OOM the microVM silently. */
30
- export const MAX_SNAPSHOT_BYTES = 64 << 20;
31
- /** Warn past this — the operator should know the snapshot is getting expensive to round-trip. */
32
- const WARN_SNAPSHOT_BYTES = 16 << 20;
33
- /** Upload deadline. Generous (a large snapshot on a cold network) but finite. */
34
- const PUT_TIMEOUT_MS = 60_000;
35
- /**
36
- * Files the snapshot must NOT carry. `control.json` is a PER-BOOT artifact (this process's control
37
- * URL + token, written once the port is known): snapshotting it would hand the next boot a file
38
- * advertising a dead endpoint and a token that no longer matches the one in memory.
39
- *
40
- * Everything else is durable and restores VERBATIM — including `auth.json`. The deploy seeds that
41
- * file from the builder machine, but the box's own copy is the one that has been REFRESHED, and this
42
- * snapshot is its volume: the same rule every other host states ("a credential already refreshed on
43
- * the volume is never overwritten"). The seed is bootstrap for a snapshot that has none.
44
- *
45
- * That sentence is only true because the generated template points FASTAGENT_SECRETS_DIR INSIDE the
46
- * state root (deploy/agentcore/plan.ts `SECRETS_DIR`) — nothing here special-cases credentials, the
47
- * walk below simply reaches them. Moving the secrets dir back out (e.g. to the sibling layout the
48
- * volume-backed hosts use) silently un-does it: the file stops being copied, the platform wipes it
49
- * with the microVM, and a single-use OAuth refresh token dies with it. `restores VERBATIM` is a
50
- * statement about where that directory is, not a property this module can enforce on its own.
51
- */
52
- const EXCLUDED = new Set(["control.json"]);
53
- /** Every regular file under `root`, as root-relative POSIX paths (stable across platforms). */
54
- async function walk(root, dir = root, out = []) {
55
- let entries;
56
- try {
57
- entries = await readdir(dir, { withFileTypes: true });
58
- }
59
- catch (e) {
60
- if (e.code === "ENOENT")
61
- return out;
62
- throw e;
63
- }
64
- for (const entry of entries) {
65
- const full = join(dir, entry.name);
66
- // Symlinks/sockets/FIFOs are skipped on purpose: the state root holds plain files, and
67
- // AgentCore's session storage does not support special files anyway.
68
- if (entry.isDirectory())
69
- await walk(root, full, out);
70
- else if (entry.isFile())
71
- out.push(relative(root, full).split(sep).join("/"));
72
- }
73
- return out;
74
- }
75
- /** Pack the whole state root into one gzipped snapshot object.
76
- * ponytail: the WHOLE root, every idle edge — O(state size) per settled turn, and session jsonl
77
- * grows without bound. Upgrade path when the 16 MiB warning is real for someone: a per-file
78
- * manifest with content hashes, uploading only what changed. */
79
- export async function packStateRoot(stateRoot, maxBytes = MAX_SNAPSHOT_BYTES) {
80
- const files = {};
81
- let raw = 0;
82
- for (const rel of await walk(stateRoot)) {
83
- if (EXCLUDED.has(rel))
84
- continue;
85
- const content = await readFile(join(stateRoot, rel));
86
- raw += content.byteLength;
87
- if (raw > maxBytes) {
88
- throw new Error(`state root exceeds ${maxBytes} bytes — it cannot be snapshotted for cross-deploy durability`);
89
- }
90
- files[rel] = content.toString("base64");
91
- }
92
- if (raw > WARN_SNAPSHOT_BYTES) {
93
- log.warn(`[agentcore] state snapshot is large (${Math.round(raw / (1 << 20))} MiB) — every turn round-trips it`);
94
- }
95
- return gzipSync(Buffer.from(JSON.stringify({ v: SNAPSHOT_VERSION, files })));
96
- }
97
- /** Apply a snapshot over the state root. Returns how many files were written. */
98
- export async function unpackIntoStateRoot(stateRoot, packed) {
99
- let snapshot;
100
- try {
101
- snapshot = JSON.parse(gunzipSync(packed).toString());
102
- }
103
- catch (e) {
104
- throw new Error(`state snapshot is unreadable (${String(e)})`);
105
- }
106
- if (snapshot?.v !== SNAPSHOT_VERSION || typeof snapshot.files !== "object" || snapshot.files === null) {
107
- throw new Error(`state snapshot has an unsupported shape (v=${String(snapshot?.v)})`);
108
- }
109
- let written = 0;
110
- for (const [rel, b64] of Object.entries(snapshot.files)) {
111
- // A snapshot is written by this same code, but it arrives over the network: refuse anything
112
- // that could escape the state root.
113
- if (rel.startsWith("/") || rel.split("/").includes("..")) {
114
- throw new Error(`state snapshot contains an unsafe path (${rel})`);
115
- }
116
- if (EXCLUDED.has(rel))
117
- continue; // never write a per-boot artifact from an older boot
118
- const target = join(stateRoot, rel);
119
- await mkdir(dirname(target), { recursive: true });
120
- await writeFile(target, Buffer.from(b64, "base64"));
121
- written += 1;
122
- }
123
- return written;
124
- }
125
- export function createStateSync(options) {
126
- const { stateRoot } = options;
127
- const doFetch = options.fetchImpl ?? fetch;
128
- let urls;
129
- let restore;
130
- let restored = false;
131
- let saving;
132
- let queued = false;
133
- // Whether the upload loop is still able to pick up another round. Counting the upload as in-flight
134
- // work (below) means ITS completion is itself a 0-in-flight edge, which re-enters save() — without
135
- // this the snapshot would queue a redundant follow-up after every single upload.
136
- let looping = false;
137
- const runRestore = async (urls) => {
138
- const res = await doFetch(urls.getUrl, { method: "GET" });
139
- // ONLY a proven 404 is "first deploy". A missing key answers 404 only because the generated
140
- // template grants the signer s3:ListBucket on the snapshot prefix — without it S3 folds "absent"
141
- // into 403 (anti-enumeration). A 403 therefore means an expired or malformed signature, a
142
- // revoked permission, or a template from before that grant — i.e. the snapshot may well exist.
143
- // Reading 403 as "absent" would serve an empty agent and then overwrite the real snapshot with
144
- // that emptiness.
145
- if (res.status === 404) {
146
- log.info("[agentcore] no state snapshot yet — starting from an empty state root (first deploy)");
147
- restored = true;
148
- return;
149
- }
150
- if (!res.ok) {
151
- const hint = res.status === 403
152
- ? " (an expired presigned URL, a revoked permission, or a template generated before the " +
153
- "ForwarderRole granted s3:ListBucket — S3 answers 403 even for a MISSING first-deploy " +
154
- "snapshot without it; regenerate with `fastagent deploy agentcore --force` and redeploy)"
155
- : "";
156
- throw new Error(`state snapshot GET failed: ${res.status}${hint}`);
157
- }
158
- const written = await unpackIntoStateRoot(stateRoot, Buffer.from(await res.arrayBuffer()));
159
- log.info(`[agentcore] restored ${written} state file(s) from the snapshot`);
160
- restored = true;
161
- };
162
- const refreshUrls = async () => {
163
- if (!urls?.refresh)
164
- return;
165
- const res = await doFetch(urls.refresh.url, {
166
- method: "POST",
167
- headers: { "content-type": "application/json" },
168
- body: JSON.stringify({ auth: urls.refresh.auth }),
169
- signal: AbortSignal.timeout(PUT_TIMEOUT_MS),
170
- });
171
- if (!res.ok)
172
- throw new Error(`state snapshot URL refresh failed: ${res.status}`);
173
- const fresh = (await res.json());
174
- if (typeof fresh.getUrl !== "string" || typeof fresh.putUrl !== "string") {
175
- throw new Error("state snapshot URL refresh returned an invalid response");
176
- }
177
- urls = { ...urls, getUrl: fresh.getUrl, putUrl: fresh.putUrl };
178
- };
179
- const runSave = async () => {
180
- // Counted as in-flight work for its whole duration: `save()` fires on the 0-in-flight edge, the
181
- // exact moment /ping starts answering Healthy — without this the platform may reclaim the microVM
182
- // mid-upload and the turn that just finished is lost with only a log line. Bounded too: a hung
183
- // PUT would otherwise pin `saving` forever and block every later snapshot.
184
- const workDone = beginWork();
185
- looping = true;
186
- try {
187
- do {
188
- queued = false;
189
- if (!restored || !urls)
190
- return;
191
- // A webhook turn may settle hours after its envelope. Re-mint immediately before every PUT
192
- // instead of assuming the Lambda credentials that signed the envelope outlive the turn.
193
- await refreshUrls();
194
- const body = await packStateRoot(stateRoot);
195
- const res = await doFetch(urls.putUrl, {
196
- method: "PUT",
197
- body: new Uint8Array(body),
198
- signal: AbortSignal.timeout(PUT_TIMEOUT_MS),
199
- });
200
- if (!res.ok) {
201
- const hint = res.status === 403 ? " (presigned URL or its temporary signing credentials may have expired)" : "";
202
- throw new Error(`state snapshot PUT failed: ${res.status}${hint}`);
203
- }
204
- } while (queued);
205
- }
206
- finally {
207
- looping = false;
208
- workDone();
209
- }
210
- };
211
- return {
212
- use(next) {
213
- urls = next;
214
- },
215
- configured() {
216
- return urls !== undefined;
217
- },
218
- ready() {
219
- // No URLs = not an ingress envelope (a direct invoke has its own isolated storage): nothing to
220
- // restore, and deliberately NOT cached, so the first envelope that does carry them still runs
221
- // the restore.
222
- if (!urls)
223
- return Promise.resolve();
224
- // One attempt per process otherwise: a rejected restore stays rejected so every subsequent
225
- // envelope fails the same visible way instead of quietly serving an empty agent.
226
- restore ??= runRestore(urls);
227
- return restore;
228
- },
229
- save() {
230
- if (!restored)
231
- return; // never overwrite a good snapshot with a state root we failed to fill
232
- if (saving) {
233
- if (looping)
234
- queued = true; // otherwise this is the upload's own completion edge, not new work
235
- return;
236
- }
237
- saving = runSave()
238
- .catch((e) => {
239
- log.error(`[agentcore] could not save the state snapshot: ${String(e)} — retrying when work next settles`);
240
- })
241
- .finally(() => {
242
- saving = undefined;
243
- });
244
- },
245
- async flush() {
246
- while (saving)
247
- await saving;
248
- },
249
- async checkpoint() {
250
- if (!urls) {
251
- return { written: false, reason: "this session has never served a forwarder envelope" };
252
- }
253
- if (!restored) {
254
- return { written: false, reason: "the state root is not authoritative here (nothing restored)" };
255
- }
256
- // Never overlap an in-flight upload, then run a FRESH one: joining the running round could
257
- // return before the bytes written moments ago (the interrupted turn's intent) are included.
258
- while (saving)
259
- await saving;
260
- const run = runSave();
261
- // Stored form never rejects (an unawaited rejection would be an unhandled crash); the caller
262
- // awaits `run` itself and gets the error.
263
- saving = run.then(() => { }, () => { });
264
- const settle = saving.finally(() => {
265
- saving = undefined;
266
- });
267
- try {
268
- await run;
269
- }
270
- finally {
271
- await settle;
272
- }
273
- return { written: true };
274
- },
275
- };
276
- }
@@ -1,31 +0,0 @@
1
- import type { Schema } from "@octokit/webhooks-types";
2
- import type { ChannelModule } from "../../channel.ts";
3
- /** A verified GitHub webhook event. Header fields plus the official typed payload. */
4
- export interface GithubEvent {
5
- /** `X-GitHub-Event` (e.g. "pull_request", "issue_comment"). */
6
- event: string;
7
- /** `payload.action` (e.g. "opened"), when present — the usual routing discriminant. */
8
- action?: string;
9
- /** `X-GitHub-Delivery` — unique per delivery. */
10
- deliveryId: string;
11
- /** The native payload (union of all events); narrow it, e.g. `if ("pull_request" in event.payload)`. */
12
- payload: Schema;
13
- }
14
- /** What `on` returns per acted-on delivery: a session + the prompt text for the agent turn. */
15
- export interface Intent {
16
- session: string;
17
- text: string;
18
- }
19
- export interface GithubChannelOptions {
20
- /** Webhook secret — verifies inbound deliveries (HMAC-SHA256 over the raw body). */
21
- secret: string;
22
- /** Map a verified event to the intents this agent acts on (empty array = ignore). */
23
- on: (event: GithubEvent) => Intent[];
24
- }
25
- /**
26
- * Build a GitHub webhook channel: policy options in, a {@link ChannelModule} out (mounts
27
- * `POST /webhook`). `agent` arrives via the mount context. The adapter owns that route key; to serve
28
- * it elsewhere, re-key the returned module:
29
- * `(ctx) => ({ "POST /gh": githubChannel(opts)(ctx)["POST /webhook"]! })`.
30
- */
31
- export declare function githubChannel({ secret, on }: GithubChannelOptions): ChannelModule;
@@ -1,89 +0,0 @@
1
- /**
2
- * GitHub webhook channel: verify → route via `on(event)` → fire-and-forget agent turns, ACK 202.
3
- * The developer writes only `on`. Concurrency safety is the engine's per-session lease. A turn that
4
- * fails after the 202, or an in-flight turn on shutdown, is lost (server log only).
5
- */
6
- import { verify } from "@octokit/webhooks-methods";
7
- import { collect } from "../../collect.js";
8
- import { log } from "../../log.js";
9
- import { readBodyCapped } from "../body.js";
10
- import { beginWork } from "../busy.js";
11
- import { text } from "../respond.js";
12
- /** Raw body cap before verification — GitHub caps webhook payloads at 25 MB; reject larger early. */
13
- const MAX_WEBHOOK_BYTES = 25 << 20;
14
- /**
15
- * Build a GitHub webhook channel: policy options in, a {@link ChannelModule} out (mounts
16
- * `POST /webhook`). `agent` arrives via the mount context. The adapter owns that route key; to serve
17
- * it elsewhere, re-key the returned module:
18
- * `(ctx) => ({ "POST /gh": githubChannel(opts)(ctx)["POST /webhook"]! })`.
19
- */
20
- export function githubChannel({ secret, on }) {
21
- const channel = ({ agent }) => ({
22
- "POST /webhook": async (req) => {
23
- if (req.method !== "POST")
24
- return text("POST only\n", 405);
25
- // Cap before verify: a public endpoint must not buffer an unbounded body (chunked bypasses Content-Length).
26
- const body = await readBodyCapped(req, MAX_WEBHOOK_BYTES);
27
- if ("tooLarge" in body)
28
- return text("payload too large\n", 413);
29
- const raw = body.text;
30
- // Fail closed: verify() throws on an empty/missing arg, so treat any verify error as a clean 401.
31
- const signature = req.headers.get("x-hub-signature-256");
32
- if (!signature || !(await verify(secret, raw, signature).catch(() => false))) {
33
- return text("invalid signature\n", 401);
34
- }
35
- const eventName = req.headers.get("x-github-event") ?? "";
36
- if (eventName === "ping")
37
- return new Response(null, { status: 204 });
38
- // x-www-form-urlencoded (GitHub's UI default) wraps the JSON in a `payload` field; json is the body.
39
- let json = raw;
40
- if ((req.headers.get("content-type") ?? "").includes("application/x-www-form-urlencoded")) {
41
- const field = new URLSearchParams(raw).get("payload");
42
- if (field === null)
43
- return text("missing form payload\n", 400);
44
- json = field;
45
- }
46
- let payload;
47
- try {
48
- payload = JSON.parse(json);
49
- }
50
- catch {
51
- return text("invalid json\n", 400);
52
- }
53
- const event = {
54
- event: eventName,
55
- action: typeof payload.action === "string" ? payload.action : undefined,
56
- deliveryId: req.headers.get("x-github-delivery") ?? "",
57
- payload: payload, // trust boundary: the verified body is a GitHub event
58
- };
59
- // Fire each turn, return 202; the process runs them to completion. The lifecycle is logged to
60
- // stderr — after the 202 there is no response body, so these lines are the operator's only signal
61
- // (and the sink that keeps a post-ACK error from going unhandled).
62
- const intents = on(event);
63
- const label = event.action ? `${event.event}.${event.action}` : event.event;
64
- for (let i = 0; i < intents.length; i++) {
65
- const { session, text } = intents[i];
66
- // Per-turn correlation id (deliveryId is unique per webhook; the index disambiguates fan-out),
67
- // threaded through start/done/failed so a terminal line joins back to its start.
68
- const turn = `${event.deliveryId}#${i}`;
69
- log.info(`[github] turn start: turn=${turn} session=${session} event=${label}`);
70
- const startedAt = Date.now();
71
- // Post-ACK turns are process-wide in-flight work (busy.ts): a serving surface that must not
72
- // idle mid-turn (the AgentCore /ping's HealthyBusy) has no other way to see them — and github
73
- // turns have NO replay, so an idle reclaim here loses the review outright.
74
- const workDone = beginWork();
75
- void collect(agent.invoke({ session }, { text }))
76
- .then(() => log.info(`[github] turn done: turn=${turn} session=${session} (${Date.now() - startedAt}ms)`), (error) => log.error(`[github] turn failed: turn=${turn} session=${session} (${Date.now() - startedAt}ms): ${String(error)}`))
77
- .finally(workDone);
78
- }
79
- return new Response(null, { status: 202 });
80
- },
81
- });
82
- return (ctx) => {
83
- // Validate at activation so deploy may inspect the module shape before secrets are provisioned.
84
- if (!secret) {
85
- throw new Error("githubChannel requires a non-empty secret (the GitHub webhook secret, e.g. GITHUB_WEBHOOK_SECRET)");
86
- }
87
- return channel(ctx);
88
- };
89
- }
@@ -1,25 +0,0 @@
1
- import { githubChannel } from "@fastagent-sh/fastagent/github";
2
-
3
- // A channel = a third-party ADAPTER (githubChannel: verify + parse + ACK) configured with YOUR on() policy.
4
- // fastagent discovers this file under channels/, mounts POST /webhook, and pipes the agent to the
5
- // adapter — this file holds only policy. Set GITHUB_WEBHOOK_SECRET in .env (a missing secret fails at
6
- // startup — an empty key would accept forged deliveries) and point a GitHub webhook (JSON) at POST /webhook.
7
- export default githubChannel({
8
- secret: process.env.GITHUB_WEBHOOK_SECRET ?? "",
9
- // Map a verified event to the intents the agent acts on (empty array = ignore). Each review is
10
- // INDEPENDENT and idempotent (it reconciles against the PR's existing comments), so use a
11
- // distinct per-delivery session (event.deliveryId): overlapping deliveries then run on their
12
- // own session without a shared-lease drop.
13
- on: (event) => {
14
- if (event.event === "pull_request" && event.action === "opened" && "pull_request" in event.payload) {
15
- const { repository, pull_request } = event.payload;
16
- return [
17
- {
18
- session: event.deliveryId,
19
- text: `Review pull request #${pull_request.number} in ${repository.full_name}.`,
20
- },
21
- ];
22
- }
23
- return [];
24
- },
25
- });
@@ -1,108 +0,0 @@
1
- import { type SessionControl, type SessionEntry } from "../../session.ts";
2
- export interface AttachOptions {
3
- /** Override the control endpoint (skip control.json discovery) — for a remote serve. */
4
- url?: string;
5
- token?: string;
6
- }
7
- export declare function runAttach(sessionArg: string, dirArg: string | undefined, opts: AttachOptions): Promise<void>;
8
- /** One round's observed facts, gathered by the loop (IO) and judged by {@link decideRound} (pure). */
9
- export type RoundOutcome = {
10
- type: "progress";
11
- }
12
- /** Clean end that delivered nothing — indistinguishable from a half-dead proxy closing every stream. */
13
- | {
14
- type: "empty";
15
- } | {
16
- type: "error";
17
- error: unknown;
18
- isAuth: boolean;
19
- /** discover(dir) compared against the current endpoint; remote endpoints report "unavailable". */
20
- discovery: "unchanged" | "changed" | "unavailable";
21
- /** The freshly discovered credentials when {@link discovery} is "changed". */
22
- fresh?: {
23
- url: string;
24
- token: string;
25
- };
26
- };
27
- export type RoundDecision = {
28
- kind: "reset";
29
- } | {
30
- kind: "exit";
31
- message: string;
32
- } | {
33
- kind: "try-reattach";
34
- fresh: {
35
- url: string;
36
- token: string;
37
- };
38
- } | {
39
- kind: "retry";
40
- warn: string;
41
- };
42
- /**
43
- * The reconnect policy for BOTH phases (`startup` = connectWithGrace, `steady` = the round loop),
44
- * pure and testable: every exit diagnosis and budget claim lives here. `downMs` is wall-clock time
45
- * since the phase's anchor (first connect attempt / first non-progress round). The phases differ
46
- * on priors, not principle: startup has never succeeded (short budget; a remote non-auth error
47
- * exits at once), steady-state had a working attach (longer budgets); the local-401-unchanged
48
- * diagnosis and reattach-on-changed-credentials apply identically to both.
49
- */
50
- export declare function decideRound(outcome: RoundOutcome, ctx: {
51
- discovered: boolean;
52
- downMs: number;
53
- phase?: "startup" | "steady";
54
- }): RoundDecision;
55
- /** What one ROUND prints through (streamed deltas included), so tests can observe every path a
56
- * round writes. Round output only: stdin-side acceptance/rejection lines print directly — they
57
- * are immediate feedback to a user action and may interleave with a replay block. Timing knobs
58
- * travel as their own parameter. */
59
- export interface AttachIo {
60
- println: (line: string) => void;
61
- /** Raw chunk, no newline — the streamed message_delta path. */
62
- write: (chunk: string) => void;
63
- warn: (line: string) => void;
64
- }
65
- /**
66
- * The backfill slice REDUCED TO THE ACTIVE PATH — a session is a tree (a leaf move, or a
67
- * compaction, leaves sibling branches behind), and printing the slice raw renders the abandoned
68
- * branch interleaved with the live one as if it were one conversation. Reachability is computed
69
- * backwards from the leaf; an entry whose parent lies BEFORE the slice is path-connected by
70
- * construction (its ancestors were rendered in an earlier round), so only what the walk cannot
71
- * reach is dropped. An engine that reports NO leaf says nothing about branches, so its slice stands
72
- * whole — the one case where "unknown" must not read as "off-path".
73
- */
74
- export declare function activePathSlice(entries: SessionEntry[], leafEntryId: string | undefined): SessionEntry[];
75
- /**
76
- * Answer a RESERVED-SLASH line (anything starting with `/` that is not `/abort`). Two intents share
77
- * the prefix — a mistyped control command and an attempt to invoke a name — and they are answered
78
- * differently:
79
- *
80
- * - a mistyped slash gets the certain half NOW (a leading `/` is reserved here, whatever the token
81
- * turns out to be), because waiting on a remote read that can be slow or fail would leave the
82
- * input unanswered; it deliberately does not pre-judge the token as unknown — the read may be
83
- * about to prove it names a real skill;
84
- * - `/commands` prints nothing first: its whole answer IS the read, and a placeholder is noise;
85
- * - the enumeration answers `/commands` ONLY — dumping every skill at a mistyped `/aboort` answers
86
- * an intent the typo did not express.
87
- *
88
- * Names print BARE: this composer cannot expand `/name` (the data plane takes prompts as text), so
89
- * printing them with a slash would invite the user straight back into this branch. Returns the
90
- * promise for the remote half, so a caller (or a test) can await the second line.
91
- */
92
- export declare function answerSlashInput(trimmed: string, control: Pick<SessionControl, "commands">, println: (line: string) => void): Promise<void>;
93
- /**
94
- * ONE attach round: subscribe → backfill (render the durable record since `cursor`) → drain live
95
- * until the stream drops. Returns the advanced cursor. Subscribing first + the server's eager
96
- * registration (subscribed before response headers) covers the cursor→subscription window in the
97
- * normal case; `settleMs` is a HEURISTIC — on a very slow link an event can still land between the
98
- * backfill and the subscription taking effect, surfacing only at the next round's replay. Live
99
- * events carry no entry ids, so the cursor advances only on backfill; a replay may overlap what
100
- * was already seen live — labeled, not silently dropped. A 401 (stale token after a serve restart)
101
- * is thrown to the caller: unrecoverable here, never retried silently.
102
- */
103
- export declare function attachRound(control: SessionControl, session: string, cursor: string | undefined, io: AttachIo,
104
- /** The subscribe→sync settle heuristic (see the round comment). Tests shrink it. */
105
- settleMs?: number): Promise<{
106
- cursor: string | undefined;
107
- sawProgress: boolean;
108
- }>;