@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
@@ -12,7 +12,8 @@ export function eventStream(open, label) {
12
12
  const close = iterator.return?.bind(iterator);
13
13
  if (!close || exhausted)
14
14
  return Effect.void;
15
- return Effect.promise(close).pipe(Effect.onError((cause) => Effect.sync(() => log.warn(`${label} source cleanup failed: ${String(Cause.squash(cause))}`))));
15
+ // Logged, not raised: a failing release would make the consumer's typed failure a defect.
16
+ return Effect.promise(close).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn(`${label} source cleanup failed: ${String(Cause.squash(cause))}`))));
16
17
  });
17
18
  return Effect.gen(function* () {
18
19
  const result = yield* Effect.tryPromise({
@@ -1,58 +1,57 @@
1
1
  import * as Stream from "effect/Stream";
2
- import { type Agent, type AgentEvent, type Prompt, type Scope } from "../../agent.ts";
2
+ import { type Agent, type AgentErrorData, type MessageStreamEvent, type SendRequest } from "../../agent-api.ts";
3
3
  import { type PortFailure } from "../../effect-port.ts";
4
4
  /**
5
- * One turn, end to end: resolve the platform inputs, then stream `agent.invoke` with the shared
6
- * busy-wait. All three chat channels wrote this out, and the copies had already disagreed about the
7
- * one thing that is actually a per-platform judgement (whether a load failure is worth a redelivery)
8
- * while agreeing on everything that is not.
5
+ * What a turn reads, in order: the message as it was recorded, or why there is none (`rejected`), then the events of
6
+ * its session that belong to it through its `done` (SPEC.md §6.2).
7
+ */
8
+ export type TurnEvent = {
9
+ type: "rejected";
10
+ error: AgentErrorData;
11
+ } | MessageStreamEvent;
12
+ /** An image a channel read from its platform, as a message carries it. */
13
+ export interface ImageAttachment {
14
+ mimeType: string;
15
+ /** base64 */
16
+ data: string;
17
+ }
18
+ type MessageContent = Exclude<SendRequest["content"], string>;
19
+ /** A turn's text and images as the content of one message. */
20
+ export declare function promptContent(text: string, images: readonly ImageAttachment[] | undefined): MessageContent;
21
+ /**
22
+ * One turn, end to end: the platform inputs prepared, the session the turn's conversation talks in (created and
23
+ * recorded before anything is sent, conversation-sessions.ts), then the message sent and its answer read as it is written
24
+ * (`stream`).
9
25
  *
10
- * A resolution failure becomes a `failed` EVENT. It must never throw out of the stream: the SPEC
11
- * forbids it (MUST 2), and the turn's own settlement path is what tells the user — an exception here
12
- * would take the reply with it.
26
+ * The message queues behind whatever runs in the session (a wake-up, a schedule): the channel's own turns never
27
+ * collide, its queue serializes them per conversation. Its key is the turn's, so a turn run again after a restart is the
28
+ * message it already sent: one that ended is read back, answer included.
29
+ *
30
+ * A failure to prepare is the turn's `rejected` event: nothing was sent. It must never throw out of the stream, since
31
+ * the turn's own settlement path is what tells the user; an exception here would take the reply with it.
13
32
  */
14
33
  export declare function turnStream<R>(opts: {
15
34
  agent: Agent;
16
35
  label: string;
17
- /** Resolve this turn's platform inputs (download attachments, walk a reply chain). */
36
+ /**
37
+ * The session the turn's conversation talks in: the one it has, or one created and recorded before anything is
38
+ * sent. It gets the prepared inputs, since where a new thread forks from is what its reply chain names.
39
+ */
40
+ session: (resolved: R) => Promise<string>;
41
+ /** While the turn runs, what its session reaches by its id (a thread-reading tool's room); released when it ends. */
42
+ enter?: (sessionId: string) => () => void;
43
+ /** Prepare this turn's inputs (download attachments, walk a reply chain). */
18
44
  resolve: () => Promise<R>;
19
- /** What the resolved inputs make of the turn: where it runs, and what it asks. */
45
+ /** What the prepared inputs make of the turn: what it asks, and the key it is known by. */
20
46
  turn: (resolved: R) => {
21
- scope: Scope;
22
- prompt: Prompt;
47
+ content: MessageContent;
48
+ idempotencyKey: string;
23
49
  };
24
- /** Whether a failed resolution is worth a platform redelivery. Stated per channel because it IS a
50
+ /** Whether a failed preparation is worth a platform redelivery. Stated per channel because it IS a
25
51
  * platform question — Slack reads its API error's status, and a transport that cannot tell says
26
52
  * yes, since a retried download is cheap next to an unanswered ask. */
27
53
  retryableLoadFailure: (cause: unknown) => boolean;
28
- busyRetry?: BusyRetry;
29
- }): Stream.Stream<AgentEvent, PortFailure>;
30
- /** How the busy-wait paces: retry the invoke every `delayMs` while the session's lease is held by an
31
- * EXTERNAL turn (a self-scheduled wake, a concurrent embedder invoke), up to `maxWaitMs` total. The
32
- * channel's own turns never collide (the turn-queue serializes per session), so a busy reject here is
33
- * always an outside holder — wait for it like a queued turn, instead of erroring at the user. */
34
- export interface BusyRetry {
35
- delayMs: number;
36
- maxWaitMs: number;
37
- }
38
- export declare const DEFAULT_BUSY_RETRY: BusyRetry;
39
- /**
40
- * Stream one Agent turn with the shared busy-wait. The durable-commit point is NOT here: an answer becomes durable
41
- * where its text is known and about to be written to the chat (the renderer's `onAnswered`), because "the model
42
- * finished" and "the user has it" are different events. Source pulls remain demand-driven, including while final
43
- * platform delivery runs.
44
- *
45
- * BUSY-WAIT: a `failed{code: session_busy}` FIRST event means an external turn holds this session's
46
- * lease and OUR turn never started — replay-safe. Retry (bounded) instead of yielding it: the user
47
- * sees the channel's "Thinking…" placeholder while waiting (the mirror of the scheduler deferring a
48
- * wake INTO a busy session), and only an exhausted wait surfaces the busy failure. Only a FIRST-event
49
- * busy retries — a fail-fast reject is the only shape the engine emits it in, so nothing that started
50
- * is ever re-run.
51
- */
52
- export declare function busyRetryStream(agent: Agent, scope: Scope, prompt: Prompt, { label, busyRetry }: {
53
- label: string;
54
- busyRetry?: BusyRetry;
55
- }): Stream.Stream<AgentEvent, PortFailure>;
54
+ }): Stream.Stream<TurnEvent, PortFailure>;
56
55
  /**
57
56
  * Load the turn's BACKGROUND refs — what was folded in from earlier un-summoned discussion, not the
58
57
  * ask itself. Per-ref degradation is the whole meaning of that tier: one expired file costs a warn
@@ -104,3 +103,4 @@ export declare function backgroundImagesManifest(primaryCount: number, refs: rea
104
103
  * cap-skipped) — without it, the model holds fold references it silently cannot open and may
105
104
  * pretend it read them. Neutral wording (platforms differ on WHY: expired file_ids, deleted files). */
106
105
  export declare function missingAttachmentsNote(missing: number): string;
106
+ export {};
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Shared pieces of the channels' invoke-turn modules (telegram/feishu/slack `invoke-turn.ts`) — the
3
- * halves that are channel-independent, so a retry-policy or prompt-wording change lands ONCE:
3
+ * halves that are channel-independent, so a policy or prompt-wording change lands ONCE:
4
4
  *
5
- * - {@link turnStream}: resolve inputs → ask the agent, with a load failure arriving as a `failed`
6
- * EVENT rather than a thrown iteration (SPEC MUST 2);
7
- * - {@link busyRetryStream}: the busy-retry loop around `agent.invoke`;
5
+ * - {@link turnStream}: prepare the turn's inputs → ask the agent, with a preparation failure arriving as the
6
+ * invoke's `rejected` EVENT rather than a thrown iteration;
8
7
  * - the prompt-suffix wording: {@link attachedFilesManifest}, {@link backgroundImagesManifest},
9
8
  * {@link missingAttachmentsNote}, {@link attributedFileName}.
10
9
  *
@@ -16,83 +15,75 @@
16
15
  * message-scoped Feishu keys, Slack file objects) are real differences. The two TIERS' failure
17
16
  * policies are not: see {@link loadBackground}.
18
17
  */
19
- import * as Clock from "effect/Clock";
20
18
  import * as Effect from "effect/Effect";
19
+ import * as Result from "effect/Result";
21
20
  import * as Stream from "effect/Stream";
22
- import { SESSION_BUSY_CODE } from "../../agent.js";
23
- import { eventStream } from "./event-stream.js";
24
- import { portJoin } from "../../effect-port.js";
21
+ import { AgentError, } from "../../agent-api.js";
25
22
  import { log } from "../../log.js";
23
+ import { eventStream } from "./event-stream.js";
24
+ import { portError, portJoin } from "../../effect-port.js";
25
+ /** A turn's text and images as the content of one message. */
26
+ export function promptContent(text, images) {
27
+ return [{ type: "text", text }, ...(images ?? []).map((image) => ({ type: "image", ...image }))];
28
+ }
26
29
  /**
27
- * One turn, end to end: resolve the platform inputs, then stream `agent.invoke` with the shared
28
- * busy-wait. All three chat channels wrote this out, and the copies had already disagreed about the
29
- * one thing that is actually a per-platform judgement (whether a load failure is worth a redelivery)
30
- * while agreeing on everything that is not.
30
+ * One turn, end to end: the platform inputs prepared, the session the turn's conversation talks in (created and
31
+ * recorded before anything is sent, conversation-sessions.ts), then the message sent and its answer read as it is written
32
+ * (`stream`).
31
33
  *
32
- * A resolution failure becomes a `failed` EVENT. It must never throw out of the stream: the SPEC
33
- * forbids it (MUST 2), and the turn's own settlement path is what tells the user — an exception here
34
- * would take the reply with it.
34
+ * The message queues behind whatever runs in the session (a wake-up, a schedule): the channel's own turns never
35
+ * collide, its queue serializes them per conversation. Its key is the turn's, so a turn run again after a restart is the
36
+ * message it already sent: one that ended is read back, answer included.
37
+ *
38
+ * A failure to prepare is the turn's `rejected` event: nothing was sent. It must never throw out of the stream, since
39
+ * the turn's own settlement path is what tells the user; an exception here would take the reply with it.
35
40
  */
36
41
  export function turnStream(opts) {
37
- return Stream.unwrap(portJoin(opts.resolve).pipe(Effect.map((resolved) => {
38
- const { scope, prompt } = opts.turn(resolved);
39
- return busyRetryStream(opts.agent, scope, prompt, {
40
- label: opts.label,
41
- ...(opts.busyRetry ? { busyRetry: opts.busyRetry } : {}),
42
- });
43
- }), Effect.catchTag("PortFailure", ({ cause }) => Effect.succeed(Stream.succeed({
44
- type: "failed",
45
- details: `could not load attachment: ${String(cause)}`,
46
- retryable: opts.retryableLoadFailure(cause),
47
- })))));
42
+ const rejected = (error) => Stream.succeed({ type: "rejected", error });
43
+ return Stream.unwrap(Effect.gen(function* () {
44
+ const prepared = yield* Effect.result(portJoin(opts.resolve));
45
+ if (Result.isFailure(prepared)) {
46
+ const { cause } = prepared.failure;
47
+ return rejected({
48
+ code: "unavailable",
49
+ message: `could not prepare the turn: ${String(cause)}`,
50
+ retryable: opts.retryableLoadFailure(cause),
51
+ });
52
+ }
53
+ const resolved = prepared.success;
54
+ // The Agent's own question from here on, so its own answer says whether a retry is worth it.
55
+ const session = yield* Effect.result(portJoin(() => opts.session(resolved)));
56
+ if (Result.isFailure(session))
57
+ return rejected(agentFailure("its session", session.failure.cause));
58
+ const sessionId = session.success;
59
+ const enter = opts.enter;
60
+ if (enter !== undefined)
61
+ yield* Effect.acquireRelease(Effect.sync(() => enter(sessionId)), (leave) => Effect.sync(leave));
62
+ const { content, idempotencyKey } = opts.turn(resolved);
63
+ const request = { sessionId, content, idempotencyKey, whenBusy: "queue" };
64
+ // A send the agent refused rejects the stream's first read: the turn's `rejected`, not a failure of its stream.
65
+ return eventStream(() => opts.agent.messages.stream(request), `${opts.label} (session=${sessionId})`).pipe(Stream.scoped, Stream.catchCause((cause) => {
66
+ const error = portError(cause);
67
+ return error instanceof AgentError ? rejected(agentFailure("its message", error)) : Stream.failCause(cause);
68
+ }));
69
+ }));
48
70
  }
49
- // Each retry is a lease-check-level reject (tryAcquire runs before the session is bound) — waiting is nearly
50
- // free, and the loop exits within one delay of the holder finishing. So the cap is sized to outlast a
51
- // real tool-using wake turn (minutes), not to be short: 10 min. CEILING: a holder that runs longer than
52
- // this still surfaces the busy error to the user — the bound exists so a stuck lease can't hang a chat
53
- // turn forever.
54
- export const DEFAULT_BUSY_RETRY = { delayMs: 5_000, maxWaitMs: 600_000 };
55
71
  /**
56
- * Stream one Agent turn with the shared busy-wait. The durable-commit point is NOT here: an answer becomes durable
57
- * where its text is known and about to be written to the chat (the renderer's `onAnswered`), because "the model
58
- * finished" and "the user has it" are different events. Source pulls remain demand-driven, including while final
59
- * platform delivery runs.
60
- *
61
- * BUSY-WAIT: a `failed{code: session_busy}` FIRST event means an external turn holds this session's
62
- * lease and OUR turn never started — replay-safe. Retry (bounded) instead of yielding it: the user
63
- * sees the channel's "Thinking…" placeholder while waiting (the mirror of the scheduler deferring a
64
- * wake INTO a busy session), and only an exhausted wait surfaces the busy failure. Only a FIRST-event
65
- * busy retries — a fail-fast reject is the only shape the engine emits it in, so nothing that started
66
- * is ever re-run.
72
+ * A step of the Agent's that failed: its refusal as it gave it (an `AgentError`, with its own `retryable`), or a
73
+ * failure nothing was sent past, so worth a retry. A send that may have been recorded (`unconfirmed`) is worth one too:
74
+ * the turn's send carries its key, so sending it again finds the message rather than recording a second.
67
75
  */
68
- export function busyRetryStream(agent, scope, prompt, { label, busyRetry = DEFAULT_BUSY_RETRY }) {
69
- return Stream.unwrap(Effect.map(Clock.currentTimeMillis, (started) => {
70
- const deadline = started + busyRetry.maxWaitMs;
71
- const attempt = () => Stream.suspend(() => {
72
- let retryBusy = false;
73
- let first = true;
74
- return eventStream(() => agent.invoke(scope, prompt), `${label} (session=${scope.session})`).pipe(Stream.takeWhileEffect((event) => Effect.gen(function* () {
75
- if (first &&
76
- event.type === "failed" &&
77
- event.code === SESSION_BUSY_CODE &&
78
- (yield* Clock.currentTimeMillis) + busyRetry.delayMs < deadline) {
79
- retryBusy = true;
80
- return false;
81
- }
82
- first = false;
83
- return true;
84
- })), Stream.scoped,
85
- // concat closes the attempt's scope (and its iterator) before the wait or next invoke.
86
- Stream.concat(Stream.suspend(() => retryBusy
87
- ? Stream.unwrap(Effect.gen(function* () {
88
- log.info(`${label} session ${scope.session} is busy (an external turn holds it) — retrying in ${busyRetry.delayMs}ms`);
89
- yield* Effect.sleep(busyRetry.delayMs);
90
- return attempt();
91
- }))
92
- : Stream.empty)));
93
- });
94
- return attempt();
95
- }));
76
+ function agentFailure(what, cause) {
77
+ if (cause instanceof AgentError) {
78
+ const { code, message, retryable, details } = cause;
79
+ return {
80
+ code,
81
+ message: `could not start ${what}: ${message}`,
82
+ retryable: retryable || code === "unconfirmed",
83
+ ...(details === undefined ? {} : { details }),
84
+ };
85
+ }
86
+ return { code: "unavailable", message: `could not start ${what}: ${String(cause)}`, retryable: true };
96
87
  }
97
88
  /**
98
89
  * Load the turn's BACKGROUND refs — what was folded in from earlier un-summoned discussion, not the
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Where a turn's discussion comes from. `peek` snapshots what this turn folds; `commit` runs when the turn's answer
3
+ * is recorded, so a failure or crash before then leaves the discussion to be folded again. `P` names what is read: a
4
+ * buffer bucket's key, or a place plus where this turn's read ends.
5
+ */
6
+ export interface DiscussionSource<E, P = string> {
7
+ /** Never rejects: a source that cannot read says so in `text`, and the turn proceeds without it. */
8
+ peek(place: P): {
9
+ text: string;
10
+ consumed: E[];
11
+ } | Promise<{
12
+ text: string;
13
+ consumed: E[];
14
+ }>;
15
+ commit(place: P, consumed: E[]): void;
16
+ }
17
+ /** One message of a place, as the fold renders it. */
18
+ export interface PlaceMessage {
19
+ id: string;
20
+ /** Milliseconds since the epoch. */
21
+ at: number;
22
+ /** "self": this agent, outside its own turn answers (a schedule's digest, a post from another chat). */
23
+ from: {
24
+ kind: "human" | "self" | "bot";
25
+ label: string;
26
+ };
27
+ text: string;
28
+ replyTo?: string;
29
+ images: {
30
+ key: string;
31
+ }[];
32
+ files: {
33
+ key: string;
34
+ name?: string;
35
+ }[];
36
+ }
37
+ /**
38
+ * What a turn reads: its place, up to and including its own ask. The bound is what keeps every later message out —
39
+ * an ask queued behind this one is its own turn, and folding it here would answer it twice — and it needs no record
40
+ * of which messages were asks, so a lost or cold state cannot break it. `C` is the platform's position: the newest
41
+ * message a read reached.
42
+ */
43
+ export interface PlaceRead<C> {
44
+ key: string;
45
+ until: C;
46
+ }
47
+ /** One turn's read: where it reached, what it folded (whose attachments ride along), and which outputs it passed. */
48
+ export interface PlaceDiscussion<C> {
49
+ /** Absent when the read failed: the cursor then stays, and the next turn reads the same messages again. */
50
+ cursor?: C;
51
+ folded: PlaceMessage[];
52
+ outputsPassed: string[];
53
+ }
54
+ /** What a platform read of one place returns. */
55
+ export interface PlaceListing<C> {
56
+ /** Every message id the read covered, kept or dropped: what tells which recorded outputs it passed. */
57
+ covered: string[];
58
+ /** What to fold, oldest first: system messages and what `drop` named already left out. */
59
+ messages: PlaceMessage[];
60
+ /** The newest message listed. A bounded read's cursor is its bound instead, the ask it read up to. */
61
+ newest?: C;
62
+ /** Older messages exist that the read never reached. */
63
+ earlier: boolean;
64
+ }
65
+ export interface PlaceHistory<C> extends DiscussionSource<PlaceDiscussion<C>, PlaceRead<C>> {
66
+ /** A message a turn posted into this place: in the session already, so the place's next read leaves it out. */
67
+ recordOutput(key: string, messageId: string): void;
68
+ /**
69
+ * A thread's room, read-only, for the thread's first turn (participant-model §8): the room's own next answered turn
70
+ * still reads it, so each place takes the discussion into its own memory.
71
+ */
72
+ room(key: string): Promise<{
73
+ text: string;
74
+ folded: PlaceMessage[];
75
+ }>;
76
+ /**
77
+ * A place as it reads now, for a tool: its newest messages, with no cursor and nothing left out (another session's
78
+ * asks and answers are what that place said). Rejects when the read fails: a tool's failure is the model's to see.
79
+ */
80
+ snapshot(key: string): Promise<{
81
+ text: string;
82
+ folded: PlaceMessage[];
83
+ }>;
84
+ }
85
+ /**
86
+ * A place's history read from its platform (docs/design/place-history.md): what was said in a chat or a thread since
87
+ * this agent last answered there, up to the turn's own ask. The platform owns the read; this owns what a place
88
+ * remembers between turns — its cursor, and the messages turns posted there — and what a turn leaves out of its read:
89
+ * the messages a turn posted into the place (`recordOutput`: answers, queue notices, stop feedback), kept beside the
90
+ * cursor until a read has passed them, so a busy deployment cannot push a quiet place's last answer out of a shared
91
+ * ring; and, as prompt shaping only, what the channel took as input (`isTurnInput`). What the agent sent itself from a
92
+ * send tool stays — that is what a later "what did point 3 mean?" is about.
93
+ */
94
+ export declare function createPlaceHistory<C>(deps: {
95
+ label: string;
96
+ /** Where each place's cursor and outputs persist. */
97
+ path: string;
98
+ isCursor(value: unknown): value is C;
99
+ /** Negative, zero or positive as `a` is before, at or after `b`. */
100
+ compare(a: C, b: C): number;
101
+ isTurnInput(key: string, messageId: string): boolean;
102
+ /** The place's messages after `cursor` (or its newest few, with none) and before `until`, oldest first. */
103
+ read(key: string, from: {
104
+ cursor?: C;
105
+ drop(messageId: string): boolean;
106
+ }, until?: C): Promise<PlaceListing<C>>;
107
+ }): PlaceHistory<C>;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * What a turn learns about its place (a chat, or a thread in one) beyond its own ask — and the fold that renders it,
3
+ * shared by every channel whose platform can be read (docs/design/place-history.md).
4
+ *
5
+ * Two sources fill a turn's discussion: the context buffer (`context-buffer.ts`), for a platform with no history read,
6
+ * and the place's own history, read from the platform when the turn runs ({@link createPlaceHistory}). The turn
7
+ * runner sees only {@link DiscussionSource}.
8
+ */
9
+ import { log } from "../../log.js";
10
+ import { BUFFER_LINE_MAX_CHARS, BUFFER_MAX_CHARS } from "./context-buffer.js";
11
+ import { loadStateFile, saveStateFile } from "../../state-file.js";
12
+ import { truncateCodePointPrefix } from "./text.js";
13
+ /**
14
+ * Per-message bound for the agent's own post. A digest runs to thousands of characters; the line bound would keep its
15
+ * title only. It stays inside the budget, so later discussion still pushes it out.
16
+ */
17
+ const PLACE_OWN_MAX_CHARS = 2000;
18
+ function line(message) {
19
+ const own = message.from.kind === "self";
20
+ const body = truncateCodePointPrefix(message.text.replace(/\s+/g, " ").trim(), own ? PLACE_OWN_MAX_CHARS : BUFFER_LINE_MAX_CHARS, own ? " … (truncated)" : "…");
21
+ const meta = [`msg ${message.id}`, message.replyTo ? `reply to msg ${message.replyTo}` : undefined]
22
+ .filter(Boolean)
23
+ .join(", ");
24
+ return `${message.from.label} (${meta}): ${body}`;
25
+ }
26
+ /**
27
+ * Render `messages` (oldest first) newest-first into the buffer's budget (the same bound, whichever the source). What the budget leaves out, and what the read never
28
+ * reached (`earlier`), is said in a line, never dropped silently.
29
+ */
30
+ function foldPlace(messages, earlier) {
31
+ const lines = [];
32
+ const folded = [];
33
+ let used = 0;
34
+ let cut = 0;
35
+ for (let i = messages.length - 1; i >= 0; i--) {
36
+ const message = messages[i];
37
+ const rendered = line(message);
38
+ if (cut > 0 || used + rendered.length + 1 > BUFFER_MAX_CHARS) {
39
+ cut++;
40
+ continue;
41
+ }
42
+ used += rendered.length + 1;
43
+ lines.unshift(rendered);
44
+ folded.unshift(message);
45
+ }
46
+ if (cut > 0 || earlier) {
47
+ lines.unshift(cut > 0
48
+ ? `(${cut}${earlier ? "+" : ""} earlier message${cut === 1 && !earlier ? "" : "s"} here not shown)`
49
+ : "(earlier messages here not shown)");
50
+ }
51
+ return { text: folded.length > 0 || cut > 0 ? lines.join("\n") : "", folded };
52
+ }
53
+ /**
54
+ * Places kept, least recently used dropped. A dropped place costs one re-read of its newest messages, its earlier
55
+ * answers among them.
56
+ */
57
+ const MAX_PLACES = 2000;
58
+ /**
59
+ * Of those, places no read has reached yet (only outputs, no cursor), least recently used dropped. Slack records an
60
+ * answer to a top-level ask in the thread it opens, and most such threads are never asked in again: their own bound
61
+ * keeps them from pushing out a quiet place's cursor, while a thread answered a moment ago is still among the newest
62
+ * when its follow-up comes.
63
+ */
64
+ const MAX_UNREAD_PLACES = 500;
65
+ /**
66
+ * A place's outputs not yet passed by a read. One turn posts a handful (an answer, its chunks, a queue notice); the
67
+ * bound only stops a place whose reads keep failing from growing without end.
68
+ */
69
+ const MAX_OUTPUTS = 200;
70
+ /**
71
+ * A place's history read from its platform (docs/design/place-history.md): what was said in a chat or a thread since
72
+ * this agent last answered there, up to the turn's own ask. The platform owns the read; this owns what a place
73
+ * remembers between turns — its cursor, and the messages turns posted there — and what a turn leaves out of its read:
74
+ * the messages a turn posted into the place (`recordOutput`: answers, queue notices, stop feedback), kept beside the
75
+ * cursor until a read has passed them, so a busy deployment cannot push a quiet place's last answer out of a shared
76
+ * ring; and, as prompt shaping only, what the channel took as input (`isTurnInput`). What the agent sent itself from a
77
+ * send tool stays — that is what a later "what did point 3 mean?" is about.
78
+ */
79
+ export function createPlaceHistory(deps) {
80
+ const { label, path } = deps;
81
+ const places = loadPlaces(path, label, deps.isCursor);
82
+ /**
83
+ * Mark `key` most recently used (the map's order is the eviction order), apply both bounds, and persist every place.
84
+ */
85
+ const save = (key, state, lost) => {
86
+ places.delete(key);
87
+ places.set(key, state);
88
+ const unread = [...places.keys()].filter((other) => places.get(other)?.cursor === undefined);
89
+ for (const victim of unread.slice(0, Math.max(0, unread.length - MAX_UNREAD_PLACES)))
90
+ places.delete(victim);
91
+ for (const victim of [...places.keys()].slice(0, Math.max(0, places.size - MAX_PLACES)))
92
+ places.delete(victim);
93
+ try {
94
+ saveStateFile(path, Object.fromEntries(places));
95
+ }
96
+ catch (error) {
97
+ // Both callers are past a platform write (a sent message, a recorded answer): throwing would report a send that
98
+ // happened as failed. The state holds in memory; only a restart loses it.
99
+ log.warn(`${label} could not write ${path} (${lost}): ${String(error)}`);
100
+ }
101
+ };
102
+ const read = async (key, until) => {
103
+ const state = places.get(key);
104
+ const outputs = new Set(state?.outputs);
105
+ const listing = await deps.read(key, {
106
+ ...(state?.cursor !== undefined ? { cursor: state.cursor } : {}),
107
+ drop: (id) => outputs.has(id) || deps.isTurnInput(key, id),
108
+ }, until);
109
+ return {
110
+ ...listing,
111
+ // A bounded read has accounted for everything up to its bound, the ask included: the next read starts after it.
112
+ newest: until ?? listing.newest,
113
+ outputsPassed: listing.covered.filter((id) => outputs.has(id)),
114
+ };
115
+ };
116
+ const unreadable = (key, error) => {
117
+ log.warn(`${label} could not read the history of place ${key}; the turn runs without it: ${String(error)}`);
118
+ return { text: `(could not read the recent discussion here: ${String(error)})`, folded: [] };
119
+ };
120
+ return {
121
+ async peek({ key, until }) {
122
+ try {
123
+ const { messages, newest, earlier, outputsPassed } = await read(key, until);
124
+ const { text, folded } = foldPlace(messages, earlier);
125
+ return { text, consumed: [{ ...(newest !== undefined ? { cursor: newest } : {}), folded, outputsPassed }] };
126
+ }
127
+ catch (error) {
128
+ const { text } = unreadable(key, error);
129
+ return { text, consumed: [{ folded: [], outputsPassed: [] }] };
130
+ }
131
+ },
132
+ commit({ key }, consumed) {
133
+ const done = consumed[0];
134
+ if (done?.cursor === undefined)
135
+ return;
136
+ // Only the outputs this read passed: the turn's own answer came after it and waits for the next read.
137
+ const passed = new Set(done.outputsPassed);
138
+ const state = places.get(key);
139
+ const outputs = (state?.outputs ?? []).filter((id) => !passed.has(id));
140
+ // Forward only. Turns in a place can finish out of ask order (a redelivered ask, a deferred turn); an earlier
141
+ // ask committing after a later one must not pull the cursor back over what that later turn already folded.
142
+ const cursor = state?.cursor !== undefined && deps.compare(state.cursor, done.cursor) >= 0 ? state.cursor : done.cursor;
143
+ save(key, { cursor, outputs }, "a restart may re-fold answered discussion");
144
+ },
145
+ recordOutput(key, messageId) {
146
+ const state = places.get(key);
147
+ const outputs = [...(state?.outputs ?? []), messageId].slice(-MAX_OUTPUTS);
148
+ save(key, { ...(state?.cursor !== undefined ? { cursor: state.cursor } : {}), outputs }, "after a restart this place's next read may fold the agent's own answer");
149
+ },
150
+ async room(key) {
151
+ try {
152
+ const { messages, earlier } = await read(key);
153
+ return foldPlace(messages, earlier);
154
+ }
155
+ catch (error) {
156
+ return unreadable(key, error);
157
+ }
158
+ },
159
+ async snapshot(key) {
160
+ const { messages, earlier } = await deps.read(key, { drop: () => false });
161
+ return foldPlace(messages, earlier);
162
+ },
163
+ };
164
+ }
165
+ function loadPlaces(path, label, isCursor) {
166
+ const raw = loadStateFile(path);
167
+ if (raw === undefined)
168
+ return new Map();
169
+ const valid = (value) => {
170
+ const state = value;
171
+ return (typeof state === "object" &&
172
+ state !== null &&
173
+ (state.cursor === undefined || isCursor(state.cursor)) &&
174
+ Array.isArray(state.outputs) &&
175
+ state.outputs.every((id) => typeof id === "string"));
176
+ };
177
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw) && Object.values(raw).every(valid)) {
178
+ return new Map(Object.entries(raw));
179
+ }
180
+ log.warn(`${label} unexpected shape in ${path} — every place reads its recent history afresh`);
181
+ return new Map();
182
+ }
@@ -2,18 +2,19 @@
2
2
  * Channel-neutral live-preview pieces shared by every messaging channel's preview renderer (telegram/preview.ts,
3
3
  * feishu/preview.ts, slack/preview.ts).
4
4
  */
5
- import type { AgentEvent, Json } from "../../agent.ts";
5
+ import type { Json, SessionEvent } from "../../agent-api.ts";
6
+ import type { TurnEvent } from "./invoke-turn-kit.ts";
6
7
  /** A terminal failure, as a channel hands it to its `onError`. */
7
8
  export interface ChannelFailure {
8
9
  details: string;
9
10
  retryable: boolean;
10
- /** The engine's failure code, when it set one. */
11
+ /** The harness's failure code, when it set one. */
11
12
  code?: string;
12
13
  }
13
14
  /** The customer-facing default: neutral, no leaked internals. */
14
15
  export declare function defaultErrorMessage(failed: ChannelFailure): string;
15
16
  /**
16
- * Customer-facing live-preview line for an engine-internal retry backoff (the advisory `retrying` event): neutral, no
17
+ * Customer-facing live-preview line for a harness-internal retry backoff (the advisory `retrying` event): neutral, no
17
18
  * leaked internals — the reason stays in operator logs.
18
19
  */
19
20
  export declare const RETRY_NOTICE = "\u23F3 Temporary problem \u2014 retrying\u2026";
@@ -30,16 +31,19 @@ interface ToolLine {
30
31
  export interface TurnView {
31
32
  thinking: string;
32
33
  tools: ToolLine[];
33
- /** tool-call id → its line, for `tool_ended` status flips (bookkeeping; renderers read `tools`). */
34
+ /** tool-call id → its line, for the `tool_result` status flips (bookkeeping; renderers read `tools`). */
34
35
  toolById: Map<string, ToolLine>;
35
36
  answer: string;
37
+ /** What each event adds to the answer: its delta, or the settled text no delta carried. */
38
+ textOf: (event: SessionEvent) => string;
36
39
  /** Arrival time of the first non-empty answer delta; age-based reveal policies read it. */
37
40
  answerSince?: number;
38
41
  /** An advisory retry backoff is in progress (closed again by any subsequent progress event). */
39
42
  retrying: boolean;
40
43
  }
41
44
  export declare function createTurnView(): TurnView;
42
- export declare function applyTurnEvent(view: TurnView, e: AgentEvent, now?: number): boolean;
45
+ /** Fold one event of a turn's invoke into its view; true when what a renderer shows changed. */
46
+ export declare function applyTurnEvent(view: TurnView, e: TurnEvent, now?: number): boolean;
43
47
  /**
44
48
  * The tool-activity block: one `🔧 label …/✓/✗` line per call, in call order. A running call shows its latest
45
49
  * progress after the mark (`🔧 Bash npm test … › 42 passed`); a finished one drops it.