@fastagent-sh/fastagent 0.24.3 → 0.25.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 (480) hide show
  1. package/README.md +53 -82
  2. package/dist/agent-api.d.ts +452 -0
  3. package/dist/agent-api.js +201 -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 +77 -0
  9. package/dist/authoring.js +238 -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 +115 -63
  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 +17 -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 +252 -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 +190 -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 +131 -16
  164. package/dist/cli/commands/login.d.ts +31 -2
  165. package/dist/cli/commands/login.js +413 -64
  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 +3 -2
  184. package/dist/cli/login-relay.js +18 -6
  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 +68 -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 +217 -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 +222 -201
  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 +51 -26
  260. package/dist/effect-port.d.ts +4 -4
  261. package/dist/effect-port.js +4 -4
  262. package/dist/environment/apt.d.ts +4 -0
  263. package/dist/environment/apt.js +32 -0
  264. package/dist/environment/declare.d.ts +59 -0
  265. package/dist/environment/declare.js +210 -0
  266. package/dist/environment/mise.d.ts +50 -0
  267. package/dist/environment/mise.js +268 -0
  268. package/dist/feishu.d.ts +2 -2
  269. package/dist/feishu.js +2 -2
  270. package/dist/git.d.ts +13 -0
  271. package/dist/git.js +55 -0
  272. package/dist/harnesses/models/catalog.d.ts +20 -0
  273. package/dist/harnesses/models/catalog.js +254 -0
  274. package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
  275. package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
  276. package/dist/harnesses/models/environment.d.ts +43 -0
  277. package/dist/harnesses/models/environment.js +82 -0
  278. package/dist/{engines/pi → harnesses/models}/login.d.ts +57 -26
  279. package/dist/{engines/pi → harnesses/models}/login.js +187 -79
  280. package/dist/harnesses/models/models.d.ts +83 -0
  281. package/dist/harnesses/models/models.js +158 -0
  282. package/dist/harnesses/pi-durable/agent.d.ts +42 -0
  283. package/dist/harnesses/pi-durable/agent.js +579 -0
  284. package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
  285. package/dist/harnesses/pi-durable/defaults.js +26 -0
  286. package/dist/harnesses/pi-durable/define.d.ts +77 -0
  287. package/dist/harnesses/pi-durable/define.js +28 -0
  288. package/dist/harnesses/pi-durable/entries.d.ts +32 -0
  289. package/dist/harnesses/pi-durable/entries.js +126 -0
  290. package/dist/harnesses/pi-durable/events.d.ts +25 -0
  291. package/dist/harnesses/pi-durable/events.js +238 -0
  292. package/dist/harnesses/pi-durable/ids.d.ts +16 -0
  293. package/dist/harnesses/pi-durable/ids.js +55 -0
  294. package/dist/harnesses/pi-durable/messages.d.ts +41 -0
  295. package/dist/harnesses/pi-durable/messages.js +116 -0
  296. package/dist/harnesses/pi-durable/open.d.ts +88 -0
  297. package/dist/harnesses/pi-durable/open.js +329 -0
  298. package/dist/harnesses/pi-durable/reads.d.ts +30 -0
  299. package/dist/harnesses/pi-durable/reads.js +94 -0
  300. package/dist/harnesses/pi-durable/registry.d.ts +18 -0
  301. package/dist/harnesses/pi-durable/registry.js +56 -0
  302. package/dist/harnesses/pi-durable/release.d.ts +13 -0
  303. package/dist/harnesses/pi-durable/release.js +40 -0
  304. package/dist/harnesses/pi-durable/serve.d.ts +80 -0
  305. package/dist/harnesses/pi-durable/serve.js +156 -0
  306. package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
  307. package/dist/harnesses/pi-durable/sessions.js +47 -0
  308. package/dist/harnesses/pi-durable/storage.d.ts +4 -0
  309. package/dist/harnesses/pi-durable/storage.js +49 -0
  310. package/dist/harnesses/pi-durable/stream.d.ts +20 -0
  311. package/dist/harnesses/pi-durable/stream.js +221 -0
  312. package/dist/harnesses/pi-durable/text.d.ts +30 -0
  313. package/dist/harnesses/pi-durable/text.js +292 -0
  314. package/dist/harnesses/pi-durable/wake.d.ts +4 -0
  315. package/dist/harnesses/pi-durable/wake.js +81 -0
  316. package/dist/http/client.d.ts +6 -0
  317. package/dist/http/client.js +256 -0
  318. package/dist/http/contract.d.ts +541 -0
  319. package/dist/http/contract.js +179 -0
  320. package/dist/http/server.d.ts +34 -0
  321. package/dist/http/server.js +194 -0
  322. package/dist/index.d.ts +17 -4
  323. package/dist/index.js +10 -5
  324. package/dist/lark.d.ts +2 -2
  325. package/dist/lark.js +2 -2
  326. package/dist/locked-file.d.ts +17 -0
  327. package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
  328. package/dist/once.d.ts +6 -0
  329. package/dist/once.js +23 -0
  330. package/dist/paths.d.ts +36 -52
  331. package/dist/paths.js +66 -182
  332. package/dist/runtime.d.ts +25 -2
  333. package/dist/runtime.js +27 -0
  334. package/dist/scaffold/add-channel.d.ts +14 -27
  335. package/dist/scaffold/add-channel.js +82 -180
  336. package/dist/scaffold/init.d.ts +17 -18
  337. package/dist/scaffold/init.js +87 -99
  338. package/dist/scaffold/templates/SYSTEM.md +12 -0
  339. package/dist/scaffold/templates/env.example +4 -10
  340. package/dist/scaffold/templates/fastagent.config.ts +21 -33
  341. package/dist/scaffold/templates/gitignore +8 -1
  342. package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
  343. package/dist/scaffold/templates.d.ts +9 -4
  344. package/dist/scaffold/templates.js +7 -5
  345. package/dist/scaffold/vendor-skill.d.ts +1 -4
  346. package/dist/scaffold/vendor-skill.js +5 -6
  347. package/dist/schedule/cron.d.ts +13 -0
  348. package/dist/schedule/cron.js +52 -0
  349. package/dist/schedule/eventbridge-cron.d.ts +17 -0
  350. package/dist/schedule/eventbridge-cron.js +88 -0
  351. package/dist/schedule/schedule.d.ts +23 -0
  352. package/dist/schedule/schedule.js +34 -0
  353. package/dist/schedule/scheduler.d.ts +34 -33
  354. package/dist/schedule/scheduler.js +251 -137
  355. package/dist/schedule/state.d.ts +4 -14
  356. package/dist/schedule/state.js +7 -7
  357. package/dist/schedule/wake-alarm.d.ts +22 -2
  358. package/dist/schedule/wake-alarm.js +66 -20
  359. package/dist/schedule/wakeups.d.ts +16 -15
  360. package/dist/schedule/wakeups.js +47 -45
  361. package/dist/service.d.ts +56 -169
  362. package/dist/service.js +91 -187
  363. package/dist/serving-command.d.ts +10 -0
  364. package/dist/serving-command.js +16 -0
  365. package/dist/slack.d.ts +1 -1
  366. package/dist/slack.js +1 -1
  367. package/dist/{channels/kit/state.js → state-file.js} +6 -6
  368. package/dist/telegram.d.ts +1 -0
  369. package/dist/telegram.js +1 -0
  370. package/dist/tunnel.d.ts +3 -0
  371. package/dist/tunnel.js +12 -4
  372. package/dist/value-file.d.ts +21 -0
  373. package/dist/value-file.js +108 -0
  374. package/dist/version.d.ts +5 -0
  375. package/dist/version.js +14 -1
  376. package/package.json +18 -28
  377. package/dist/agent.d.ts +0 -106
  378. package/dist/agent.js +0 -21
  379. package/dist/channels/control.d.ts +0 -31
  380. package/dist/channels/control.js +0 -329
  381. package/dist/channels/define-channel.d.ts +0 -39
  382. package/dist/channels/define-channel.js +0 -9
  383. package/dist/channels/feishu/context-buffer.d.ts +0 -35
  384. package/dist/channels/feishu/context-buffer.js +0 -65
  385. package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
  386. package/dist/channels/http.d.ts +0 -23
  387. package/dist/channels/http.js +0 -57
  388. package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
  389. package/dist/channels/slack/context-buffer.d.ts +0 -23
  390. package/dist/channels/slack/context-buffer.js +0 -37
  391. package/dist/channels/sse.d.ts +0 -4
  392. package/dist/channels/sse.js +0 -66
  393. package/dist/cli/commands/chat.d.ts +0 -3
  394. package/dist/cli/commands/chat.js +0 -14
  395. package/dist/cli/commands/routine-run.d.ts +0 -6
  396. package/dist/cli/commands/routine-run.js +0 -46
  397. package/dist/cli/commands/routine.d.ts +0 -25
  398. package/dist/cli/commands/routine.js +0 -122
  399. package/dist/cli/commands/tool.d.ts +0 -1
  400. package/dist/cli/commands/tool.js +0 -57
  401. package/dist/collect.d.ts +0 -63
  402. package/dist/collect.js +0 -90
  403. package/dist/core.d.ts +0 -13
  404. package/dist/core.js +0 -15
  405. package/dist/engines/pi/agent-models.d.ts +0 -83
  406. package/dist/engines/pi/agent-models.js +0 -120
  407. package/dist/engines/pi/agent-session-factory.d.ts +0 -121
  408. package/dist/engines/pi/agent-session-factory.js +0 -517
  409. package/dist/engines/pi/chat.d.ts +0 -3
  410. package/dist/engines/pi/chat.js +0 -8
  411. package/dist/engines/pi/config.d.ts +0 -71
  412. package/dist/engines/pi/config.js +0 -199
  413. package/dist/engines/pi/create.d.ts +0 -170
  414. package/dist/engines/pi/create.js +0 -343
  415. package/dist/engines/pi/definition.d.ts +0 -74
  416. package/dist/engines/pi/definition.js +0 -236
  417. package/dist/engines/pi/entry-images.d.ts +0 -14
  418. package/dist/engines/pi/entry-images.js +0 -23
  419. package/dist/engines/pi/invoke-session.d.ts +0 -18
  420. package/dist/engines/pi/invoke-session.js +0 -403
  421. package/dist/engines/pi/locked-file.d.ts +0 -9
  422. package/dist/engines/pi/machine.d.ts +0 -53
  423. package/dist/engines/pi/machine.js +0 -137
  424. package/dist/engines/pi/models.d.ts +0 -200
  425. package/dist/engines/pi/models.js +0 -533
  426. package/dist/engines/pi/open.d.ts +0 -180
  427. package/dist/engines/pi/open.js +0 -270
  428. package/dist/engines/pi/openai-account-models.d.ts +0 -35
  429. package/dist/engines/pi/openai-account-models.js +0 -116
  430. package/dist/engines/pi/report.d.ts +0 -18
  431. package/dist/engines/pi/report.js +0 -59
  432. package/dist/engines/pi/retry-event.d.ts +0 -6
  433. package/dist/engines/pi/retry-event.js +0 -15
  434. package/dist/engines/pi/service.d.ts +0 -9
  435. package/dist/engines/pi/service.js +0 -14
  436. package/dist/engines/pi/session-builder.d.ts +0 -13
  437. package/dist/engines/pi/session-builder.js +0 -117
  438. package/dist/engines/pi/session-control.d.ts +0 -77
  439. package/dist/engines/pi/session-control.js +0 -908
  440. package/dist/engines/pi/session-effects.d.ts +0 -14
  441. package/dist/engines/pi/session-effects.js +0 -30
  442. package/dist/engines/pi/session-inheritance.d.ts +0 -21
  443. package/dist/engines/pi/session-inheritance.js +0 -204
  444. package/dist/engines/pi/session-markers.d.ts +0 -49
  445. package/dist/engines/pi/session-markers.js +0 -60
  446. package/dist/engines/pi/session-settings.d.ts +0 -67
  447. package/dist/engines/pi/session-settings.js +0 -119
  448. package/dist/engines/pi/session-store.d.ts +0 -69
  449. package/dist/engines/pi/session-store.js +0 -430
  450. package/dist/engines/pi/tool-context.d.ts +0 -45
  451. package/dist/engines/pi/tool-context.js +0 -31
  452. package/dist/engines/pi/tool.d.ts +0 -99
  453. package/dist/engines/pi/tool.js +0 -118
  454. package/dist/engines/pi/turn-kit.d.ts +0 -83
  455. package/dist/engines/pi/turn-kit.js +0 -275
  456. package/dist/engines/pi/wake-tool.d.ts +0 -9
  457. package/dist/engines/pi/wake-tool.js +0 -92
  458. package/dist/loader.d.ts +0 -41
  459. package/dist/loader.js +0 -116
  460. package/dist/node.d.ts +0 -3
  461. package/dist/node.js +0 -4
  462. package/dist/observe.d.ts +0 -6
  463. package/dist/observe.js +0 -47
  464. package/dist/pi.d.ts +0 -25
  465. package/dist/pi.js +0 -15
  466. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  467. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  468. package/dist/schedule/discover.d.ts +0 -17
  469. package/dist/schedule/discover.js +0 -61
  470. package/dist/schedule/routine.d.ts +0 -43
  471. package/dist/schedule/routine.js +0 -32
  472. package/dist/schedule/run.d.ts +0 -55
  473. package/dist/schedule/run.js +0 -146
  474. package/dist/secrets-gate.d.ts +0 -32
  475. package/dist/secrets-gate.js +0 -33
  476. package/dist/session-remote.d.ts +0 -47
  477. package/dist/session-remote.js +0 -460
  478. package/dist/session.d.ts +0 -466
  479. package/dist/session.js +0 -57
  480. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
@@ -2,7 +2,8 @@
2
2
  * Canonical Feishu bot-channel engine: verified webhook or official-SDK WebSocket → dedup → route → persist → enqueue
3
3
  * → stream a live card.
4
4
  */
5
- import { isAbsolute, join } from "node:path";
5
+ import { join } from "node:path";
6
+ import { Effect } from "effect";
6
7
  import { log } from "../../log.js";
7
8
  import { readBodyCapped } from "../body.js";
8
9
  import { text } from "../respond.js";
@@ -10,22 +11,24 @@ import { secretEquals } from "../secret.js";
10
11
  import { createSeenRing } from "../kit/seen.js";
11
12
  import { createTaskTracker } from "../kit/tasks.js";
12
13
  import { attachmentsDir } from "../kit/attachment-path.js";
13
- import { ensureStateHome, loadStateFile, saveStateFile } from "../kit/state.js";
14
+ import { loadStateFile, saveStateFile } from "../../state-file.js";
14
15
  import { signatureIsFresh } from "../kit/signature.js";
15
16
  import { dispatchStop, isStopText } from "../kit/stop-command.js";
17
+ import { createRoomThreads, registerRoomThreads } from "../kit/room-threads.js";
18
+ import { threadForkSource } from "../kit/thread-fork.js";
16
19
  import { createTurnRunner } from "../kit/turn-runner.js";
17
20
  import { portJoin } from "../../effect-port.js";
18
21
  import { createTurnStore } from "../kit/turn-store.js";
19
22
  import { discussionBlock } from "../kit/context-buffer.js";
20
23
  import { FEISHU_CLOUD } from "./cloud.js";
21
- import { collectFeishuBufferedAttachments, createFeishuContextBuffer, feishuBufferPlaceKey, feishuBufferText, } from "./context-buffer.js";
24
+ import { collectFoldedAttachments, createFeishuPlaceHistory, feishuHistoryKey, feishuPlaceOf, } from "./history.js";
22
25
  import { decryptEvent, verifySignature } from "./crypto.js";
23
26
  import { feishuTurnStream } from "./invoke-turn.js";
24
27
  import { createFeishuApi } from "./feishu-api.js";
25
28
  import { normalizeFeishuMessage } from "./normalize.js";
26
29
  import { createThreadParticipants } from "../kit/thread-participants.js";
27
- import { FEISHU_GROUP_CONTEXT_SCOPE, FEISHU_MESSAGE_READ_REQUEST, FEISHU_MESSAGE_READ_SCOPE, scopeSatisfied, } from "./setup-mode.js";
28
- import { cloudEnvelope, defaultFeishuRoute, feishuEnvelope, placeKey, senderId, senderLabel, } from "./parse.js";
30
+ import { FEISHU_AGENT_SCOPES, feishuIngressFor, scopeSatisfied } from "./setup-mode.js";
31
+ import { cloudEnvelope, defaultFeishuRoute, feishuEnvelope, placeKey, senderId, } from "./parse.js";
29
32
  import { defaultErrorMessage, mountFeishuPreview, settleFeishuPreview, feishuReply, } from "./preview.js";
30
33
  import { connectFeishuWs } from "./ws-ingress.js";
31
34
  import { registerFeishuApi } from "./shared-api.js";
@@ -52,16 +55,18 @@ function isStoredFeishuTurn(t) {
52
55
  v.every((x) => typeof x.msg === "string" && typeof x.key === "string");
53
56
  return (typeof r?.id === "string" &&
54
57
  typeof r.seq === "number" &&
55
- typeof r.session === "string" &&
58
+ typeof r.conversation === "string" &&
56
59
  typeof r.baseText === "string" &&
57
- typeof r.bufferKey === "string" &&
60
+ (r.historyKey === undefined || typeof r.historyKey === "string") &&
61
+ typeof r.group === "boolean" &&
62
+ typeof r.askAt === "number" &&
58
63
  typeof r.chatId === "string" &&
59
64
  (r.replyTo === undefined || typeof r.replyTo === "string") &&
60
65
  (r.queueReplyTo === undefined || typeof r.queueReplyTo === "string") &&
61
66
  (r.replyInThread === undefined || typeof r.replyInThread === "boolean") &&
62
67
  (r.parentId === undefined || typeof r.parentId === "string") &&
63
- (r.parentSession === undefined || typeof r.parentSession === "string") &&
64
- (r.roomBufferKey === undefined || typeof r.roomBufferKey === "string") &&
68
+ (r.parentConversation === undefined || typeof r.parentConversation === "string") &&
69
+ (r.roomKey === undefined || typeof r.roomKey === "string") &&
65
70
  refs(r.images) &&
66
71
  refs(r.files) &&
67
72
  typeof r.attempts === "number");
@@ -70,6 +75,10 @@ function isStoredFeishuTurn(t) {
70
75
  export function feishuChannel(opts) {
71
76
  return buildFeishuChannel(FEISHU_CLOUD, opts, feishuChannel.name);
72
77
  }
78
+ /** How this process receives the Feishu channel: `FEISHU_INGRESS`, else WebSocket under `dev` and webhook otherwise. */
79
+ export function feishuIngress() {
80
+ return feishuIngressFor("feishu");
81
+ }
73
82
  /** Build the canonical Feishu WebSocket long-connection channel. */
74
83
  export function feishuWebSocketChannel(opts) {
75
84
  return buildFeishuWebSocketChannel(FEISHU_CLOUD, opts, feishuWebSocketChannel.name);
@@ -125,50 +134,57 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
125
134
  const baseUrl = opts.apiBaseUrl ?? profile.apiBase;
126
135
  const { kind } = profile;
127
136
  const label = `[${kind}]`;
128
- return ({ agent, stateRoot, control }) => {
137
+ return ({ agent, sessionFor, recordedSession, stateDir: stateHome }) => {
129
138
  // Credential checks run when serving starts, not while the authored module is imported.
130
139
  if (!appId || !appSecret) {
131
140
  throw new Error(`${factoryName} requires appId + appSecret (developer console → Credentials & Basic Info)`);
132
141
  }
133
142
  const formatError = onError ?? defaultErrorMessage;
143
+ // Shared with the send tools (registered below). A turn's own posts go through `placeApi`.
134
144
  const api = createFeishuApi({ kind, baseUrl, appId, appSecret });
135
145
  void api.listAppScopes().then((scopes) => {
136
- const grantedScope = (name) => scopes.some((scope) => scope.name === name && scope.grantStatus === 1 && (scope.type === undefined || scope.type === "tenant"));
137
- if (grantedScope(FEISHU_GROUP_CONTEXT_SCOPE)) {
138
- // This scope settles the rule's whole input: it delivers the un-mentioned group messages the channel
139
- // buffers, which is also what lets it HEAR a thread.
140
- log.info(`${label} group visibility: context-aware — buffered discussion enabled; bare replies work in a thread once the agent has been mentioned in it`);
141
- }
142
- else {
143
- log.warn(`${label} group visibility: @mentions only — ${FEISHU_GROUP_CONTEXT_SCOPE} is not granted; bare replies in the agent's threads + group context buffering are unavailable`);
144
- }
145
- // Reported OUTSIDE the branch above: the quoted-message read runs in every chat type and every posture (a p2p
146
- // thread's opening ask, any quoted @mention in a group), so pairing this warning with the group scope would
147
- // leave a mention-only deployment silently losing every referent.
148
- if (!scopeSatisfied(FEISHU_MESSAGE_READ_REQUEST, grantedScope)) {
149
- log.warn(`${label} ${FEISHU_MESSAGE_READ_SCOPE} is not granted — a message quoted by an ask cannot be read, and degrades to a marker in the prompt`);
146
+ const granted = (name) => scopes.some((scope) => scope.name === name && scope.grantStatus === 1 && (scope.type === undefined || scope.type === "tenant"));
147
+ const missing = FEISHU_AGENT_SCOPES.filter((entry) => !scopeSatisfied(entry, granted));
148
+ if (missing.length === 0) {
149
+ log.info(`${label} permissions: every agent scope granted — the agent hears its group chats`);
150
150
  }
151
- }, (error) => log.warn(`${label} could not inspect group visibility: ${String(error)}`));
152
- // The channel-state convention: this channel's durable home is `<stateRoot>/channels/<kind>` (engine state at the
153
- // root, channel state under `channels/<kind>/`).
154
- if (!isAbsolute(stateRoot)) {
155
- throw new Error(`${factoryName} requires an absolute ctx.stateRoot, got "${stateRoot}"`);
156
- }
157
- const stateHome = join(stateRoot, "channels", kind);
158
- ensureStateHome(stateHome); // buffers/files may carry chat content; the agent .gitignore covers .state/
151
+ for (const entry of missing)
152
+ log.warn(`${label} ${entry.request} is not granted — ${entry.withoutIt}`);
153
+ }, (error) => log.warn(`${label} could not inspect the app's permissions: ${String(error)}`));
159
154
  const botOpenId = createBotIdentity({ api, appId, label, botFile: join(stateHome, "bot.json") });
160
155
  const decide = route ?? ((event) => defaultFeishuRoute(event, { botOpenId: botOpenId() }));
161
156
  const threadParticipants = createThreadParticipants(join(stateHome, "thread-participants.json"), label);
162
157
  /** This channel's place key for a thread (the shared store is key-agnostic). */
163
158
  // The SAME identity the session uses (`placeKey`) — a thread's place.
164
159
  const threadKey = (chatId, threadId) => placeKey(kind, { chat_id: chatId, thread_id: threadId });
165
- const buffer = createFeishuContextBuffer(join(stateHome, "buffers.json"), label);
166
160
  const store = createTurnStore(join(stateHome, "turns.json"), {
167
161
  label,
168
162
  isRecord: isStoredFeishuTurn,
169
163
  order: (a, b) => a.seq - b.seq,
170
164
  });
165
+ // Every message this channel took as input (a turn, a /stop): the dedup ring. A history read also leaves these out,
166
+ // to shape the prompt only — what keeps a queued ask out of an earlier turn is the read's bound, not this ring.
171
167
  const seen = createSeenRing(join(stateHome, "seen.json"), label);
168
+ const history = createFeishuPlaceHistory({
169
+ api,
170
+ appId,
171
+ label,
172
+ path: join(stateHome, "history.json"),
173
+ isTurnInput: (id) => seen.has(id),
174
+ });
175
+ // A group turn's room, for the thread-reading tool (`feishuThreads`): the chat it was asked in, and its threads.
176
+ const rooms = createRoomThreads({
177
+ list: (chatId) => history.threads(chatId),
178
+ read: async (chatId, threadId) => (await history.snapshot(feishuHistoryKey({ chatId, threadId }))).text ||
179
+ `(thread ${threadId} shows no messages)`,
180
+ });
181
+ registerRoomThreads(kind, stateHome, rooms);
182
+ /**
183
+ * The client a turn posts through: what it posts into its place (answer, queue notice, stop feedback) is in the
184
+ * session already, so the place records it and its next read leaves it out. The send tools use the plain client,
185
+ * so what the agent posts itself stays discussion. A routed turn has no place and reads no history.
186
+ */
187
+ const placeApi = (historyKey) => historyKey === undefined ? api : api.recordingSends((id) => history.recordOutput(historyKey, id));
172
188
  // Side tasks (stop feedback) run off the ingress path but drain in turnsIdle.
173
189
  const sideTasks = createTaskTracker(label);
174
190
  const targetOf = (r) => ({
@@ -185,16 +201,16 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
185
201
  // operator log line.
186
202
  const notifyDropped = (r) => {
187
203
  const body = "⚠️ I couldn’t complete an earlier request — please ask again.";
188
- void settleFeishuPreview(api, targetOf(r), r.preview, body).catch((e) => log.warn(`${label} could not notify a dropped turn (session=${r.session}): ${String(e)}`));
204
+ void settleFeishuPreview(placeApi(r.historyKey), targetOf(r), r.preview, body).catch((e) => log.warn(`${label} could not notify a dropped turn (conversation=${r.conversation}): ${String(e)}`));
189
205
  };
190
206
  const runner = createTurnRunner({
191
207
  label,
192
208
  store,
193
- buffer,
209
+ discussion: history,
194
210
  seen,
195
211
  toStored: ({ preview: _live, ...intent }) => ({ ...intent, attempts: 0 }),
196
212
  fromStored: ({ attempts: _a, ...intent }) => ({ ...intent, preview: undefined }),
197
- bufferKey: (rec) => rec.bufferKey,
213
+ discussionKey: (rec) => rec.historyKey === undefined ? undefined : { key: rec.historyKey, until: { at: rec.askAt, id: rec.id } },
198
214
  where: (rec) => `chat=${rec.chatId}`,
199
215
  // Queue feedback: mount that turn's preview early with a queue status.
200
216
  onQueuedBehind: (rec) => {
@@ -205,7 +221,7 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
205
221
  });
206
222
  const mount = () => {
207
223
  fired = true;
208
- mountFeishuPreview(api, queueTargetOf(rec), QUEUED_PLACEHOLDER, label)
224
+ mountFeishuPreview(placeApi(rec.historyKey), queueTargetOf(rec), QUEUED_PLACEHOLDER, label)
209
225
  .then((preview) => {
210
226
  rec.preview = preview;
211
227
  }, (e) => log.warn(`${label} queue preview failed (the turn still runs): ${String(e)}`))
@@ -230,42 +246,54 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
230
246
  // honest delayed status.
231
247
  onDeferred: (rec) => {
232
248
  if (rec.preview !== undefined) {
233
- void settleFeishuPreview(api, targetOf(rec), rec.preview, DEFERRED_PLACEHOLDER).catch((e) => log.warn(`${label} could not update a deferred turn's queue preview: ${String(e)}`));
249
+ void settleFeishuPreview(placeApi(rec.historyKey), targetOf(rec), rec.preview, DEFERRED_PLACEHOLDER).catch((e) => log.warn(`${label} could not update a deferred turn's queue preview: ${String(e)}`));
234
250
  }
235
251
  },
236
252
  notifyDropped,
237
253
  // `rec.preview`: the queue card THIS process mounted if the record waited behind another turn.
238
- deliverAnswer: (rec, answer) => portJoin(() => settleFeishuPreview(api, targetOf(rec), rec.preview, answer)),
239
- execute: (rec, discussion, onAnswered) => {
240
- // PEEK and never commit: the room still owes this discussion to its OWN memory (§8).
241
- // ponytail: independent threaded roots in one main chat dequeue concurrently and may both fold the room's
242
- // snapshot before either commits it. That fan-out loses nothing; claiming by buffer key would instead couple
243
- // otherwise-independent root sessions and require failure rollback.
244
- const room = rec.roomBufferKey !== undefined ? buffer.peek(rec.roomBufferKey) : undefined;
254
+ deliverAnswer: (rec, answer) => portJoin(() => settleFeishuPreview(placeApi(rec.historyKey), targetOf(rec), rec.preview, answer)),
255
+ execute: (rec, discussion, onAnswered) => Effect.promise(async () =>
256
+ // Read-only: the room's own next answered turn still reads it into the room's memory (§8).
257
+ rec.roomKey !== undefined ? history.room(rec.roomKey) : undefined).pipe(Effect.flatMap((room) => {
245
258
  const roomBlock = room?.text
246
259
  ? `[recent discussion in the room this thread branched from — not yet answered there:\n${room.text}\n]\n\n`
247
260
  : "";
248
261
  const prompt = `${roomBlock}${discussionBlock(discussion.text)}${rec.baseText}`;
249
- // Room entries FIRST: the collector keeps the TAIL under its cap, so the thread's own attachments win the
262
+ // Room messages FIRST: the collector keeps the TAIL under its cap, so the thread's own attachments win the
250
263
  // slots.
251
- const buffered = collectFeishuBufferedAttachments([...(room?.consumed ?? []), ...discussion.consumed], {
264
+ const buffered = collectFoldedAttachments([...(room?.folded ?? []), ...discussion.consumed.flatMap((read) => read.folded)], {
252
265
  images: rec.images.map((ref) => ({ messageId: ref.msg, key: ref.key })),
253
- files: rec.files.map((ref) => ({ messageId: ref.msg, key: ref.key, name: ref.name })),
266
+ files: rec.files.map((ref) => ({ messageId: ref.msg, key: ref.key })),
254
267
  });
255
- // Recorded at ingress (see submit) — never re-derived from the session key, which may be a routed OPAQUE id
256
- // that only looks like a place key.
257
- const parentSession = rec.parentSession;
258
- return feishuReply(feishuTurnStream(agent, rec.session, prompt, {
259
- api,
268
+ // Recorded at ingress (see submit), never re-derived from the conversation key, which may be a routed
269
+ // OPAQUE id that only looks like a place key.
270
+ const parent = rec.parentConversation;
271
+ // A group turn under the default routing reads its chat's threads; a DM has none to read.
272
+ const threadsRoom = rec.group && rec.historyKey !== undefined ? feishuPlaceOf(rec.historyKey).chatId : undefined;
273
+ const turnApi = placeApi(rec.historyKey);
274
+ return feishuReply(feishuTurnStream(agent, {
275
+ session: (chain) => sessionFor(rec.conversation, {
276
+ forkFrom: async () => {
277
+ // A thread of a room nothing was said in starts empty: there is nothing to fork.
278
+ const room = parent === undefined ? undefined : recordedSession(parent);
279
+ return room === undefined ? undefined : threadForkSource(agent, room, chain);
280
+ },
281
+ }),
282
+ ...(threadsRoom === undefined
283
+ ? {}
284
+ : { enter: (sessionId) => rooms.enter(sessionId, threadsRoom) }),
285
+ key: rec.id,
286
+ text: prompt,
287
+ }, {
288
+ api: turnApi,
260
289
  chatId: rec.chatId,
261
290
  filesDir: attachmentsDir(stateHome),
262
291
  label,
263
292
  appId,
264
- ...(parentSession !== undefined ? { parentSession } : {}),
265
- }, { primary: { images: rec.images, files: rec.files, parentId: rec.parentId }, buffered }), api, targetOf(rec), formatError, rec.preview, label, onAnswered);
266
- },
293
+ }, { primary: { images: rec.images, files: rec.files, parentId: rec.parentId }, buffered }), turnApi, targetOf(rec), formatError, rec.preview, label, onAnswered);
294
+ })),
267
295
  });
268
- registerFeishuApi(stateRoot, kind, api);
296
+ registerFeishuApi(appId, kind, api);
269
297
  let seqCounter = runner.recover().reduce((max, r) => Math.max(max, r.seq), 0);
270
298
  // Who the agent has heard in a thread decides whether a bare message addresses it (participant model §3), and it
271
299
  // comes from what this channel observed.
@@ -296,7 +324,6 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
296
324
  const normalized = normalizeFeishuMessage(event);
297
325
  if (!normalized)
298
326
  return;
299
- const bufferKey = feishuBufferPlaceKey(normalized.conversation);
300
327
  const isHumanGroup = event.sender?.sender_type === "user" && m.chat_type === "group";
301
328
  // Listening is not speaking: every message the channel can see refines who takes part in its thread, whether or
302
329
  // not it is answered.
@@ -312,50 +339,26 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
312
339
  r = {};
313
340
  }
314
341
  if (!r) {
315
- if (route === undefined && isHumanGroup) {
316
- const bodyText = feishuBufferText(normalized.content.text);
317
- if (bodyText) {
318
- const resources = normalized.content.resources;
319
- const images = resources
320
- .filter((resource) => resource.kind === "image")
321
- .map((resource) => ({ messageId: resource.messageId, key: resource.key }));
322
- const files = resources
323
- .filter((resource) => resource.kind === "file" || resource.kind === "audio" || resource.kind === "video")
324
- .map((resource) => ({
325
- messageId: resource.messageId,
326
- key: resource.key,
327
- name: resource.name,
328
- }));
329
- // A write failure escapes this boundary.
330
- buffer.push(bufferKey, {
331
- sender: senderLabel(event.sender) ?? "someone",
332
- body: bodyText,
333
- messageId: m.message_id,
334
- replyTo: m.parent_id,
335
- files: files.length ? files : undefined,
336
- images: images.length ? images : undefined,
337
- });
338
- seen.add(m.message_id);
339
- log.debug(`${label} buffered unsummoned group message ${m.message_id} (place ${bufferKey})`);
340
- }
341
- else {
342
- log.debug(`${label} not summoned — ignoring empty message ${m.message_id} (chat ${m.chat_id})`);
343
- }
344
- }
345
- else {
346
- log.debug(`${label} not summoned — ignoring message ${m.message_id} (chat ${m.chat_id}, ${m.chat_type})`);
347
- }
342
+ // Not lost: the turn that is summoned here reads it from the place's history.
343
+ log.debug(`${label} not summoned — message ${m.message_id} (chat ${m.chat_id}, ${m.chat_type})`);
348
344
  return;
349
345
  }
350
- // Memory follows the place (participant model §5): one session per chat, and one per thread.
351
- const routed = r.session;
352
- const session = routed ?? placeKey(kind, m);
353
- // Lineage is recorded ONLY for the default place-derived session.
354
- const parentSession = routed === undefined && m.thread_id !== undefined ? placeKey(kind, { chat_id: m.chat_id }) : undefined;
346
+ // Memory follows the place (participant model §5): one conversation per chat, and one per thread.
347
+ const routed = r.conversation;
348
+ const conversation = routed ?? placeKey(kind, m);
349
+ // Lineage is recorded ONLY for the default place-derived conversation.
350
+ const parentConversation = routed === undefined && m.thread_id !== undefined ? placeKey(kind, { chat_id: m.chat_id }) : undefined;
355
351
  // Read BEFORE this turn records its own participation below, or it is always true.
356
- const roomBufferKey = parentSession !== undefined && !threadParticipants.agentSpokeIn(session)
357
- ? feishuBufferPlaceKey({ chatId: m.chat_id })
352
+ const roomKey = parentConversation !== undefined && !threadParticipants.agentSpokeIn(conversation)
353
+ ? feishuHistoryKey({ chatId: m.chat_id })
358
354
  : undefined;
355
+ // Every place's discussion under the default routing, a DM's too (what a send tool posted there is in no session);
356
+ // a routed session's place is the router's to decide.
357
+ const historyKey = route === undefined ? feishuHistoryKey(normalized.conversation) : undefined;
358
+ // The platform stamps every message; acceptance time stands in only if a payload lacks it, and is still a bound
359
+ // the ask was created before.
360
+ const created = Number(m.create_time);
361
+ const askAt = Number.isFinite(created) && created > 0 ? created : Date.now();
359
362
  const chatId = r.chatId ?? m.chat_id;
360
363
  const sameTarget = chatId === m.chat_id;
361
364
  // Answer where asked (§4): quote in a group so the ask is identifiable among many speakers, stay plain in an
@@ -367,8 +370,10 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
367
370
  // Explicit user stop: a control action, never a turn — it must not queue behind the run it stops.
368
371
  if (isStopText(normalized.content.text.replace(/@\S+/g, " "))) {
369
372
  seen.add(m.message_id);
370
- sideTasks.track(dispatchStop(control, session, label)
371
- .then((feedback) => api.sendText({ chatId, replyTo, replyInThread }, feedback).then(() => undefined))
373
+ sideTasks.track(dispatchStop(agent, recordedSession(conversation), label)
374
+ .then((feedback) => placeApi(historyKey)
375
+ .sendText({ chatId, replyTo, replyInThread }, feedback)
376
+ .then(() => undefined))
372
377
  .catch((error) => log.warn(`${label} stop feedback failed: ${String(error)}`)));
373
378
  return;
374
379
  }
@@ -385,9 +390,11 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
385
390
  runner.submit({
386
391
  id: m.message_id,
387
392
  seq: ++seqCounter, // arrival order; the turn store replays by it
388
- session,
393
+ conversation,
389
394
  baseText,
390
- bufferKey,
395
+ ...(historyKey !== undefined ? { historyKey } : {}),
396
+ group: m.chat_type === "group",
397
+ askAt,
391
398
  chatId,
392
399
  replyTo,
393
400
  queueReplyTo,
@@ -395,14 +402,14 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
395
402
  // A quote is the user explicitly pointing at something that may predate this session (§8 rung 2), so it is
396
403
  // always loaded.
397
404
  parentId: m.parent_id,
398
- ...(parentSession !== undefined ? { parentSession } : {}),
399
- ...(roomBufferKey !== undefined ? { roomBufferKey } : {}),
405
+ ...(parentConversation !== undefined ? { parentConversation } : {}),
406
+ ...(roomKey !== undefined && historyKey !== undefined ? { roomKey } : {}),
400
407
  images,
401
408
  files,
402
409
  }, true);
403
410
  // Answering inside a thread makes the agent a participant of it, which is what lets the NEXT bare message
404
411
  // address it without a mention (§3).
405
- if (heard && replyInThread === true && sameTarget && r.session === undefined) {
412
+ if (heard && replyInThread === true && sameTarget && r.conversation === undefined) {
406
413
  // Both halves in ONE merge, like Slack's: a record that needed an earlier merge to survive could otherwise
407
414
  // say "answered here, heard nobody".
408
415
  threadParticipants.merge(heard.key, { agentSpoke: true, humans: [heard.speaker] });
@@ -508,7 +515,11 @@ export function buildFeishuChannel(profile, opts, factoryName) {
508
515
  const createRuntime = createFeishuRuntimeFactory(profile, opts, factoryName);
509
516
  return (ctx) => {
510
517
  if (!opts.verificationToken) {
511
- throw new Error(`${factoryName} requires a non-empty verificationToken (console → Events & Callbacks)`);
518
+ // The scaffolded file reaches this under `start` with nothing set, so the error names both ways out.
519
+ throw new Error(`${factoryName} receives by webhook and needs a non-empty verificationToken: \`fastagent deploy <host> --run\` ` +
520
+ `or \`fastagent add ${profile.kind} --ingress webhook\` captures ${profile.envPrefix}_VERIFICATION_TOKEN ` +
521
+ `(console → Events & Callbacks has it too), or set ${profile.envPrefix}_INGRESS=websocket to receive by ` +
522
+ `WebSocket instead`);
512
523
  }
513
524
  return createFeishuWebhookRoutes(profile, opts, createRuntime(ctx));
514
525
  };
@@ -0,0 +1,55 @@
1
+ import { type PlaceDiscussion, type PlaceHistory, type PlaceMessage, type PlaceRead } from "../kit/place-history.ts";
2
+ import type { FeishuApi } from "./feishu-api.ts";
3
+ /** A message's position: `create_time` alone is not unique, so the id breaks ties. */
4
+ interface FeishuCursor {
5
+ at: number;
6
+ id: string;
7
+ }
8
+ export type FeishuPlaceRead = PlaceRead<FeishuCursor>;
9
+ export type FeishuDiscussion = PlaceDiscussion<FeishuCursor>;
10
+ export interface FeishuPlaceHistory extends PlaceHistory<FeishuCursor> {
11
+ /** The chat's threads among its newest messages, for a tool (`kit/room-threads.ts`). */
12
+ threads(chatId: string): Promise<string>;
13
+ }
14
+ /** A background resource carried into a turn, with attribution for its prompt manifest. */
15
+ export interface FeishuBufferedRef {
16
+ messageId: string;
17
+ key: string;
18
+ name?: string;
19
+ from: string;
20
+ }
21
+ /** A place: the chat, or a thread within it. The turn record carries it as this key. */
22
+ export declare function feishuHistoryKey(place: {
23
+ chatId: string;
24
+ threadId?: string;
25
+ }): string;
26
+ /** The inverse of {@link feishuHistoryKey}. */
27
+ export declare function feishuPlaceOf(key: string): {
28
+ chatId: string;
29
+ threadId?: string;
30
+ };
31
+ /** Select the most recent background resources of the folded messages, excluding the turn's own primary ones. */
32
+ export declare function collectFoldedAttachments(folded: readonly PlaceMessage[], primary: {
33
+ files: {
34
+ messageId: string;
35
+ key: string;
36
+ }[];
37
+ images: {
38
+ messageId: string;
39
+ key: string;
40
+ }[];
41
+ }): {
42
+ files: FeishuBufferedRef[];
43
+ images: FeishuBufferedRef[];
44
+ skipped: number;
45
+ };
46
+ export declare function createFeishuPlaceHistory(deps: {
47
+ api: Pick<FeishuApi, "listMessages" | "chatMemberNames">;
48
+ /** THIS app's id: a message whose sender is this app is the agent's own. */
49
+ appId: string;
50
+ label: string;
51
+ /** Where each place's cursor and outputs persist. */
52
+ path: string;
53
+ isTurnInput(messageId: string): boolean;
54
+ }): FeishuPlaceHistory;
55
+ export {};