@fastagent-sh/fastagent 0.24.4 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (487) hide show
  1. package/README.md +54 -82
  2. package/dist/agent-api.d.ts +460 -0
  3. package/dist/agent-api.js +209 -0
  4. package/dist/answer-text.d.ts +8 -0
  5. package/dist/answer-text.js +18 -0
  6. package/dist/atomic-write.d.ts +2 -2
  7. package/dist/atomic-write.js +2 -2
  8. package/dist/authoring.d.ts +81 -0
  9. package/dist/authoring.js +245 -0
  10. package/dist/channel.d.ts +29 -15
  11. package/dist/channel.js +4 -1
  12. package/dist/channels/agentcore-protocol.d.ts +36 -50
  13. package/dist/channels/agentcore-protocol.js +3 -3
  14. package/dist/channels/agentcore-service.d.ts +8 -18
  15. package/dist/channels/agentcore-service.js +108 -127
  16. package/dist/channels/agentcore.d.ts +8 -24
  17. package/dist/channels/agentcore.js +32 -65
  18. package/dist/channels/body.d.ts +0 -16
  19. package/dist/channels/body.js +0 -25
  20. package/dist/channels/busy.js +6 -1
  21. package/dist/channels/context.d.ts +4 -0
  22. package/dist/channels/context.js +35 -0
  23. package/dist/channels/discover.d.ts +18 -20
  24. package/dist/channels/discover.js +41 -99
  25. package/dist/channels/feishu/feishu-api.d.ts +54 -4
  26. package/dist/channels/feishu/feishu-api.js +192 -149
  27. package/dist/channels/feishu/feishu.d.ts +3 -0
  28. package/dist/channels/feishu/feishu.js +117 -106
  29. package/dist/channels/feishu/history.d.ts +55 -0
  30. package/dist/channels/feishu/history.js +199 -0
  31. package/dist/channels/feishu/invoke-turn.d.ts +13 -11
  32. package/dist/channels/feishu/invoke-turn.js +9 -10
  33. package/dist/channels/feishu/model.d.ts +2 -1
  34. package/dist/channels/feishu/normalize.js +100 -0
  35. package/dist/channels/feishu/preview.d.ts +2 -2
  36. package/dist/channels/feishu/register-webhook.d.ts +1 -0
  37. package/dist/channels/feishu/register-webhook.js +3 -2
  38. package/dist/channels/feishu/scaffold/channel.ts +65 -46
  39. package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
  40. package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
  41. package/dist/channels/feishu/setup-mode.d.ts +45 -25
  42. package/dist/channels/feishu/setup-mode.js +74 -23
  43. package/dist/channels/feishu/shared-api.d.ts +24 -6
  44. package/dist/channels/feishu/shared-api.js +30 -24
  45. package/dist/channels/kit/context-buffer.d.ts +11 -2
  46. package/dist/channels/kit/context-buffer.js +7 -4
  47. package/dist/channels/kit/delivery.d.ts +19 -3
  48. package/dist/channels/kit/delivery.js +74 -15
  49. package/dist/channels/kit/event-stream.js +2 -1
  50. package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
  51. package/dist/channels/kit/invoke-turn-kit.js +62 -71
  52. package/dist/channels/kit/place-history.d.ts +107 -0
  53. package/dist/channels/kit/place-history.js +182 -0
  54. package/dist/channels/kit/preview-kit.d.ts +9 -5
  55. package/dist/channels/kit/preview-kit.js +36 -15
  56. package/dist/channels/kit/room-threads.d.ts +46 -0
  57. package/dist/channels/kit/room-threads.js +81 -0
  58. package/dist/channels/kit/seen.js +1 -1
  59. package/dist/channels/kit/stop-command.d.ts +6 -3
  60. package/dist/channels/kit/stop-command.js +20 -18
  61. package/dist/channels/kit/thread-fork.d.ts +17 -0
  62. package/dist/channels/kit/thread-fork.js +43 -0
  63. package/dist/channels/kit/thread-participants.js +1 -1
  64. package/dist/channels/kit/transport.d.ts +5 -0
  65. package/dist/channels/kit/transport.js +5 -0
  66. package/dist/channels/kit/turn-runner.d.ts +10 -9
  67. package/dist/channels/kit/turn-runner.js +17 -19
  68. package/dist/channels/kit/turn-store.d.ts +5 -4
  69. package/dist/channels/kit/turn-store.js +5 -5
  70. package/dist/channels/lark/lark.d.ts +3 -0
  71. package/dist/channels/lark/lark.js +5 -0
  72. package/dist/channels/lark/onboard.d.ts +5 -3
  73. package/dist/channels/lark/onboard.js +14 -14
  74. package/dist/channels/lark/scaffold/channel.ts +67 -44
  75. package/dist/channels/lark/scaffold/lark-send.ts +38 -33
  76. package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
  77. package/dist/channels/respond.d.ts +0 -4
  78. package/dist/channels/respond.js +1 -1
  79. package/dist/channels/router.d.ts +18 -0
  80. package/dist/channels/router.js +68 -0
  81. package/dist/channels/serve.d.ts +5 -70
  82. package/dist/channels/serve.js +105 -287
  83. package/dist/channels/slack/history.d.ts +42 -0
  84. package/dist/channels/slack/history.js +282 -0
  85. package/dist/channels/slack/invoke-turn.d.ts +12 -5
  86. package/dist/channels/slack/invoke-turn.js +7 -5
  87. package/dist/channels/slack/manifest.d.ts +1 -4
  88. package/dist/channels/slack/manifest.js +22 -10
  89. package/dist/channels/slack/model.d.ts +2 -1
  90. package/dist/channels/slack/onboard.d.ts +0 -2
  91. package/dist/channels/slack/onboard.js +2 -4
  92. package/dist/channels/slack/onboarding-state.d.ts +0 -2
  93. package/dist/channels/slack/onboarding-state.js +0 -1
  94. package/dist/channels/slack/parse.d.ts +0 -5
  95. package/dist/channels/slack/parse.js +1 -16
  96. package/dist/channels/slack/preview.d.ts +2 -3
  97. package/dist/channels/slack/preview.js +78 -52
  98. package/dist/channels/slack/register-webhook.d.ts +1 -0
  99. package/dist/channels/slack/register-webhook.js +6 -5
  100. package/dist/channels/slack/scaffold/channel.ts +23 -24
  101. package/dist/channels/slack/scaffold/slack-send.ts +50 -43
  102. package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
  103. package/dist/channels/slack/setup-server.d.ts +19 -0
  104. package/dist/channels/slack/setup-server.js +113 -11
  105. package/dist/channels/slack/shared-api.d.ts +15 -4
  106. package/dist/channels/slack/shared-api.js +19 -14
  107. package/dist/channels/slack/slack-api.d.ts +61 -2
  108. package/dist/channels/slack/slack-api.js +28 -5
  109. package/dist/channels/slack/slack.js +97 -52
  110. package/dist/channels/slack/welcomed.js +1 -1
  111. package/dist/channels/telegram/context-buffer.d.ts +6 -1
  112. package/dist/channels/telegram/context-buffer.js +19 -0
  113. package/dist/channels/telegram/invoke-turn.d.ts +8 -4
  114. package/dist/channels/telegram/invoke-turn.js +7 -7
  115. package/dist/channels/telegram/parse.d.ts +7 -2
  116. package/dist/channels/telegram/parse.js +7 -0
  117. package/dist/channels/telegram/preview.d.ts +2 -2
  118. package/dist/channels/telegram/register-webhook.d.ts +1 -0
  119. package/dist/channels/telegram/register-webhook.js +3 -2
  120. package/dist/channels/telegram/scaffold/channel.ts +27 -28
  121. package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
  122. package/dist/channels/telegram/shared-api.d.ts +34 -0
  123. package/dist/channels/telegram/shared-api.js +79 -0
  124. package/dist/channels/telegram/telegram-api.d.ts +15 -5
  125. package/dist/channels/telegram/telegram-api.js +10 -6
  126. package/dist/channels/telegram/telegram.js +27 -21
  127. package/dist/channels/telegram/turn-store.js +1 -1
  128. package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
  129. package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
  130. package/dist/cli/add-feishu.d.ts +42 -12
  131. package/dist/cli/add-feishu.js +177 -124
  132. package/dist/cli/add-slack.d.ts +8 -3
  133. package/dist/cli/add-slack.js +116 -64
  134. package/dist/cli/auth-view.d.ts +10 -3
  135. package/dist/cli/auth-view.js +8 -6
  136. package/dist/cli/box-login.d.ts +18 -7
  137. package/dist/cli/box-login.js +46 -12
  138. package/dist/cli/commands/add.d.ts +15 -4
  139. package/dist/cli/commands/add.js +253 -143
  140. package/dist/cli/commands/auth.d.ts +6 -0
  141. package/dist/cli/commands/auth.js +50 -0
  142. package/dist/cli/commands/content.d.ts +6 -0
  143. package/dist/cli/commands/content.js +45 -0
  144. package/dist/cli/commands/deploy/agentcore.js +50 -77
  145. package/dist/cli/commands/deploy/docker.js +38 -39
  146. package/dist/cli/commands/deploy/fly.js +21 -18
  147. package/dist/cli/commands/deploy/host-login.d.ts +26 -0
  148. package/dist/cli/commands/deploy/host-login.js +90 -0
  149. package/dist/cli/commands/deploy/railway.js +21 -26
  150. package/dist/cli/commands/deploy/shared.d.ts +45 -17
  151. package/dist/cli/commands/deploy/shared.js +34 -15
  152. package/dist/cli/commands/deploy.d.ts +0 -4
  153. package/dist/cli/commands/deploy.js +237 -50
  154. package/dist/cli/commands/destroy.js +8 -6
  155. package/dist/cli/commands/dev.d.ts +0 -2
  156. package/dist/cli/commands/dev.js +20 -19
  157. package/dist/cli/commands/env.d.ts +12 -0
  158. package/dist/cli/commands/env.js +191 -0
  159. package/dist/cli/commands/info.js +95 -156
  160. package/dist/cli/commands/init.d.ts +2 -2
  161. package/dist/cli/commands/init.js +35 -33
  162. package/dist/cli/commands/invoke.d.ts +6 -0
  163. package/dist/cli/commands/invoke.js +132 -16
  164. package/dist/cli/commands/login.d.ts +31 -2
  165. package/dist/cli/commands/login.js +416 -65
  166. package/dist/cli/commands/logout.d.ts +4 -0
  167. package/dist/cli/commands/logout.js +53 -0
  168. package/dist/cli/commands/logs.js +8 -6
  169. package/dist/cli/commands/models.d.ts +2 -1
  170. package/dist/cli/commands/models.js +39 -37
  171. package/dist/cli/commands/schedules.d.ts +21 -0
  172. package/dist/cli/commands/schedules.js +143 -0
  173. package/dist/cli/commands/start.d.ts +11 -10
  174. package/dist/cli/commands/start.js +35 -62
  175. package/dist/cli/content-view.d.ts +9 -0
  176. package/dist/cli/content-view.js +26 -0
  177. package/dist/cli/fail.d.ts +9 -12
  178. package/dist/cli/fail.js +17 -14
  179. package/dist/cli/invoke-stream.d.ts +12 -3
  180. package/dist/cli/invoke-stream.js +85 -23
  181. package/dist/cli/kernel.d.ts +9 -2
  182. package/dist/cli/kernel.js +15 -6
  183. package/dist/cli/login-relay.d.ts +6 -4
  184. package/dist/cli/login-relay.js +19 -7
  185. package/dist/cli/models-view.d.ts +5 -1
  186. package/dist/cli/models-view.js +1 -1
  187. package/dist/cli/person.d.ts +10 -0
  188. package/dist/cli/person.js +23 -0
  189. package/dist/cli/program.js +421 -203
  190. package/dist/cli/serve.d.ts +13 -63
  191. package/dist/cli/serve.js +31 -121
  192. package/dist/cli/shared.d.ts +21 -48
  193. package/dist/cli/shared.js +70 -89
  194. package/dist/cli/ticket.d.ts +136 -0
  195. package/dist/cli/ticket.js +476 -0
  196. package/dist/client.d.ts +7 -0
  197. package/dist/client.js +2 -0
  198. package/dist/content/declare.d.ts +70 -0
  199. package/dist/content/declare.js +163 -0
  200. package/dist/content/file.d.ts +32 -0
  201. package/dist/content/file.js +119 -0
  202. package/dist/content/git.d.ts +41 -0
  203. package/dist/content/git.js +220 -0
  204. package/dist/content/mount.d.ts +2 -0
  205. package/dist/content/mount.js +15 -0
  206. package/dist/content/resolve.d.ts +52 -0
  207. package/dist/content/resolve.js +151 -0
  208. package/dist/content/source.d.ts +27 -0
  209. package/dist/content/source.js +53 -0
  210. package/dist/conversation-sessions.d.ts +26 -0
  211. package/dist/conversation-sessions.js +99 -0
  212. package/dist/declared-secrets.d.ts +19 -37
  213. package/dist/declared-secrets.js +16 -58
  214. package/dist/deploy/agentcore/destroy.d.ts +3 -3
  215. package/dist/deploy/agentcore/destroy.js +46 -28
  216. package/dist/deploy/agentcore/forwarder.js +105 -22
  217. package/dist/deploy/agentcore/logs.js +1 -1
  218. package/dist/deploy/agentcore/plan.d.ts +10 -24
  219. package/dist/deploy/agentcore/plan.js +31 -135
  220. package/dist/deploy/agentcore/run.d.ts +1 -1
  221. package/dist/deploy/agentcore/run.js +8 -5
  222. package/dist/deploy/agentcore/shell.d.ts +1 -1
  223. package/dist/deploy/agentcore/shell.js +5 -5
  224. package/dist/deploy/api.d.ts +34 -0
  225. package/dist/deploy/api.js +50 -0
  226. package/dist/deploy/box-shell.d.ts +6 -0
  227. package/dist/deploy/box-shell.js +7 -0
  228. package/dist/deploy/build-context.d.ts +7 -5
  229. package/dist/deploy/build-context.js +41 -35
  230. package/dist/deploy/channel-ingress.d.ts +6 -1
  231. package/dist/deploy/channel-ingress.js +18 -5
  232. package/dist/deploy/container.d.ts +22 -9
  233. package/dist/deploy/container.js +96 -55
  234. package/dist/deploy/docker/plan.d.ts +3 -2
  235. package/dist/deploy/docker/plan.js +13 -14
  236. package/dist/deploy/docker/run.d.ts +2 -2
  237. package/dist/deploy/docker/run.js +8 -4
  238. package/dist/deploy/fly/plan.d.ts +1 -1
  239. package/dist/deploy/fly/plan.js +12 -21
  240. package/dist/deploy/fly/run.d.ts +10 -6
  241. package/dist/deploy/fly/run.js +11 -6
  242. package/dist/deploy/hosts.d.ts +2 -1
  243. package/dist/deploy/hosts.js +2 -1
  244. package/dist/deploy/preflight.d.ts +21 -23
  245. package/dist/deploy/preflight.js +223 -202
  246. package/dist/deploy/railway/plan.d.ts +8 -6
  247. package/dist/deploy/railway/plan.js +15 -58
  248. package/dist/deploy/railway/run.d.ts +8 -3
  249. package/dist/deploy/railway/run.js +12 -8
  250. package/dist/deploy/residency.d.ts +5 -11
  251. package/dist/deploy/residency.js +0 -6
  252. package/dist/deploy/runner.d.ts +1 -1
  253. package/dist/deploy/runner.js +1 -1
  254. package/dist/deploy/secrets.d.ts +16 -2
  255. package/dist/deploy/secrets.js +33 -6
  256. package/dist/deploy/workspace.d.ts +11 -6
  257. package/dist/deploy/workspace.js +55 -38
  258. package/dist/dev-supervisor.d.ts +11 -2
  259. package/dist/dev-supervisor.js +52 -27
  260. package/dist/effect-port.d.ts +4 -4
  261. package/dist/effect-port.js +4 -4
  262. package/dist/env.d.ts +0 -13
  263. package/dist/env.js +1 -44
  264. package/dist/environment/apt.d.ts +4 -0
  265. package/dist/environment/apt.js +32 -0
  266. package/dist/environment/declare.d.ts +59 -0
  267. package/dist/environment/declare.js +210 -0
  268. package/dist/environment/mise.d.ts +51 -0
  269. package/dist/environment/mise.js +272 -0
  270. package/dist/feishu.d.ts +2 -2
  271. package/dist/feishu.js +2 -2
  272. package/dist/git.d.ts +13 -0
  273. package/dist/git.js +55 -0
  274. package/dist/harnesses/models/catalog.d.ts +20 -0
  275. package/dist/harnesses/models/catalog.js +254 -0
  276. package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
  277. package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
  278. package/dist/harnesses/models/environment.d.ts +48 -0
  279. package/dist/harnesses/models/environment.js +88 -0
  280. package/dist/harnesses/models/host.d.ts +30 -0
  281. package/dist/harnesses/models/host.js +97 -0
  282. package/dist/{engines/pi → harnesses/models}/login.d.ts +66 -70
  283. package/dist/harnesses/models/login.js +439 -0
  284. package/dist/harnesses/models/models.d.ts +84 -0
  285. package/dist/harnesses/models/models.js +197 -0
  286. package/dist/harnesses/pi-durable/agent.d.ts +42 -0
  287. package/dist/harnesses/pi-durable/agent.js +581 -0
  288. package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
  289. package/dist/harnesses/pi-durable/defaults.js +26 -0
  290. package/dist/harnesses/pi-durable/define.d.ts +77 -0
  291. package/dist/harnesses/pi-durable/define.js +28 -0
  292. package/dist/harnesses/pi-durable/entries.d.ts +32 -0
  293. package/dist/harnesses/pi-durable/entries.js +126 -0
  294. package/dist/harnesses/pi-durable/events.d.ts +25 -0
  295. package/dist/harnesses/pi-durable/events.js +238 -0
  296. package/dist/harnesses/pi-durable/ids.d.ts +16 -0
  297. package/dist/harnesses/pi-durable/ids.js +55 -0
  298. package/dist/harnesses/pi-durable/messages.d.ts +41 -0
  299. package/dist/harnesses/pi-durable/messages.js +116 -0
  300. package/dist/harnesses/pi-durable/open.d.ts +95 -0
  301. package/dist/harnesses/pi-durable/open.js +330 -0
  302. package/dist/harnesses/pi-durable/reads.d.ts +30 -0
  303. package/dist/harnesses/pi-durable/reads.js +94 -0
  304. package/dist/harnesses/pi-durable/registry.d.ts +18 -0
  305. package/dist/harnesses/pi-durable/registry.js +56 -0
  306. package/dist/harnesses/pi-durable/release.d.ts +13 -0
  307. package/dist/harnesses/pi-durable/release.js +40 -0
  308. package/dist/harnesses/pi-durable/serve.d.ts +90 -0
  309. package/dist/harnesses/pi-durable/serve.js +186 -0
  310. package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
  311. package/dist/harnesses/pi-durable/sessions.js +47 -0
  312. package/dist/harnesses/pi-durable/storage.d.ts +4 -0
  313. package/dist/harnesses/pi-durable/storage.js +49 -0
  314. package/dist/harnesses/pi-durable/stream.d.ts +20 -0
  315. package/dist/harnesses/pi-durable/stream.js +221 -0
  316. package/dist/harnesses/pi-durable/text.d.ts +30 -0
  317. package/dist/harnesses/pi-durable/text.js +292 -0
  318. package/dist/harnesses/pi-durable/wake.d.ts +11 -0
  319. package/dist/harnesses/pi-durable/wake.js +90 -0
  320. package/dist/http/client.d.ts +6 -0
  321. package/dist/http/client.js +256 -0
  322. package/dist/http/contract.d.ts +541 -0
  323. package/dist/http/contract.js +179 -0
  324. package/dist/http/server.d.ts +34 -0
  325. package/dist/http/server.js +194 -0
  326. package/dist/index.d.ts +19 -4
  327. package/dist/index.js +13 -5
  328. package/dist/lark.d.ts +2 -2
  329. package/dist/lark.js +2 -2
  330. package/dist/locked-file.d.ts +17 -0
  331. package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
  332. package/dist/once.d.ts +6 -0
  333. package/dist/once.js +23 -0
  334. package/dist/paths.d.ts +36 -52
  335. package/dist/paths.js +66 -182
  336. package/dist/runtime.d.ts +25 -2
  337. package/dist/runtime.js +27 -0
  338. package/dist/scaffold/add-channel.d.ts +14 -27
  339. package/dist/scaffold/add-channel.js +82 -180
  340. package/dist/scaffold/init.d.ts +17 -18
  341. package/dist/scaffold/init.js +87 -99
  342. package/dist/scaffold/templates/SYSTEM.md +12 -0
  343. package/dist/scaffold/templates/env.example +4 -10
  344. package/dist/scaffold/templates/fastagent.config.ts +21 -33
  345. package/dist/scaffold/templates/gitignore +8 -1
  346. package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
  347. package/dist/scaffold/templates.d.ts +9 -4
  348. package/dist/scaffold/templates.js +7 -5
  349. package/dist/scaffold/vendor-skill.d.ts +1 -4
  350. package/dist/scaffold/vendor-skill.js +5 -6
  351. package/dist/schedule/cron.d.ts +13 -0
  352. package/dist/schedule/cron.js +52 -0
  353. package/dist/schedule/eventbridge-cron.d.ts +17 -0
  354. package/dist/schedule/eventbridge-cron.js +88 -0
  355. package/dist/schedule/schedule.d.ts +23 -0
  356. package/dist/schedule/schedule.js +34 -0
  357. package/dist/schedule/scheduler.d.ts +34 -33
  358. package/dist/schedule/scheduler.js +251 -137
  359. package/dist/schedule/state.d.ts +4 -14
  360. package/dist/schedule/state.js +7 -7
  361. package/dist/schedule/wake-alarm.d.ts +22 -2
  362. package/dist/schedule/wake-alarm.js +66 -20
  363. package/dist/schedule/wakeups.d.ts +16 -15
  364. package/dist/schedule/wakeups.js +47 -45
  365. package/dist/service.d.ts +56 -169
  366. package/dist/service.js +91 -187
  367. package/dist/serving-command.d.ts +10 -0
  368. package/dist/serving-command.js +16 -0
  369. package/dist/sign-in-api.d.ts +147 -0
  370. package/dist/sign-in-api.js +9 -0
  371. package/dist/slack.d.ts +1 -1
  372. package/dist/slack.js +1 -1
  373. package/dist/{channels/kit/state.js → state-file.js} +6 -6
  374. package/dist/telegram.d.ts +1 -0
  375. package/dist/telegram.js +1 -0
  376. package/dist/tunnel.d.ts +3 -0
  377. package/dist/tunnel.js +14 -5
  378. package/dist/value-file.d.ts +40 -0
  379. package/dist/value-file.js +164 -0
  380. package/dist/version.d.ts +5 -0
  381. package/dist/version.js +14 -1
  382. package/package.json +18 -28
  383. package/dist/agent.d.ts +0 -106
  384. package/dist/agent.js +0 -21
  385. package/dist/channels/control.d.ts +0 -31
  386. package/dist/channels/control.js +0 -329
  387. package/dist/channels/define-channel.d.ts +0 -39
  388. package/dist/channels/define-channel.js +0 -9
  389. package/dist/channels/feishu/context-buffer.d.ts +0 -35
  390. package/dist/channels/feishu/context-buffer.js +0 -65
  391. package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
  392. package/dist/channels/http.d.ts +0 -23
  393. package/dist/channels/http.js +0 -57
  394. package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
  395. package/dist/channels/slack/context-buffer.d.ts +0 -23
  396. package/dist/channels/slack/context-buffer.js +0 -37
  397. package/dist/channels/sse.d.ts +0 -4
  398. package/dist/channels/sse.js +0 -66
  399. package/dist/cli/commands/chat.d.ts +0 -3
  400. package/dist/cli/commands/chat.js +0 -14
  401. package/dist/cli/commands/routine-run.d.ts +0 -6
  402. package/dist/cli/commands/routine-run.js +0 -46
  403. package/dist/cli/commands/routine.d.ts +0 -25
  404. package/dist/cli/commands/routine.js +0 -122
  405. package/dist/cli/commands/tool.d.ts +0 -1
  406. package/dist/cli/commands/tool.js +0 -57
  407. package/dist/collect.d.ts +0 -63
  408. package/dist/collect.js +0 -90
  409. package/dist/core.d.ts +0 -13
  410. package/dist/core.js +0 -15
  411. package/dist/engines/pi/agent-models.d.ts +0 -83
  412. package/dist/engines/pi/agent-models.js +0 -120
  413. package/dist/engines/pi/agent-session-factory.d.ts +0 -121
  414. package/dist/engines/pi/agent-session-factory.js +0 -535
  415. package/dist/engines/pi/chat.d.ts +0 -3
  416. package/dist/engines/pi/chat.js +0 -8
  417. package/dist/engines/pi/config.d.ts +0 -71
  418. package/dist/engines/pi/config.js +0 -199
  419. package/dist/engines/pi/create.d.ts +0 -170
  420. package/dist/engines/pi/create.js +0 -343
  421. package/dist/engines/pi/definition.d.ts +0 -74
  422. package/dist/engines/pi/definition.js +0 -236
  423. package/dist/engines/pi/entry-images.d.ts +0 -14
  424. package/dist/engines/pi/entry-images.js +0 -23
  425. package/dist/engines/pi/invoke-session.d.ts +0 -18
  426. package/dist/engines/pi/invoke-session.js +0 -403
  427. package/dist/engines/pi/locked-file.d.ts +0 -9
  428. package/dist/engines/pi/login.js +0 -331
  429. package/dist/engines/pi/machine.d.ts +0 -53
  430. package/dist/engines/pi/machine.js +0 -137
  431. package/dist/engines/pi/models.d.ts +0 -200
  432. package/dist/engines/pi/models.js +0 -533
  433. package/dist/engines/pi/open.d.ts +0 -180
  434. package/dist/engines/pi/open.js +0 -270
  435. package/dist/engines/pi/openai-account-models.d.ts +0 -35
  436. package/dist/engines/pi/openai-account-models.js +0 -116
  437. package/dist/engines/pi/report.d.ts +0 -18
  438. package/dist/engines/pi/report.js +0 -59
  439. package/dist/engines/pi/retry-event.d.ts +0 -6
  440. package/dist/engines/pi/retry-event.js +0 -15
  441. package/dist/engines/pi/service.d.ts +0 -9
  442. package/dist/engines/pi/service.js +0 -14
  443. package/dist/engines/pi/session-builder.d.ts +0 -13
  444. package/dist/engines/pi/session-builder.js +0 -117
  445. package/dist/engines/pi/session-control.d.ts +0 -77
  446. package/dist/engines/pi/session-control.js +0 -908
  447. package/dist/engines/pi/session-effects.d.ts +0 -14
  448. package/dist/engines/pi/session-effects.js +0 -30
  449. package/dist/engines/pi/session-inheritance.d.ts +0 -21
  450. package/dist/engines/pi/session-inheritance.js +0 -204
  451. package/dist/engines/pi/session-markers.d.ts +0 -49
  452. package/dist/engines/pi/session-markers.js +0 -60
  453. package/dist/engines/pi/session-settings.d.ts +0 -67
  454. package/dist/engines/pi/session-settings.js +0 -119
  455. package/dist/engines/pi/session-store.d.ts +0 -69
  456. package/dist/engines/pi/session-store.js +0 -430
  457. package/dist/engines/pi/tool-context.d.ts +0 -45
  458. package/dist/engines/pi/tool-context.js +0 -31
  459. package/dist/engines/pi/tool.d.ts +0 -99
  460. package/dist/engines/pi/tool.js +0 -118
  461. package/dist/engines/pi/turn-kit.d.ts +0 -92
  462. package/dist/engines/pi/turn-kit.js +0 -288
  463. package/dist/engines/pi/wake-tool.d.ts +0 -9
  464. package/dist/engines/pi/wake-tool.js +0 -92
  465. package/dist/loader.d.ts +0 -41
  466. package/dist/loader.js +0 -116
  467. package/dist/node.d.ts +0 -3
  468. package/dist/node.js +0 -4
  469. package/dist/observe.d.ts +0 -6
  470. package/dist/observe.js +0 -47
  471. package/dist/pi.d.ts +0 -25
  472. package/dist/pi.js +0 -15
  473. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  474. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  475. package/dist/schedule/discover.d.ts +0 -17
  476. package/dist/schedule/discover.js +0 -61
  477. package/dist/schedule/routine.d.ts +0 -43
  478. package/dist/schedule/routine.js +0 -32
  479. package/dist/schedule/run.d.ts +0 -55
  480. package/dist/schedule/run.js +0 -146
  481. package/dist/secrets-gate.d.ts +0 -32
  482. package/dist/secrets-gate.js +0 -33
  483. package/dist/session-remote.d.ts +0 -47
  484. package/dist/session-remote.js +0 -460
  485. package/dist/session.d.ts +0 -466
  486. package/dist/session.js +0 -57
  487. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
@@ -1,256 +1,22 @@
1
- /**
2
- * How a {@link Routes} table becomes a running server: the path rule, dispatch, WHO MAY CALL IT FROM A BROWSER, the
3
- * totality boundary, and the node:http binding.
4
- */
5
- import { serve, getRequestListener } from "@hono/node-server";
1
+ /** The server a Fetch handler is served on, behind the totality boundary every serving path shares. */
2
+ import { createServer } from "node:http";
3
+ import * as Context from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Exit from "effect/Exit";
6
+ import * as Layer from "effect/Layer";
7
+ import * as Scope from "effect/Scope";
8
+ import { HttpEffect, HttpServer, HttpServerError } from "effect/http";
9
+ import { LIMITS } from "../agent-api.js";
6
10
  import { log } from "../log.js";
7
- import { refuseNonJsonBody } from "./body.js";
8
11
  import { text } from "./respond.js";
9
- /** Methods whose request carries a body a handler will parse — what the JSON gate applies to. */
10
- const BODY_METHODS = new Set(["POST", "PUT", "PATCH"]);
11
- /** Parse a route key: `"METHOD /path"` → `{ method, path }`, or `"/path"` → `{ path }` (any method). */
12
- export function parseRouteKey(key) {
13
- const sp = key.indexOf(" ");
14
- return sp === -1 ? { path: key } : { method: key.slice(0, sp).toUpperCase(), path: key.slice(sp + 1) };
15
- }
16
- /** A route key is `"METHOD /path"` or `"/path"`, with a literal path. */
17
- export function assertRouteKey(key, describe) {
18
- const { method, path } = parseRouteKey(key);
19
- if (method === "")
20
- throw new Error(describe('a leading space is not a method — write "/path" for any method'));
21
- if (!path.startsWith("/"))
22
- throw new Error(describe('must start with "/"'));
23
- // Asked of `URL` rather than by listing what it rewrites (`?`/`#`, `.`/`..`, `\`, `%2e`).
24
- const arrives = new URL(path, "http://x").pathname;
25
- if (arrives !== path) {
26
- throw new Error(describe(`is not the path a request would carry — that request arrives as "${arrives}"`));
27
- }
28
- }
29
- /** Do these two keys fight over the same request? */
30
- export function routeKeysConflict(a, b) {
31
- const ka = parseRouteKey(a);
32
- const kb = parseRouteKey(b);
33
- if (ka.path !== kb.path)
34
- return false;
35
- return ka.method === undefined || kb.method === undefined || ka.method === kb.method;
36
- }
37
- /** Same status and headers, no content (RFC 9110's HEAD). */
38
- export function withoutBody(res) {
39
- void res.body?.cancel().catch(() => { });
40
- return new Response(null, { status: res.status, statusText: res.statusText, headers: res.headers });
41
- }
42
- export function pathUnderPrefix(path, prefix) {
43
- return path === prefix || path.startsWith(`${prefix}/`);
44
- }
45
- /**
46
- * THE cross-origin policy, in one place, for the routes that authenticate nobody.
47
- *
48
- * THIS IS AN API, so the default is an API's: `*`, unconditionally. Origin is not access control — what makes any
49
- * API safe to call from a page is a credential the page does not have, which makes the caller's origin irrelevant.
50
- * We have no credential, and the boundary that replaces it — who may reach this port — belongs to the deployment,
51
- * the same rule that removed the bearer token. Deciding for the operator from a signal this process can observe
52
- * (which address it bound) is not that rule: a wildcard bind is what a container needs, not something the operator
53
- * said, and a loopback one is `dev`'s default, not a claim about who may call.
54
- *
55
- * WHAT `*` GRANTS, stated because it is the default and nobody opts into it: any page the user's browser visits can
56
- * call this port cross-origin and read the reply — a turn with the agent's full tool authority, and with
57
- * `sessionControl: true`, reading and deleting sessions. On a published port that is nothing an attacker could not
58
- * already curl. On a loopback `dev` serve it is the whole attack path, and this is the posture the decision costs:
59
- * `announceControl` says so at every boot, and `http.cors` is the one way to take it back.
60
- *
61
- * `allow` is `http.cors`, and when it is set it REPLACES the default rather than adding to it: `["*"]` is the
62
- * default said out loud, a list of origins is the narrowing.
63
- *
64
- * An origin that is not allowed is simply not ANSWERED — no headers, no refusal. That is safe only because a route
65
- * that authenticates nobody also refuses a body that is not `application/json` (channels/body.ts): without that, a
66
- * cross-origin `text/plain` POST is a CORS simple request, sent with no preflight, and withholding the headers
67
- * would only stop the page from reading a turn that had already run.
68
- */
69
- function allowedOrigin(origin, allow) {
70
- if (allow.length === 0)
71
- return "*";
72
- return allow.includes("*") ? "*" : allow.includes(origin) ? origin : undefined;
73
- }
74
- /**
75
- * Refuse a cross-origin allow-list that cannot match anything.
76
- *
77
- * ONE check for BOTH ways in: `http.cors` in a config file, and `corsOrigins` handed to `mountAgentService` by an
78
- * embedder. `allowedOrigin` compares exact strings, so `"https://app.example.com/"` — one trailing slash — never
79
- * matches and the front end is refused with nothing pointing at the rule. A rule that silently matches nothing is
80
- * worse than one that refuses to load.
81
- */
82
- export function assertCorsOrigins(origins, where) {
83
- if (!Array.isArray(origins) || origins.some((o) => typeof o !== "string" || o === "")) {
84
- throw new Error(`${where} must be an array of origin strings (e.g. ["https://app.example.com"])`);
85
- }
86
- // An EMPTY list is the one spelling that means the OPPOSITE of what it looks like. `allowedOrigin` reads "nothing
87
- // configured" as the `*` default and cannot tell that apart from a list someone wrote as empty, so `http.cors: []`
88
- // — the obvious way to write "no page may call this" — would grant the widest policy there is, and silence the
89
- // boot warning that names it (`announceControl` treats a set list as the operator having chosen the origins).
90
- if (origins.length === 0) {
91
- throw new Error(`${where} is empty — list at least one origin, or write ["*"] to say the default out loud`);
92
- }
93
- for (const origin of origins) {
94
- if (origin === "*")
95
- continue;
96
- let parsed;
97
- try {
98
- parsed = new URL(origin);
99
- }
100
- catch {
101
- // `new URL` throwing IS the answer here ("not a URL at all"); rethrown as the rule that cannot load.
102
- throw new Error(`${where} entry ${JSON.stringify(origin)} is not an origin ("https://host[:port]")`);
103
- }
104
- // An ORIGIN, not a URL with a path: `new URL(...).origin` is what a browser sends.
105
- if (parsed.origin !== origin) {
106
- throw new Error(`${where} entry ${JSON.stringify(origin)} is not the origin a browser sends — use ${JSON.stringify(parsed.origin)}`);
107
- }
108
- }
109
- }
110
- /**
111
- * Compose the routes that AUTHENTICATE NOBODY, the self-verifying channel routes, and the {@link PrefixMount}s
112
- * into one handler.
113
- *
114
- * That is the axis, not authorship. Two things follow from it and nothing else does: whether a browser may drive
115
- * the route, and whether its body must declare JSON. A channel route is exempt from both because it checks its
116
- * platform's signature inside itself — a Telegram webhook is public on purpose. Ours check nothing, so the router
117
- * checks for them.
118
- *
119
- * TWO TABLES, not one table plus a list of which keys are which, so the three readers (browser reachability, the JSON
120
- * body gate, the startup report) cannot answer differently.
121
- *
122
- * CAVEAT, stated because we cannot enforce it: a CUSTOM channel that verifies nothing lands in `selfVerifying`
123
- * anyway and gets neither guard. That is decision B (docs/design/session-control.md §14) and it belongs to the
124
- * channel's author, who owns the credential the platform issued — but it is an assumption here, not a property.
125
- */
126
- export function router(surface) {
127
- const { unverified = {}, selfVerifying = {}, mounts = [], corsOrigins = [] } = surface;
128
- for (const [i, mount] of mounts.entries()) {
129
- assertRouteKey(mount.prefix, (problem) => `mount prefix "${mount.prefix}" is invalid — ${problem}`);
130
- if (mount.prefix === "/") {
131
- throw new Error(`mount prefix "/" is invalid — a handler owning every path IS that handler; serve it directly`);
132
- }
133
- if (mount.prefix.endsWith("/")) {
134
- throw new Error(`mount prefix "${mount.prefix}" is invalid — no trailing slash (write "/control")`);
135
- }
136
- const clash = mounts
137
- .slice(0, i)
138
- .find((other) => pathUnderPrefix(mount.prefix, other.prefix) || pathUnderPrefix(other.prefix, mount.prefix));
139
- if (clash) {
140
- throw new Error(`mount "${mount.prefix}" overlaps "${clash.prefix}" — one of them would never receive a request`);
141
- }
142
- }
143
- const byKey = new Map();
144
- const paths = new Set();
145
- const unguarded = new Set();
146
- for (const [key, handler] of [...Object.entries(unverified), ...Object.entries(selfVerifying)]) {
147
- assertRouteKey(key, (problem) => `route "${key}" is not a valid route key — ${problem}`);
148
- const { path } = parseRouteKey(key);
149
- for (const mount of mounts) {
150
- if (pathUnderPrefix(path, mount.prefix)) {
151
- throw new Error(`route "${key}" is inside the mount "${mount.prefix}" — it would never receive a request`);
152
- }
153
- }
154
- const shadowed = [...byKey.keys()].find((other) => routeKeysConflict(other, key));
155
- if (shadowed) {
156
- throw new Error(`route "${key}" conflicts with "${shadowed}" — one of them would never receive a request`);
157
- }
158
- // Stored normalised: `parseRouteKey` upper-cases the method, so `"get /x"` validates under `GET` and would
159
- // otherwise be looked up under a name nothing stores.
160
- const { method } = parseRouteKey(key);
161
- const normalised = method ? `${method} ${path}` : path;
162
- byKey.set(normalised, handler);
163
- paths.add(path);
164
- if (key in unverified)
165
- unguarded.add(normalised);
166
- }
167
- /** Does this route authenticate nobody? Every mount does (the control plane), plus what we registered ourselves. */
168
- const authenticatesNobody = (method, path) => mounts.some((mount) => pathUnderPrefix(path, mount.prefix)) ||
169
- unguarded.has(`${method} ${path}`) ||
170
- unguarded.has(path) ||
171
- // The router answers HEAD from a GET route, so the question follows it.
172
- (method === "HEAD" && unguarded.has(`GET ${path}`));
173
- return (req) => {
174
- // `URL` normalises the path (`/a/../x` → `/x`) and drops query/fragment.
175
- const path = new URL(req.url).pathname;
176
- const origin = req.headers.get("origin");
177
- // A preflight asks ABOUT a method; ownership is that method's, not `OPTIONS`'s (nothing registers OPTIONS).
178
- const asking = req.method === "OPTIONS"
179
- ? (req.headers.get("access-control-request-method")?.toUpperCase() ?? "OPTIONS")
180
- : req.method;
181
- // A BROWSER request against a route that authenticates nobody. A non-browser client sends no `Origin` and is
182
- // untouched by any of this.
183
- const owned = authenticatesNobody(asking, path);
184
- const fromPage = origin !== null && owned;
185
- // Decided BEFORE dispatch, so a 404, a 405 and a handler's own reply all leave with the same verdict — a browser
186
- // that cannot read the 405 gets an opaque network error instead of the reason.
187
- const cors = fromPage ? allowedOrigin(origin, corsOrigins) : undefined;
188
- const answer = () => {
189
- // The gate that makes "an origin we do not allow is simply not answered" safe: a POST carrying `text/plain`
190
- // is a CORS simple request, sent with no preflight at all, so a route that authenticates nobody must refuse
191
- // the body itself. HERE, over the whole unguarded surface, rather than inside each handler — the next route
192
- // we add is covered by existing. A self-verifying channel is exempt: Slack posts urlencoded, and a page
193
- // cannot forge its signature anyway.
194
- if (owned && BODY_METHODS.has(req.method)) {
195
- const wrongType = refuseNonJsonBody(req);
196
- if (wrongType)
197
- return wrongType;
198
- }
199
- // The preflight is the ROUTER's to answer: it names a method the route table does not register, so leaving it
200
- // to the routes below is a 405 with no CORS headers, which is a browser client that cannot call a route that
201
- // works.
202
- if (req.method === "OPTIONS" && cors !== undefined)
203
- return new Response(null, { status: 204 });
204
- for (const mount of mounts)
205
- if (pathUnderPrefix(path, mount.prefix))
206
- return mount.handler(req);
207
- const exact = byKey.get(`${req.method} ${path}`) ?? byKey.get(path);
208
- if (exact)
209
- return exact(req);
210
- if (req.method === "HEAD") {
211
- const get = byKey.get(`GET ${path}`);
212
- if (get)
213
- return get(req);
214
- }
215
- return paths.has(path) ? text("method not allowed\n", 405) : text("not found\n", 404);
216
- };
217
- /**
218
- * ONE exit, so the HEAD rule and the CORS verdict hold for every reply — a mount's, a route's, the GET
219
- * fallback's, and the 404/405 this router writes itself.
220
- */
221
- const finish = (res) => {
222
- const out = req.method === "HEAD" ? withoutBody(res) : res;
223
- // `vary` whether or not the origin was allowed: the answer DEPENDS on the request's origin either way, and a
224
- // cache that does not know it would serve one caller's verdict to another.
225
- if (owned)
226
- out.headers.append("vary", "origin");
227
- if (cors === undefined)
228
- return out;
229
- out.headers.set("access-control-allow-origin", cors);
230
- // Echoed for the same reason the methods are: what a caller needs is not ours to enumerate. A gateway in front
231
- // of this port demands its own header (`connectSessionControl({ fetchFn })` is how a client sends one), and a
232
- // fixed list turns that preflight into a 204 the browser then refuses to act on. The default names the two a
233
- // client of ours always needs, for a preflight that asked about nothing.
234
- out.headers.set("access-control-allow-headers", req.headers.get("access-control-request-headers") ?? "authorization, content-type");
235
- // The method the preflight ASKED about, not a table lookup: a mount owns a prefix and does not publish which
236
- // methods each path under it serves. Naming one the path does not serve costs nothing now that the 405 it
237
- // produces carries these same headers.
238
- const asked = req.headers.get("access-control-request-method")?.toUpperCase();
239
- out.headers.set("access-control-allow-methods", [...new Set([asked, "OPTIONS"].filter(Boolean))].join(", "));
240
- return out;
241
- };
242
- const res = answer();
243
- return res instanceof Promise ? res.then(finish) : finish(res);
244
- };
245
- }
246
- /** The totality boundary every serving path shares. */
247
- function totalFetch(handler) {
12
+ /** The totality boundary every serving path shares; `prepare` shapes the handler's response inside it. */
13
+ export function totalFetch(handler, prepare = (response) => response) {
248
14
  return async (req) => {
249
15
  try {
250
16
  const response = await handler(req);
251
17
  if (!(response instanceof Response))
252
18
  throw new TypeError("handler did not return a Response");
253
- return response;
19
+ return prepare(response);
254
20
  }
255
21
  catch (error) {
256
22
  log.error(`[serve] request failed: ${String(error)}`);
@@ -258,50 +24,102 @@ function totalFetch(handler) {
258
24
  }
259
25
  };
260
26
  }
261
- /** Has upstream middleware already drained the request body? */
262
- function bodyAlreadyRead(req) {
263
- const length = Number(req.headers["content-length"]);
264
- return Number.isFinite(length) && length > 0 && req.readableEnded;
265
- }
266
27
  /**
267
- * The node:http adapter for a Fetch handler — the embedded server uses it, and an embedder mounting fastagent on its
268
- * OWN node:http server can too.
28
+ * A response whose body fails mid-flight, said in the log. Once the headers are out the failure can only cut the body
29
+ * short, and the server this binds to reports nothing of it; the client sees a truncated body and nothing more. A
30
+ * client that leaves is not a failure: the server cancels the body, which ends the read in progress and calls `left`.
269
31
  */
270
- export function nodeListener(handler) {
271
- const listener = getRequestListener(totalFetch(handler), { overrideGlobalObjects: false });
272
- return (req, res) => {
273
- if (bodyAlreadyRead(req)) {
274
- log.error(`[serve] ${req.method} ${req.url}: the request body was already read by upstream middleware ` +
275
- `(e.g. express.json()) — mount fastagent BEFORE the body parser, or scope the parser away ` +
276
- `from this route. Channels that verify webhook signatures need the RAW body.`);
277
- res.writeHead(500, { "content-type": "text/plain" });
278
- res.end("internal error\n");
279
- return;
280
- }
281
- listener(req, res);
282
- };
283
- }
284
- export function serveNode(handler, options) {
285
- let onListening;
286
- let onBindError;
287
- const listening = new Promise((resolve, reject) => {
288
- onListening = resolve;
289
- onBindError = reject;
32
+ function watchedBody(response, left) {
33
+ if (response.body === null)
34
+ return response;
35
+ const reader = response.body.getReader();
36
+ const body = new ReadableStream({
37
+ async pull(controller) {
38
+ let next;
39
+ try {
40
+ next = await reader.read();
41
+ }
42
+ catch (error) {
43
+ log.error(`[serve] a response body failed mid-flight: ${String(error)}`);
44
+ controller.error(error);
45
+ return;
46
+ }
47
+ // Outside the try: when the client left, the read in progress ends `done` on a body already cancelled, and
48
+ // closing it throws; that is not the body failing.
49
+ if (next.done)
50
+ controller.close();
51
+ else
52
+ controller.enqueue(next.value);
53
+ },
54
+ cancel(reason) {
55
+ left();
56
+ return reader.cancel(reason);
57
+ },
290
58
  });
291
- // serve() types its return as the union of every server it CAN build (incl. http2).
292
- const server = serve({
293
- fetch: totalFetch(handler),
294
- overrideGlobalObjects: false,
59
+ return new Response(body, { status: response.status, statusText: response.statusText, headers: response.headers });
60
+ }
61
+ const isBun = typeof process.versions.bun === "string";
62
+ /**
63
+ * How long `close()` gives the requests in flight before it ends them, on either runtime: a webhook is answered in
64
+ * milliseconds, and a close must fit the CLI's shutdown deadline (`FORCED_EXIT_MS` in `src/cli/serve.ts`).
65
+ */
66
+ const GRACEFUL_SHUTDOWN = "500 millis";
67
+ /**
68
+ * How long Bun lets a response say nothing before it ends it (10 s by default): longer than an Agent API request waits
69
+ * for a message, and than an event stream's keep-alive interval. Node sets no such limit.
70
+ */
71
+ const BUN_IDLE_TIMEOUT_SECONDS = LIMITS.httpWaitSeconds + 15;
72
+ /** The server this runtime serves on: Node's `http.Server`, or `Bun.serve`. */
73
+ async function serverLayer(options) {
74
+ if (isBun) {
75
+ const { layer } = await import("@effect/platform-bun/BunHttpServer");
76
+ return layer({
77
+ port: options.port,
78
+ ...(options.host === undefined ? {} : { hostname: options.host }),
79
+ gracefulShutdownTimeout: GRACEFUL_SHUTDOWN,
80
+ idleTimeout: BUN_IDLE_TIMEOUT_SECONDS,
81
+ });
82
+ }
83
+ const { layer } = await import("@effect/platform-node/NodeHttpServer");
84
+ return layer(() => createServer(), {
295
85
  port: options.port,
296
- ...(options.host !== undefined ? { hostname: options.host } : {}),
297
- }, (info) => {
298
- // Detach before resolving: this listener answers the BIND, and leaving it attached would let a later runtime
299
- // error call reject() on a settled promise.
300
- server.off("error", onBindError);
301
- onListening(info.port);
86
+ ...(options.host === undefined ? {} : { host: options.host }),
87
+ gracefulShutdownTimeout: GRACEFUL_SHUTDOWN,
88
+ });
89
+ }
90
+ /** A bind that failed, as the error its runtime raised (`EADDRINUSE` and the like), not Effect's wrapper of it. */
91
+ function bindFailure(error) {
92
+ return error instanceof HttpServerError.ServeError ? error.cause : error;
93
+ }
94
+ /** Serve `handler` on this runtime's own server: Node's `http.Server`, or `Bun.serve`. */
95
+ export function serveHandler(handler, options) {
96
+ const scope = Scope.makeUnsafe();
97
+ let closed = false;
98
+ // A server runs each request uninterruptibly unless told otherwise; a request that has not answered when the server
99
+ // closes must be interruptible, or closing waits on it forever.
100
+ const app = Effect.interruptible(HttpEffect.fromWebHandler((request) => {
101
+ // The handler's `signal` follows its client to the end of the response, as a Node server's request does: this
102
+ // server aborts it only while the handler runs, so a body cancelled because the client left aborts it too.
103
+ const left = new AbortController();
104
+ const followed = new Request(request, { signal: AbortSignal.any([request.signal, left.signal]) });
105
+ return totalFetch(handler, (response) => watchedBody(response, () => left.abort()))(followed);
106
+ }));
107
+ const listening = serverLayer(options)
108
+ .then((server) => {
109
+ if (closed)
110
+ throw new Error("the server was closed before it listened");
111
+ return Effect.runPromise(Layer.buildWithScope(HttpServer.serve(app).pipe(Layer.provideMerge(server)), scope));
112
+ })
113
+ .then((context) => Context.get(context, HttpServer.HttpServer).address.port, (error) => {
114
+ throw bindFailure(error);
302
115
  });
303
- server.once("error", onBindError); // a bind failure surfaces here, before "listening"
304
- const close = () => new Promise((resolve, reject) => server.close((e) => (e ? reject(e) : resolve())));
305
- const closeAllConnections = () => server.closeAllConnections();
306
- return { listening, close, closeAllConnections };
116
+ return {
117
+ listening,
118
+ async close() {
119
+ closed = true;
120
+ // A bind in progress finishes first, so what it bound is released here; how it ended is `listening`'s to say.
121
+ await listening.then(() => undefined, () => undefined);
122
+ await Effect.runPromise(Scope.close(scope, Exit.void));
123
+ },
124
+ };
307
125
  }
@@ -0,0 +1,42 @@
1
+ import { type PlaceDiscussion, type PlaceHistory, type PlaceMessage, type PlaceRead } from "../kit/place-history.ts";
2
+ import { type SlackApi } from "./slack-api.ts";
3
+ export type SlackPlaceRead = PlaceRead<string>;
4
+ export type SlackDiscussion = PlaceDiscussion<string>;
5
+ export interface SlackPlaceHistory extends PlaceHistory<string> {
6
+ /** The channel's threads among its newest messages, for a tool (`kit/room-threads.ts`); `room` is its place key. */
7
+ threads(room: string): Promise<string>;
8
+ }
9
+ /** A background file carried into a turn, with attribution for its prompt manifest. */
10
+ export interface SlackBufferedFileRef {
11
+ id: string;
12
+ from: string;
13
+ messageId: string;
14
+ }
15
+ /** A place: a channel's top level, or a thread in it. The turn record carries it as this key. */
16
+ export declare function slackHistoryKey(teamId: string, place: {
17
+ channelId: string;
18
+ threadTs?: string;
19
+ }): string;
20
+ /** The inverse of {@link slackHistoryKey}. Slack ids and timestamps never contain a colon. */
21
+ export declare function slackPlaceOf(key: string): {
22
+ teamId: string;
23
+ channelId: string;
24
+ threadTs?: string;
25
+ };
26
+ /** Select the most recent background files of the folded messages, excluding the turn's own. */
27
+ export declare function collectFoldedFiles(folded: readonly PlaceMessage[], primaryIds: ReadonlySet<string>): {
28
+ files: SlackBufferedFileRef[];
29
+ skipped: number;
30
+ };
31
+ export declare function createSlackPlaceHistory(deps: {
32
+ api: Pick<SlackApi, "channelHistory" | "threadReplies" | "userName">;
33
+ /** THIS app's bot user and bot ids, once `auth.test` answered: a message from either is the agent's own. */
34
+ self(): {
35
+ userId?: string;
36
+ botId?: string;
37
+ };
38
+ label: string;
39
+ /** Where each place's cursor and outputs persist. */
40
+ path: string;
41
+ isTurnInput(key: string, ts: string): boolean;
42
+ }): SlackPlaceHistory;