@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,7 +1,18 @@
1
+ /**
2
+ * The ONE Slack transport per bot a process holds: the mounted channel's, shared with the scaffolded send tool, found
3
+ * by the bot token both hold.
4
+ */
5
+ import { roomThreads } from "../kit/room-threads.ts";
1
6
  import { type SlackApi } from "./slack-api.ts";
2
7
  /** What a proactive sender needs: Markdown delivery and file upload. */
3
8
  export type SlackTransport = Pick<SlackApi, "sendMarkdown" | "uploadFile">;
4
- /** A re-mount replaces the entry: the channel's transport is the authoritative one for its root. */
5
- export declare function registerSlackApi(stateRoot: string, api: SlackApi): void;
6
- /** The transport of the agent whose workspace is `cwd` (a tool's `ctx.cwd`). */
7
- export declare function slackTransport(cwd: string): SlackTransport;
9
+ /** A re-mount replaces the entry: the channel's transport is the authoritative one for its bot. */
10
+ export declare function registerSlackApi(botToken: string, api: SlackApi): void;
11
+ /** The transport of the bot `botToken` signs in as: its mounted channel's, or the token's own when none is mounted. */
12
+ export declare function slackTransport(botToken: string): SlackTransport;
13
+ /**
14
+ * The threads of the channel the message running in `sessionId` was asked in, and only those (`kit/room-threads.ts`):
15
+ * `list()` its recent threads, `read(threadTs)` one of them. Throws outside a Slack channel turn served by this
16
+ * process.
17
+ */
18
+ export declare function slackThreads(sessionId: string | undefined): ReturnType<typeof roomThreads>;
@@ -1,23 +1,28 @@
1
1
  /**
2
- * The ONE Slack transport per state root a process holds: the mounted channel's, shared with the scaffolded send tool.
2
+ * The ONE Slack transport per bot a process holds: the mounted channel's, shared with the scaffolded send tool, found
3
+ * by the bot token both hold.
3
4
  */
4
- import { resolvePlacement, resolveStateRoot } from "../../paths.js";
5
+ import { roomThreads } from "../kit/room-threads.js";
5
6
  import { createSlackApi } from "./slack-api.js";
6
- const byStateRoot = new Map();
7
- /** A re-mount replaces the entry: the channel's transport is the authoritative one for its root. */
8
- export function registerSlackApi(stateRoot, api) {
9
- byStateRoot.set(stateRoot, api);
7
+ const byToken = new Map();
8
+ /** A re-mount replaces the entry: the channel's transport is the authoritative one for its bot. */
9
+ export function registerSlackApi(botToken, api) {
10
+ byToken.set(botToken, api);
10
11
  }
11
- /** The transport of the agent whose workspace is `cwd` (a tool's `ctx.cwd`). */
12
- export function slackTransport(cwd) {
13
- const stateRoot = resolveStateRoot(resolvePlacement(cwd).agentDir);
14
- let api = byStateRoot.get(stateRoot);
12
+ /** The transport of the bot `botToken` signs in as: its mounted channel's, or the token's own when none is mounted. */
13
+ export function slackTransport(botToken) {
14
+ let api = byToken.get(botToken);
15
15
  if (!api) {
16
- const botToken = process.env.SLACK_BOT_TOKEN;
17
- if (!botToken)
18
- throw new Error("SLACK_BOT_TOKEN is not set and no Slack channel is mounted");
19
16
  api = createSlackApi({ botToken });
20
- byStateRoot.set(stateRoot, api);
17
+ byToken.set(botToken, api);
21
18
  }
22
19
  return api;
23
20
  }
21
+ /**
22
+ * The threads of the channel the message running in `sessionId` was asked in, and only those (`kit/room-threads.ts`):
23
+ * `list()` its recent threads, `read(threadTs)` one of them. Throws outside a Slack channel turn served by this
24
+ * process.
25
+ */
26
+ export function slackThreads(sessionId) {
27
+ return roomThreads("slack", sessionId);
28
+ }
@@ -1,4 +1,4 @@
1
- import type { ImageRef } from "../../agent.ts";
1
+ import type { ImageAttachment as ImageRef } from "../kit/invoke-turn-kit.ts";
2
2
  import type { CallOptions } from "../kit/transport.ts";
3
3
  import type { SlackFile } from "./model.ts";
4
4
  export interface SlackTarget {
@@ -38,7 +38,65 @@ export interface SlackApiOptions {
38
38
  botToken: string;
39
39
  baseUrl?: string;
40
40
  }
41
+ /** A message as `conversations.history` / `conversations.replies` list it: the fields a place's history reads. */
42
+ export interface SlackListedMessage {
43
+ type?: string;
44
+ subtype?: string;
45
+ hidden?: boolean;
46
+ ts?: string;
47
+ thread_ts?: string;
48
+ /** On a thread's root, in a channel listing. */
49
+ reply_count?: number;
50
+ latest_reply?: string;
51
+ user?: string;
52
+ bot_id?: string;
53
+ username?: string;
54
+ bot_profile?: {
55
+ name?: string;
56
+ };
57
+ text?: string;
58
+ blocks?: unknown[];
59
+ /** Legacy attachments: where many integrations (CI, alerts) put all they say, with an empty `text`. */
60
+ attachments?: {
61
+ fallback?: string;
62
+ pretext?: string;
63
+ title?: string;
64
+ text?: string;
65
+ }[];
66
+ files?: SlackFile[];
67
+ }
68
+ /**
69
+ * The newest `limit` messages before `latest` (exclusive). There is deliberately no `oldest`: with it, both reads
70
+ * return the OLDEST `limit` after it instead, and `conversations.replies` then pages on to nothing (measured), so a
71
+ * reader cuts at its own cursor.
72
+ */
73
+ interface SlackRange {
74
+ latest?: string;
75
+ limit: number;
76
+ }
77
+ /** Where a posted message landed: a channel's top level, or a thread in it. */
78
+ type OnSent = (where: {
79
+ channelId: string;
80
+ threadTs?: string;
81
+ }, ts: string) => void;
41
82
  export interface SlackApi {
83
+ /**
84
+ * The same transport, reporting every message it posts (`chat.postMessage`, every chunk of a long body, and
85
+ * `chat.startStream`) with where it landed. Edits, deletions and file uploads are not reported.
86
+ */
87
+ recordingSends(onSent: OnSent): SlackApi;
88
+ /** A channel's top-level messages in `range`, NEWEST first (`conversations.history`). */
89
+ channelHistory(channelId: string, range: SlackRange, opts?: CallOptions): Promise<{
90
+ messages: SlackListedMessage[];
91
+ hasMore: boolean;
92
+ }>;
93
+ /** A thread's replies in `range`, OLDEST first, after its root, which Slack lists whatever the range. */
94
+ threadReplies(channelId: string, threadTs: string, range: SlackRange, opts?: CallOptions): Promise<{
95
+ messages: SlackListedMessage[];
96
+ hasMore: boolean;
97
+ }>;
98
+ /** A person's name as the workspace shows it (`users.info`, needs `users:read`); undefined when it has none. */
99
+ userName(userId: string, opts?: CallOptions): Promise<string | undefined>;
42
100
  authTest(): Promise<{
43
101
  teamId?: string;
44
102
  userId?: string;
@@ -73,4 +131,5 @@ export interface SlackApi {
73
131
  export declare function chunkSlackText(text: string, maxPoints?: number): string[];
74
132
  /** Split standard Markdown while balancing fenced code blocks across separately posted messages. */
75
133
  export declare function chunkSlackMarkdown(markdown: string, maxPoints?: number): string[];
76
- export declare function createSlackApi({ botToken, baseUrl }: SlackApiOptions): SlackApi;
134
+ export declare function createSlackApi(options: SlackApiOptions): SlackApi;
135
+ export {};
@@ -143,8 +143,12 @@ function assertLongLivedBotToken(botToken) {
143
143
  'create a new app (docs/slack.md → "If token rotation is on")');
144
144
  }
145
145
  }
146
- export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" }) {
147
- assertLongLivedBotToken(botToken);
146
+ export function createSlackApi(options) {
147
+ assertLongLivedBotToken(options.botToken);
148
+ return slackClient(options, undefined);
149
+ }
150
+ /** A transport holds no state between calls, so a recording one is simply another instance. */
151
+ function slackClient({ botToken, baseUrl = "https://slack.com/api" }, onSent) {
148
152
  const apiBase = baseUrl.replace(/\/$/, "");
149
153
  const call = async (method, body, httpMethod = "POST", opts = {}) => {
150
154
  const retries = opts.retries ?? RETRIES;
@@ -220,6 +224,10 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
220
224
  }
221
225
  return { bytes: await readBytesCapped(response), contentType: response.headers.get("content-type") ?? undefined };
222
226
  };
227
+ const sent = (channelId, threadTs, ts) => {
228
+ onSent?.({ channelId, ...(threadTs ? { threadTs } : {}) }, ts);
229
+ return ts;
230
+ };
223
231
  const postMarkdown = async (target, markdown) => {
224
232
  const data = await call("chat.postMessage", {
225
233
  channel: target.channelId,
@@ -231,9 +239,24 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
231
239
  if (!data.ts)
232
240
  throw new SlackApiError("chat.postMessage", 200, "response carried no ts");
233
241
  // The response's `channel` is what resolves a user-id target to its DM; a channel target echoes itself.
234
- return { ts: data.ts, channelId: data.channel ?? target.channelId };
242
+ const channelId = data.channel ?? target.channelId;
243
+ return { ts: sent(channelId, target.threadTs, data.ts), channelId };
235
244
  };
236
245
  const api = {
246
+ recordingSends: (record) => slackClient({ botToken, baseUrl }, record),
247
+ async channelHistory(channelId, range, opts) {
248
+ const data = await call("conversations.history", { channel: channelId, ...range }, "GET", opts);
249
+ return { messages: data.messages ?? [], hasMore: data.has_more === true };
250
+ },
251
+ async threadReplies(channelId, threadTs, range, opts) {
252
+ const data = await call("conversations.replies", { channel: channelId, ts: threadTs, ...range }, "GET", opts);
253
+ return { messages: data.messages ?? [], hasMore: data.has_more === true };
254
+ },
255
+ async userName(userId, opts) {
256
+ const data = await call("users.info", { user: userId }, "GET", opts);
257
+ const user = data.user;
258
+ return user?.profile?.display_name || user?.profile?.real_name || user?.real_name || user?.name || undefined;
259
+ },
237
260
  async authTest() {
238
261
  const data = await call("auth.test", {});
239
262
  return { teamId: data.team_id, userId: data.user_id, botId: data.bot_id };
@@ -248,7 +271,7 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
248
271
  });
249
272
  if (!data.ts)
250
273
  throw new SlackApiError("chat.postMessage", 200, "response carried no ts");
251
- return data.ts;
274
+ return sent(data.channel ?? target.channelId, target.threadTs, data.ts);
252
275
  },
253
276
  async postMarkdown(target, markdown) {
254
277
  return (await postMarkdown(target, markdown)).ts;
@@ -305,7 +328,7 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
305
328
  });
306
329
  if (!data.ts)
307
330
  throw new SlackApiError("chat.startStream", 200, "response carried no ts");
308
- return data.ts;
331
+ return sent(target.channelId, target.threadTs, data.ts);
309
332
  },
310
333
  async appendStream(channelId, ts, markdown) {
311
334
  await call("chat.appendStream", { channel: channelId, ts, markdown_text: markdown });
@@ -1,6 +1,6 @@
1
1
  /** First-party Slack HTTP Events API channel: signed ingress, durable turns/context, files, and edited previews. */
2
2
  import { createHmac } from "node:crypto";
3
- import { isAbsolute, join } from "node:path";
3
+ import { join } from "node:path";
4
4
  import { log } from "../../log.js";
5
5
  import { readBodyCapped } from "../body.js";
6
6
  import { text } from "../respond.js";
@@ -13,15 +13,15 @@ import * as Exit from "effect/Exit";
13
13
  import { portJoin } from "../../effect-port.js";
14
14
  import { createTaskTracker } from "../kit/tasks.js";
15
15
  import { attachmentsDir } from "../kit/attachment-path.js";
16
- import { ensureStateHome } from "../kit/state.js";
17
16
  import { dispatchStop, isStopText } from "../kit/stop-command.js";
18
17
  import { codePointPrefix } from "../kit/text.js";
18
+ import { createRoomThreads, registerRoomThreads } from "../kit/room-threads.js";
19
19
  import { createTurnRunner } from "../kit/turn-runner.js";
20
20
  import { createTurnStore } from "../kit/turn-store.js";
21
21
  import { discussionBlock } from "../kit/context-buffer.js";
22
- import { collectSlackBufferedFiles, createSlackContextBuffer } from "./context-buffer.js";
22
+ import { collectFoldedFiles, createSlackPlaceHistory, slackHistoryKey, slackPlaceOf, } from "./history.js";
23
23
  import { slackTurnStream } from "./invoke-turn.js";
24
- import { defaultSlackRoute, isSlackDirectMessage, isSlackGroupMessage, hasSlackMention, hasSlackUserMention, isSlackHumanMessage, mentionsSlackUser, stripSlackMentions, slackBufferText, slackEnvelope, slackFileIds, slackMessageText, slackPlaceKey, slackSenderLabel, slackTeamId, } from "./parse.js";
24
+ import { defaultSlackRoute, isSlackDirectMessage, isSlackGroupMessage, hasSlackMention, isSlackHumanMessage, mentionsSlackUser, stripSlackMentions, slackEnvelope, slackFileIds, slackMessageText, slackTeamId, } from "./parse.js";
25
25
  import { defaultErrorMessage, deliverSlackAnswer, settleSlackPreview, slackReply, } from "./preview.js";
26
26
  import { completeSlackReaction, resolveReactionEmojis, startSlackReaction } from "./reaction.js";
27
27
  import { registerSlackApi } from "./shared-api.js";
@@ -40,9 +40,10 @@ function isStoredSlackTurn(value) {
40
40
  const turn = value;
41
41
  return (typeof turn?.id === "string" &&
42
42
  typeof turn.seq === "number" &&
43
- typeof turn.session === "string" &&
43
+ typeof turn.conversation === "string" &&
44
44
  typeof turn.baseText === "string" &&
45
- typeof turn.bufferKey === "string" &&
45
+ (turn.historyKey === undefined || typeof turn.historyKey === "string") &&
46
+ typeof turn.group === "boolean" &&
46
47
  typeof turn.teamId === "string" &&
47
48
  typeof turn.channelId === "string" &&
48
49
  (turn.threadTs === undefined || typeof turn.threadTs === "string") &&
@@ -74,10 +75,12 @@ const AUTH_ACK_BUDGET_MS = 2_000;
74
75
  function createSlackAuthentication(api, label) {
75
76
  let teamId;
76
77
  let botUserId;
78
+ let botId;
77
79
  let state = "pending";
78
80
  const settled = api.authTest().then((identity) => {
79
81
  teamId = identity.teamId;
80
82
  botUserId = identity.userId;
83
+ botId = identity.botId;
81
84
  state = "ready";
82
85
  log.info(`${label} authenticated${identity.teamId ? ` for workspace ${identity.teamId}` : ""}`);
83
86
  }, (error) => {
@@ -91,6 +94,7 @@ function createSlackAuthentication(api, label) {
91
94
  settled,
92
95
  teamId: () => teamId,
93
96
  botUserId: () => botUserId,
97
+ botId: () => botId,
94
98
  ready: () => state !== "pending"
95
99
  ? settled
96
100
  : new Promise((resolve, reject) => {
@@ -114,37 +118,74 @@ export function slackChannel(options) {
114
118
  throw new Error("slackChannel welcome must be a string or false");
115
119
  }
116
120
  const reactionEmojis = resolveReactionEmojis(reactionAck);
117
- return ({ agent, stateRoot, control }) => {
121
+ return ({ agent, sessionFor, recordedSession, stateDir: stateHome }) => {
118
122
  if (!botToken)
119
123
  throw new Error("slackChannel requires a non-empty botToken (Bot User OAuth Token)");
120
124
  if (!signingSecret)
121
125
  throw new Error("slackChannel requires a non-empty signingSecret (Basic Information → App Credentials)");
122
- if (!isAbsolute(stateRoot))
123
- throw new Error(`slackChannel requires an absolute ctx.stateRoot, got "${stateRoot}"`);
124
126
  const label = "[slack]";
125
127
  const formatError = onError ?? defaultErrorMessage;
126
- const stateHome = join(stateRoot, "channels", "slack");
127
- ensureStateHome(stateHome);
128
128
  const api = createSlackApi({ botToken, baseUrl: apiBaseUrl });
129
- registerSlackApi(stateRoot, api); // the send tool delivers through this one (shared-api.ts)
129
+ registerSlackApi(botToken, api); // the send tool delivers through this one (shared-api.ts)
130
130
  const auth = createSlackAuthentication(api, label);
131
131
  // Side tasks (stop feedback, DM welcomes) run off the ACK path but drain in turnsIdle.
132
132
  const sideTasks = createTaskTracker(label);
133
133
  const seen = createSeenRing(join(stateHome, "seen.json"), label);
134
134
  const threadParticipants = createThreadParticipants(join(stateHome, "thread-participants.json"), label);
135
135
  /**
136
- * A thread's participation is keyed by the SESSION it describes — "the agent answered here" is a claim about a
137
- * memory, so the two must not be re-keyable independently.
136
+ * A thread's participation is keyed by the CONVERSATION it describes — "the agent answered here" is a claim about
137
+ * a memory, so the two must not be re-keyable independently.
138
138
  */
139
- const threadKey = (teamId, channelId, threadTs) => `slack:${teamId}:${channelId}:${threadTs}`;
139
+ const threadKey = (teamId, channelId, threadTs) => `${teamId}:${channelId}:${threadTs}`;
140
140
  const welcomed = createWelcomedUsers(join(stateHome, "welcomed.json"), label);
141
- const buffer = createSlackContextBuffer(join(stateHome, "buffers.json"), label);
141
+ // Every message this channel took as input (a turn, a /stop) is in `seen`. A history read also leaves these out,
142
+ // to shape the prompt only — what keeps a queued ask out of an earlier turn is the read's bound, not this ring.
143
+ const history = createSlackPlaceHistory({
144
+ api,
145
+ self: () => ({ userId: auth.botUserId(), botId: auth.botId() }),
146
+ label,
147
+ path: join(stateHome, "history.json"),
148
+ isTurnInput: (key, ts) => {
149
+ const place = slackPlaceOf(key);
150
+ return seen.has(`${place.teamId}:${place.channelId}:${ts}`);
151
+ },
152
+ });
153
+ // A group turn's room, for the thread-reading tool (`slackThreads`): the channel it was asked in, and its threads.
154
+ const rooms = createRoomThreads({
155
+ list: (room) => history.threads(room),
156
+ read: async (room, threadTs) => {
157
+ if (!/^\d+\.\d+$/.test(threadTs)) {
158
+ throw new Error(`"${threadTs}" is not a Slack thread id: pass a thread's id from the list (like 1712345678.123456)`);
159
+ }
160
+ const { teamId, channelId } = slackPlaceOf(room);
161
+ const key = slackHistoryKey(teamId, { channelId, threadTs });
162
+ return (await history.snapshot(key)).text || `(thread ${threadTs} shows no messages)`;
163
+ },
164
+ });
165
+ registerRoomThreads("slack", stateHome, rooms);
166
+ /**
167
+ * The client a turn posts through: what it posts (answer, queue notice, stop feedback) is in the session already,
168
+ * so the place it LANDS in records it, and that place's next read leaves it out — an answer to a channel's
169
+ * top-level ask lands in the thread it opens. The send tool uses the plain client, so what the agent posts itself
170
+ * stays discussion. A routed turn has no place and reads no history.
171
+ */
172
+ const placeApi = (turn) => turn.historyKey === undefined
173
+ ? api
174
+ : api.recordingSends((where, ts) => history.recordOutput(slackHistoryKey(turn.teamId, where), ts));
142
175
  const store = createTurnStore(join(stateHome, "turns.json"), {
143
176
  label,
144
177
  isRecord: isStoredSlackTurn,
145
178
  order: (a, b) => a.seq - b.seq,
146
179
  });
147
180
  const decide = route ?? defaultSlackRoute;
181
+ /** A channel turn under the default routing reads its channel's threads; a DM has none to read. */
182
+ const enterRoom = (turn) => {
183
+ if (!turn.group || turn.historyKey === undefined)
184
+ return {};
185
+ const { teamId, channelId } = slackPlaceOf(turn.historyKey);
186
+ const room = slackHistoryKey(teamId, { channelId });
187
+ return { enter: (sessionId) => rooms.enter(sessionId, room) };
188
+ };
148
189
  const targetOf = (turn) => ({
149
190
  channelId: turn.channelId,
150
191
  threadTs: turn.threadTs,
@@ -155,16 +196,20 @@ export function slackChannel(options) {
155
196
  const target = targetOf(turn);
156
197
  if (turn.nativeQueueStatus)
157
198
  void api.setThreadStatus(target, "").catch(() => { });
158
- void settleSlackPreview(api, target, turn.previewTs, "⚠️ I couldn’t complete an earlier request — please ask again.").catch((error) => log.warn(`${label} could not notify a dropped turn: ${String(error)}`));
199
+ void settleSlackPreview(placeApi(turn), target, turn.previewTs, "⚠️ I couldn’t complete an earlier request — please ask again.").catch((error) => log.warn(`${label} could not notify a dropped turn: ${String(error)}`));
159
200
  };
160
201
  const runner = createTurnRunner({
161
202
  label,
162
203
  store,
163
- buffer,
204
+ discussion: history,
164
205
  seen,
165
206
  toStored: ({ previewTs: _preview, nativeQueueStatus: _status, ...intent }) => ({ ...intent, attempts: 0 }),
166
207
  fromStored: ({ attempts: _attempts, ...intent }) => ({ ...intent }),
167
- bufferKey: (turn) => turn.bufferKey,
208
+ // The read ends at the ask (`messageRefOf`: the ts of the message that asked).
209
+ discussionKey: (turn) => {
210
+ const ask = messageRefOf(turn.id);
211
+ return turn.historyKey === undefined || ask === undefined ? undefined : { key: turn.historyKey, until: ask.ts };
212
+ },
168
213
  where: (turn) => `channel=${turn.channelId}`,
169
214
  onQueuedBehind(turn) {
170
215
  const nativeDmStatus = rendering === "native" && turn.threadTs && turn.channelId.startsWith("D");
@@ -174,7 +219,9 @@ export function slackChannel(options) {
174
219
  done: auth.settled
175
220
  .then(() => nativeDmStatus
176
221
  ? api.setThreadStatus(targetOf(turn), "is queued behind an earlier request…")
177
- : api.postMessage(targetOf(turn), QUEUED_PLACEHOLDER).then((ts) => {
222
+ : placeApi(turn)
223
+ .postMessage(targetOf(turn), QUEUED_PLACEHOLDER)
224
+ .then((ts) => {
178
225
  turn.previewTs = ts;
179
226
  }))
180
227
  .catch((error) => log.warn(`${label} queue preview failed (the turn stays durable): ${String(error)}`)),
@@ -198,7 +245,7 @@ export function slackChannel(options) {
198
245
  .catch((error) => log.warn(`${label} could not update a deferred Agent status: ${String(error)}`));
199
246
  }
200
247
  else if (turn.previewTs) {
201
- void settleSlackPreview(api, targetOf(turn), turn.previewTs, DEFERRED_PLACEHOLDER).catch((error) => log.warn(`${label} could not update a deferred queue preview: ${String(error)}`));
248
+ void settleSlackPreview(placeApi(turn), targetOf(turn), turn.previewTs, DEFERRED_PLACEHOLDER).catch((error) => log.warn(`${label} could not update a deferred queue preview: ${String(error)}`));
202
249
  }
203
250
  },
204
251
  notifyDropped,
@@ -210,7 +257,7 @@ export function slackChannel(options) {
210
257
  .setThreadStatus(targetOf(turn), "")
211
258
  .catch((error) => log.warn(`${label} could not clear a queued Agent status: ${String(error)}`));
212
259
  }
213
- await deliverSlackAnswer(api, targetOf(turn), answer, turn.previewTs);
260
+ await deliverSlackAnswer(placeApi(turn), targetOf(turn), answer, turn.previewTs);
214
261
  // The 👀 on the asker's message was added by the run that produced this answer, and `execute`'s
215
262
  // acquire/release — the only thing that turns it into ✅ — is not on this path.
216
263
  const messageRef = messageRefOf(turn.id);
@@ -234,10 +281,15 @@ export function slackChannel(options) {
234
281
  emojis: reactionEmojis,
235
282
  label,
236
283
  })
237
- : undefined), () => Effect.scoped(slackReply(slackTurnStream(agent, turn.session, `${discussionBlock(discussion.text)}${turn.baseText}`, { api, channelId: turn.channelId, filesDir: attachmentsDir(stateHome), label }, {
284
+ : undefined), () => Effect.scoped(slackReply(slackTurnStream(agent, {
285
+ session: () => sessionFor(turn.conversation),
286
+ ...enterRoom(turn),
287
+ key: turn.id,
288
+ text: `${discussionBlock(discussion.text)}${turn.baseText}`,
289
+ }, { api, channelId: turn.channelId, filesDir: attachmentsDir(stateHome), label }, {
238
290
  primaryFileIds: turn.fileIds,
239
- buffered: collectSlackBufferedFiles(discussion.consumed, new Set(turn.fileIds)),
240
- }), api, targetOf(turn), formatError, {
291
+ buffered: collectFoldedFiles(discussion.consumed.flatMap((read) => read.folded), new Set(turn.fileIds)),
292
+ }), placeApi(turn), targetOf(turn), formatError, {
241
293
  rendering,
242
294
  initialPreviewTs: turn.previewTs,
243
295
  threadTitle: turn.threadTitle,
@@ -249,7 +301,10 @@ export function slackChannel(options) {
249
301
  await reaction?.complete();
250
302
  else
251
303
  await reaction?.remove();
252
- }).pipe(Effect.orDie));
304
+ }).pipe(
305
+ // A release does not fail: its defect would make the turn's typed failure one too. Unreachable while
306
+ // the reaction's `complete` and `remove` catch their own failures (reaction.ts).
307
+ Effect.catchTag("PortFailure", (error) => Effect.sync(() => log.warn(`${label} could not settle the Slack reaction: ${String(error.cause)}`)))));
253
308
  },
254
309
  });
255
310
  let seq = runner.recover().reduce((maximum, turn) => Math.max(maximum, turn.seq), 0);
@@ -275,16 +330,13 @@ export function slackChannel(options) {
275
330
  const group = isSlackGroupMessage(event);
276
331
  const direct = isSlackDirectMessage(event);
277
332
  const rootTs = event.thread_ts ?? event.ts;
278
- const bufferKey = slackPlaceKey(teamId, event);
279
333
  // Listening is not speaking: every message the channel can see refines who takes part in its thread, whether or
280
334
  // not it is answered.
281
335
  if (group && event.thread_ts !== undefined) {
282
336
  threadParticipants.merge(threadKey(teamId, event.channel, event.thread_ts), { humans: [event.user] });
283
337
  }
284
338
  let routed = decide(envelope);
285
- // Two different questions (see parse.ts).
286
339
  const addressesSomeone = hasSlackMention(event.text ?? "");
287
- const mightBeTheBot = hasSlackUserMention(event.text ?? "");
288
340
  const structurallyMentionsBot = botUserId !== undefined && mentionsSlackUser(event.text ?? "", botUserId);
289
341
  // app_mention and message.* subscriptions can overlap.
290
342
  if (!routed && route === undefined && group && event.type === "message" && structurallyMentionsBot)
@@ -303,23 +355,8 @@ export function slackChannel(options) {
303
355
  routed = {};
304
356
  }
305
357
  if (!routed) {
306
- if (route === undefined && group && botUserId === undefined && mightBeTheBot)
307
- return;
308
- if (route === undefined && group) {
309
- const body = slackBufferText(slackMessageText(event));
310
- if (body) {
311
- const fileIds = slackFileIds(event);
312
- buffer.push(bufferKey, {
313
- sender: slackSenderLabel(event),
314
- body,
315
- messageId: event.ts,
316
- replyTo: event.thread_ts,
317
- fileIds: fileIds.length ? fileIds : undefined,
318
- });
319
- seen.add(logicalId);
320
- log.debug(`${label} buffered unsummoned group message ${logicalId} (place ${bufferKey})`);
321
- }
322
- }
358
+ // Not lost: the turn that is summoned here reads it from the place's history.
359
+ log.debug(`${label} not summoned — message ${logicalId}`);
323
360
  return;
324
361
  }
325
362
  const targetChannel = routed.channelId ?? event.channel;
@@ -327,13 +364,20 @@ export function slackChannel(options) {
327
364
  // Answer where asked (participant model §4).
328
365
  const defaultThread = event.thread_ts ?? event.ts;
329
366
  const threadTs = routed.threadTs === null ? undefined : (routed.threadTs ?? (sameChannel ? defaultThread : undefined));
330
- const defaultSession = threadKey(teamId, event.channel, rootTs);
367
+ const defaultConversation = threadKey(teamId, event.channel, rootTs);
368
+ // Every place's discussion under the default routing, a DM's too (what a send tool posted there is in no session);
369
+ // a routed session's place is the router's to decide.
370
+ const historyKey = route === undefined && (group || direct)
371
+ ? slackHistoryKey(teamId, { channelId: event.channel, threadTs: event.thread_ts })
372
+ : undefined;
331
373
  // Explicit user stop: a control action, never a turn — it must not queue behind the run it stops.
332
374
  if (isStopText(stripSlackMentions(event.text ?? ""))) {
333
375
  seen.add(logicalId);
334
376
  const target = { channelId: event.channel, threadTs: event.thread_ts };
335
- sideTasks.track(dispatchStop(control, routed.session ?? defaultSession, label)
336
- .then((feedback) => api.postMessage(target, feedback).then(() => undefined))
377
+ sideTasks.track(dispatchStop(agent, recordedSession(routed.conversation ?? defaultConversation), label)
378
+ .then((feedback) => placeApi({ historyKey, teamId })
379
+ .postMessage(target, feedback)
380
+ .then(() => undefined))
337
381
  .catch((error) => log.warn(`${label} stop feedback failed: ${String(error)}`)));
338
382
  return;
339
383
  }
@@ -347,9 +391,10 @@ export function slackChannel(options) {
347
391
  runner.submit({
348
392
  id: logicalId,
349
393
  seq: ++seq,
350
- session: routed.session ?? defaultSession,
394
+ conversation: routed.conversation ?? defaultConversation,
351
395
  baseText,
352
- bufferKey,
396
+ ...(historyKey !== undefined ? { historyKey } : {}),
397
+ group,
353
398
  teamId,
354
399
  channelId: targetChannel,
355
400
  threadTs,
@@ -362,7 +407,7 @@ export function slackChannel(options) {
362
407
  if (group &&
363
408
  threadTs !== undefined &&
364
409
  sameChannel &&
365
- routed.session === undefined &&
410
+ routed.conversation === undefined &&
366
411
  routed.threadTs === undefined) {
367
412
  // The ASKER counts as heard in this thread too, and both halves are written together so the record can never
368
413
  // say "the agent takes part and nobody has spoken".
@@ -1,6 +1,6 @@
1
1
  /** Durable set of users who have already received the first-run DM welcome, so it is sent at most once. */
2
2
  import { log } from "../../log.js";
3
- import { loadStateFile, saveStateFile } from "../kit/state.js";
3
+ import { loadStateFile, saveStateFile } from "../../state-file.js";
4
4
  function key(teamId, userId) {
5
5
  return `${teamId}:${userId}`;
6
6
  }
@@ -1,4 +1,3 @@
1
- /** Telegram's half of the shared context buffer (mechanics + consume protocol: ../kit/context-buffer.ts). */
2
1
  import { type ContextBuffer as GenericContextBuffer } from "../kit/context-buffer.ts";
3
2
  /** One buffered un-summoned message (object identity is the commit key). */
4
3
  export interface BufferEntry {
@@ -31,5 +30,11 @@ export declare function collectAttachments(consumed: BufferEntry[], primary: {
31
30
  images: BufferedRef[];
32
31
  skipped: number;
33
32
  };
33
+ /**
34
+ * A message the agent sent itself (through `telegramTransport`), as the place's discussion: the agent never receives
35
+ * its own messages as updates (and the Bot API has no history read), so this is the only way the chat's next turn
36
+ * knows it was said. The label says only what the transport knows — the agent sent it — not which tool did.
37
+ */
38
+ export declare function ownPostEntry(body: string, messageId: number | undefined): BufferEntry;
34
39
  export type ContextBuffer = GenericContextBuffer<BufferEntry>;
35
40
  export declare function createContextBuffer(path: string): ContextBuffer;
@@ -1,4 +1,5 @@
1
1
  /** Telegram's half of the shared context buffer (mechanics + consume protocol: ../kit/context-buffer.ts). */
2
+ import { truncateCodePointPrefix } from "../kit/text.js";
2
3
  import { capBufferedRefs, createContextBuffer as createGenericContextBuffer, } from "../kit/context-buffer.js";
3
4
  function bufferLine(e) {
4
5
  const meta = [
@@ -28,6 +29,24 @@ export function collectAttachments(consumed, primary) {
28
29
  const images = capBufferedRefs(refs((e) => e.imageIds, primary.images));
29
30
  return { files: files.kept, images: images.kept, skipped: files.skipped + images.skipped };
30
31
  }
32
+ /**
33
+ * How much of a message the agent sent itself a place keeps. A digest runs to thousands of characters, and the
34
+ * per-line bound a human message gets would keep its title only; this stays inside the buffer's budget, so later
35
+ * discussion still evicts it oldest-first.
36
+ */
37
+ const OWN_POST_MAX_CHARS = 2000;
38
+ /**
39
+ * A message the agent sent itself (through `telegramTransport`), as the place's discussion: the agent never receives
40
+ * its own messages as updates (and the Bot API has no history read), so this is the only way the chat's next turn
41
+ * knows it was said. The label says only what the transport knows — the agent sent it — not which tool did.
42
+ */
43
+ export function ownPostEntry(body, messageId) {
44
+ return {
45
+ sender: "you (sent by the agent)",
46
+ body: truncateCodePointPrefix(body.replace(/\s+/g, " ").trim(), OWN_POST_MAX_CHARS, " … (truncated)"),
47
+ messageId,
48
+ };
49
+ }
31
50
  function isBufferEntry(e) {
32
51
  const t = e;
33
52
  const strings = (v) => v === undefined || (Array.isArray(v) && v.every((x) => typeof x === "string"));
@@ -4,10 +4,10 @@
4
4
  * Split from parse.ts because this half touches the Bot API + disk; split from telegram.ts so the
5
5
  * factory keeps only wiring and the per-turn lifecycle.
6
6
  */
7
- import type { Agent, AgentEvent } from "../../agent.ts";
7
+ import type { Agent } from "../../agent-api.ts";
8
8
  import type * as Stream from "effect/Stream";
9
9
  import type { PortFailure } from "../../effect-port.ts";
10
- import { type BusyRetry } from "../kit/invoke-turn-kit.ts";
10
+ import { type TurnEvent } from "../kit/invoke-turn-kit.ts";
11
11
  import type { BufferedRef } from "./context-buffer.ts";
12
12
  /** Everything the transport needs to fetch a turn's attachments. */
13
13
  export interface TurnTransport {
@@ -31,6 +31,10 @@ export interface TurnAttachments {
31
31
  }
32
32
  /**
33
33
  * Run one turn: resolve its attachments, then ask the agent (invoke-turn-kit — a primary-attachment
34
- * failure surfaces as a `failed` event, never a silent drop).
34
+ * failure surfaces as a `rejected` event, never a silent drop). `key` is the turn's: the update it answers.
35
35
  */
36
- export declare function telegramTurnStream(agent: Agent, session: string, text: string, transport: TurnTransport, attachments: TurnAttachments, busyRetry?: BusyRetry): Stream.Stream<AgentEvent, PortFailure>;
36
+ export declare function telegramTurnStream(agent: Agent, turn: {
37
+ session: () => Promise<string>;
38
+ key: string;
39
+ text: string;
40
+ }, transport: TurnTransport, attachments: TurnAttachments): Stream.Stream<TurnEvent, PortFailure>;