@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
@@ -1,37 +1,42 @@
1
- import { defineTool, z } from "@fastagent-sh/fastagent";
1
+ import { Type } from "@earendil-works/pi-ai";
2
+ import { defineTool } from "@earendil-works/pi-durable";
3
+ import { needs } from "@fastagent-sh/fastagent";
2
4
  import { feishuTransport } from "@fastagent-sh/fastagent/feishu";
3
5
 
4
6
  // Proactive delivery uses the mounted channel's credentials, gateway and token cache. With no
5
- // channel mounted (fire/invoke/tool), it reads FEISHU_APP_ID / FEISHU_APP_SECRET from the environment.
6
-
7
- export default defineTool({
8
- description:
9
- "Send a message to a Feishu chat, OUTSIDE the normal reply path. Call it only for a turn NO " +
10
- "channel is carrying — a scheduled or self-scheduled (wake) turn, whose plain reply goes " +
11
- "nowhere — or to reach a chat OTHER than the one you are answering. In a normal chat turn the " +
12
- "channel streams and delivers your reply itself, so do NOT call this to answer the current " +
13
- "chat: it would post the message twice, outside the conversation thread. `chatId` (oc_…) names " +
14
- "the DESTINATION and must come from your instructions (the asking message, the schedule prompt, " +
15
- "or memory); the [feishu: chat …] context line only identifies the chat you are answering — the " +
16
- "one chat this tool must not target in a chat turn. Pass exactly ONE of `text` (plain) or " +
17
- "`markdown` (rendered as a card: headings, bold, code blocks, links).",
18
- input: z.object({
19
- chatId: z.string().describe("target chat id (oc_…)"),
20
- text: z.string().optional().describe("plain text message to send"),
21
- markdown: z.string().optional().describe("markdown to send as a card"),
7
+ // channel mounted, it signs in with FEISHU_APP_ID / FEISHU_APP_SECRET itself.
8
+ export default needs(["FEISHU_APP_ID", "FEISHU_APP_SECRET"], (secrets) =>
9
+ defineTool({
10
+ name: "feishu_send",
11
+ description:
12
+ "Send a message to a Feishu chat, OUTSIDE the normal reply path. Call it only for a message NO " +
13
+ "channel is carrying (a scheduled or self-scheduled one, whose plain reply goes " +
14
+ "nowhere) or to reach a chat OTHER than the one you are answering. In a normal chat turn the " +
15
+ "channel streams and delivers your reply itself, so do NOT call this to answer the current " +
16
+ "chat: it would post the message twice, outside the conversation thread. `chatId` (oc_…) names " +
17
+ "the DESTINATION and must come from your instructions (the asking message, the schedule prompt, " +
18
+ "or memory); the [feishu: chat …] context line only identifies the chat you are answering, the " +
19
+ "one chat this tool must not target in a chat turn. Pass exactly ONE of `text` (plain) or " +
20
+ "`markdown` (rendered as a card: headings, bold, code blocks, links).",
21
+ parameters: Type.Object({
22
+ chatId: Type.String({ description: "target chat id (oc_…)" }),
23
+ text: Type.Optional(Type.String({ description: "plain text message to send" })),
24
+ markdown: Type.Optional(Type.String({ description: "markdown to send as a card" })),
25
+ }),
26
+ execute: async ({ chatId, text, markdown }) => {
27
+ if ((text === undefined) === (markdown === undefined)) {
28
+ throw new Error("pass exactly one of `text` (plain) or `markdown` (a card)");
29
+ }
30
+ const api = feishuTransport({ appId: secrets.FEISHU_APP_ID, appSecret: secrets.FEISHU_APP_SECRET });
31
+ if (text !== undefined) await api.sendText({ chatId }, text);
32
+ else {
33
+ await api.sendMessage(
34
+ chatId,
35
+ "interactive",
36
+ JSON.stringify({ schema: "2.0", body: { elements: [{ tag: "markdown", content: markdown }] } }),
37
+ );
38
+ }
39
+ return { output: [{ type: "text", text: `sent ${text !== undefined ? "message" : "card"} to chat ${chatId}` }] };
40
+ },
22
41
  }),
23
- async execute({ chatId, text, markdown }, ctx) {
24
- if ((text === undefined) === (markdown === undefined)) {
25
- throw new Error("pass exactly one of `text` (plain) or `markdown` (a card)");
26
- }
27
- const api = feishuTransport(ctx.cwd);
28
- if (text !== undefined) await api.sendText({ chatId }, text);
29
- else {
30
- await api.sendMessage(chatId, "interactive", JSON.stringify({
31
- schema: "2.0",
32
- body: { elements: [{ tag: "markdown", content: markdown }] },
33
- }));
34
- }
35
- return `sent ${text !== undefined ? "message" : "card"} to chat ${chatId}`;
36
- },
37
- });
42
+ );
@@ -0,0 +1,25 @@
1
+ import { Type } from "@earendil-works/pi-ai";
2
+ import { defineTool } from "@earendil-works/pi-durable";
3
+ import { sessionIdOf } from "@fastagent-sh/fastagent";
4
+ import { feishuThreads } from "@fastagent-sh/fastagent/feishu";
5
+
6
+ // Reads the threads (topics) of the Feishu group chat this message was asked in, and no other chat's:
7
+ // the channel names where the message runs, so the tool takes no chat id. It works in a group turn
8
+ // served by `fastagent dev`/`start`.
9
+ export default defineTool({
10
+ name: "feishu_threads",
11
+ description:
12
+ "Read the threads (topics) of the Feishu group chat you are answering in. Without `threadId`, list its recent " +
13
+ "threads, newest first, each with its id; with a `threadId` from that list, read that thread. Use " +
14
+ "it when a question here concerns something discussed in a thread, such as whether a problem raised there was " +
15
+ "resolved: the answer is often what a person wrote in the thread. Only this chat's threads can be read, and " +
16
+ "only in a group chat turn.",
17
+ parameters: Type.Object({
18
+ threadId: Type.Optional(Type.String({ description: "a thread id from the list (omt_…); omit it to list" })),
19
+ }),
20
+ execute: async ({ threadId }, api, context) => {
21
+ const threads = feishuThreads(await sessionIdOf(api, context));
22
+ const text = threadId === undefined ? await threads.list() : await threads.read(threadId);
23
+ return { output: [{ type: "text", text }] };
24
+ },
25
+ });
@@ -1,19 +1,54 @@
1
- /** Feishu/Lark app-level event subscription choice used by onboarding and scaffolding. */
1
+ import { type FeishuCloudKind } from "./cloud.ts";
2
+ /** Feishu/Lark app-level event subscription choice: how a channel receives, and what an app is set up for. */
2
3
  export type FeishuSubscriptionMode = "webhook" | "websocket";
3
- /** Onboarding choice for group visibility. */
4
- export type FeishuGroupBehavior = "context" | "mentions";
4
+ /** The settings {@link feishuIngressFor} reads, one per cloud: what `deploy` must read the way the box will. */
5
+ export declare const FEISHU_INGRESS_SETTINGS: string[];
5
6
  /**
6
- * Configuring THIS app: what every v7 config PATCH needs — registering a webhook Request URL, and adding a scope to
7
- * the app draft during context-aware group setup.
7
+ * How THIS process receives a Feishu/Lark channel (docs/design/channel-environments.md): `<PREFIX>_INGRESS` when the
8
+ * environment names one, else WebSocket under `dev` (no public URL, no reviewed scope) and webhook everywhere else,
9
+ * which is what every deployment runs. The channel file asks at import, and so does every reader of its shape: `deploy`
10
+ * inspects it in a process that is not `dev`, with the same value file loaded, so it sees what the box will serve.
11
+ */
12
+ export declare function feishuIngressFor(kind: FeishuCloudKind, env?: NodeJS.ProcessEnv): FeishuSubscriptionMode;
13
+ /**
14
+ * Configuring THIS app through the v7 config API: registering a webhook Request URL and switching the subscription
15
+ * mode. Requested for a webhook app only. A tenant may review it, and a scope under review holds the app's whole first
16
+ * version in review, so a WebSocket app, which never uses it, would wait on an admin for nothing (measured: the same
17
+ * creation without it came back active).
8
18
  */
9
19
  export declare const FEISHU_APP_CONFIG_SCOPE = "application:application:patch";
10
20
  /** The one event an agent app cannot serve without: an inbound message. */
11
21
  export declare const FEISHU_MESSAGE_RECEIVE_EVENT = "im.message.receive_v1";
22
+ /** A scope the onboarding asks for, plus any BROADER spelling that already satisfies it. */
23
+ export interface FeishuScopeRequest {
24
+ /** What to request when nothing satisfies it. */
25
+ request: string;
26
+ /** EXTRA spellings that also count — supersets. */
27
+ supersets?: string[];
28
+ /** What the agent loses without it, for the warning that names it. */
29
+ withoutIt: string;
30
+ }
31
+ /** Whether `predicate` holds for any spelling that satisfies this request. */
32
+ export declare function scopeSatisfied(entry: FeishuScopeRequest, predicate: (name: string) => boolean): boolean;
33
+ /**
34
+ * What an agent may know about the rooms it is in, on top of the platform's agent template. There is one posture: the
35
+ * agent takes part in its rooms like a colleague (participant-model.md), so every app gets all of it, requested at
36
+ * creation in the one round a tenant may want to approve. What a scope is for is the agent's, not only the channel's:
37
+ * the agent can call the Open API with the app's credentials. A scope belongs here only once something uses it:
38
+ * `im:message.group_msg.include_bot:read` (other bots' group messages) is an event-delivery scope. The channel reads
39
+ * other bots' messages from a chat's history, which does not need it (measured), and drops every non-user event.
40
+ */
41
+ export declare const FEISHU_AGENT_SCOPES: FeishuScopeRequest[];
42
+ /**
43
+ * Every scope an app of this ingress needs: what onboarding requests at creation AND what it checks the app holds, so a
44
+ * scope the app is asked for is one whose absence is reported.
45
+ */
46
+ export declare function feishuAppScopes(ingress: FeishuSubscriptionMode): FeishuScopeRequest[];
12
47
  /**
13
- * What a created app carries on top of the platform's agent template, for EITHER ingress — the `addons` merged onto
14
- * the confirm page.
48
+ * What a created app carries on top of the platform's agent template — the `addons` merged onto the confirm page. A
49
+ * scope that arrives here needs no console visit; one a tenant withholds is reported after creation (`add-feishu.ts`).
15
50
  */
16
- export declare function feishuAppAddons(): {
51
+ export declare function feishuAppAddons(ingress: FeishuSubscriptionMode): {
17
52
  scopes: {
18
53
  tenant: string[];
19
54
  };
@@ -23,20 +58,5 @@ export declare function feishuAppAddons(): {
23
58
  };
24
59
  };
25
60
  };
26
- /** The sensitive tenant scope behind both bare replies in the agent's threads and group context buffering. */
27
- export declare const FEISHU_GROUP_CONTEXT_SCOPE = "im:message.group_msg";
28
- /** Reading a QUOTED message by id, so an ask carries what it replies to (participant-model.md §8). */
29
- export declare const FEISHU_MESSAGE_READ_SCOPE = "im:message:readonly";
30
- /** A scope the onboarding asks for, plus any BROADER spelling that already satisfies it. */
31
- export interface FeishuScopeRequest {
32
- /** What to add to the app draft when nothing satisfies it. */
33
- request: string;
34
- /** EXTRA spellings that also count — supersets. */
35
- supersets?: string[];
36
- }
37
- /** Whether `predicate` holds for any spelling that satisfies this request. */
38
- export declare function scopeSatisfied(entry: FeishuScopeRequest, predicate: (name: string) => boolean): boolean;
39
- /** Reading a quoted message: `im:message` is the read/write superset, so an app holding it can already do so. */
40
- export declare const FEISHU_MESSAGE_READ_REQUEST: FeishuScopeRequest;
41
- /** What `--group-behavior context` REQUESTS in one approval round — not a dependency set. */
42
- export declare const FEISHU_CONTEXT_ONBOARDING_SCOPES: FeishuScopeRequest[];
61
+ /** The console page that requests `scopes` for this app, pre-filled: the platform's own link for a missing scope. */
62
+ export declare function feishuScopeRequestUrl(apiBase: string, appId: string, scopes: readonly string[]): string;
@@ -1,35 +1,86 @@
1
+ import { servedByDev } from "../../serving-command.js";
2
+ import { cloudFor } from "./cloud.js";
3
+ /** The settings {@link feishuIngressFor} reads, one per cloud: what `deploy` must read the way the box will. */
4
+ export const FEISHU_INGRESS_SETTINGS = ["feishu", "lark"].map((kind) => `${cloudFor(kind).envPrefix}_INGRESS`);
1
5
  /**
2
- * Configuring THIS app: what every v7 config PATCH needs — registering a webhook Request URL, and adding a scope to
3
- * the app draft during context-aware group setup.
6
+ * How THIS process receives a Feishu/Lark channel (docs/design/channel-environments.md): `<PREFIX>_INGRESS` when the
7
+ * environment names one, else WebSocket under `dev` (no public URL, no reviewed scope) and webhook everywhere else,
8
+ * which is what every deployment runs. The channel file asks at import, and so does every reader of its shape: `deploy`
9
+ * inspects it in a process that is not `dev`, with the same value file loaded, so it sees what the box will serve.
10
+ */
11
+ export function feishuIngressFor(kind, env = process.env) {
12
+ const name = `${cloudFor(kind).envPrefix}_INGRESS`;
13
+ const raw = env[name]?.trim();
14
+ if (!raw)
15
+ return servedByDev(env) ? "websocket" : "webhook";
16
+ if (raw !== "webhook" && raw !== "websocket") {
17
+ throw new Error(`${name} must be "webhook" or "websocket", got "${raw}"`);
18
+ }
19
+ return raw;
20
+ }
21
+ /**
22
+ * Configuring THIS app through the v7 config API: registering a webhook Request URL and switching the subscription
23
+ * mode. Requested for a webhook app only. A tenant may review it, and a scope under review holds the app's whole first
24
+ * version in review, so a WebSocket app, which never uses it, would wait on an admin for nothing (measured: the same
25
+ * creation without it came back active).
4
26
  */
5
27
  export const FEISHU_APP_CONFIG_SCOPE = "application:application:patch";
6
28
  /** The one event an agent app cannot serve without: an inbound message. */
7
29
  export const FEISHU_MESSAGE_RECEIVE_EVENT = "im.message.receive_v1";
30
+ /** Whether `predicate` holds for any spelling that satisfies this request. */
31
+ export function scopeSatisfied(entry, predicate) {
32
+ return [entry.request, ...(entry.supersets ?? [])].some(predicate);
33
+ }
8
34
  /**
9
- * What a created app carries on top of the platform's agent template, for EITHER ingress — the `addons` merged onto
10
- * the confirm page.
35
+ * What an agent may know about the rooms it is in, on top of the platform's agent template. There is one posture: the
36
+ * agent takes part in its rooms like a colleague (participant-model.md), so every app gets all of it, requested at
37
+ * creation in the one round a tenant may want to approve. What a scope is for is the agent's, not only the channel's:
38
+ * the agent can call the Open API with the app's credentials. A scope belongs here only once something uses it:
39
+ * `im:message.group_msg.include_bot:read` (other bots' group messages) is an event-delivery scope. The channel reads
40
+ * other bots' messages from a chat's history, which does not need it (measured), and drops every non-user event.
11
41
  */
12
- export function feishuAppAddons() {
42
+ export const FEISHU_AGENT_SCOPES = [
43
+ {
44
+ request: "im:message.group_msg", // sensitive: some tenants approve it by hand
45
+ withoutIt: "only @mentions arrive: no bare replies in the agent's threads, no group discussion as context",
46
+ },
47
+ {
48
+ request: "im:message:readonly",
49
+ supersets: ["im:message"], // the read/write superset
50
+ withoutIt: "the discussion before an ask cannot be read (the prompt says so), and a quoted message degrades to a marker",
51
+ },
52
+ {
53
+ request: "im:chat.members:read",
54
+ withoutIt: "people in the discussion the agent reads are shown by open_id, not by name",
55
+ },
56
+ ];
57
+ /** The config scope as an app needs it: webhook only ({@link FEISHU_APP_CONFIG_SCOPE}). */
58
+ const FEISHU_APP_CONFIG_REQUEST = {
59
+ request: FEISHU_APP_CONFIG_SCOPE,
60
+ withoutIt: "the Verification Token is not captured and no Request URL is registered for you (add, dev --tunnel, deploy --run)",
61
+ };
62
+ /**
63
+ * Every scope an app of this ingress needs: what onboarding requests at creation AND what it checks the app holds, so a
64
+ * scope the app is asked for is one whose absence is reported.
65
+ */
66
+ export function feishuAppScopes(ingress) {
67
+ return [...(ingress === "webhook" ? [FEISHU_APP_CONFIG_REQUEST] : []), ...FEISHU_AGENT_SCOPES];
68
+ }
69
+ /**
70
+ * What a created app carries on top of the platform's agent template — the `addons` merged onto the confirm page. A
71
+ * scope that arrives here needs no console visit; one a tenant withholds is reported after creation (`add-feishu.ts`).
72
+ */
73
+ export function feishuAppAddons(ingress) {
13
74
  return {
14
- scopes: { tenant: [FEISHU_APP_CONFIG_SCOPE] },
75
+ scopes: { tenant: feishuAppScopes(ingress).map((entry) => entry.request) },
15
76
  events: { items: { tenant: [FEISHU_MESSAGE_RECEIVE_EVENT] } },
16
77
  };
17
78
  }
18
- /** The sensitive tenant scope behind both bare replies in the agent's threads and group context buffering. */
19
- export const FEISHU_GROUP_CONTEXT_SCOPE = "im:message.group_msg";
20
- /** Reading a QUOTED message by id, so an ask carries what it replies to (participant-model.md §8). */
21
- export const FEISHU_MESSAGE_READ_SCOPE = "im:message:readonly";
22
- /** Whether `predicate` holds for any spelling that satisfies this request. */
23
- export function scopeSatisfied(entry, predicate) {
24
- return [entry.request, ...(entry.supersets ?? [])].some(predicate);
79
+ /** The console page that requests `scopes` for this app, pre-filled: the platform's own link for a missing scope. */
80
+ export function feishuScopeRequestUrl(apiBase, appId, scopes) {
81
+ const url = new URL(`${apiBase}/app/${encodeURIComponent(appId)}/auth`);
82
+ url.searchParams.set("q", scopes.join(","));
83
+ url.searchParams.set("op_from", "openapi");
84
+ url.searchParams.set("token_type", "tenant");
85
+ return url.toString();
25
86
  }
26
- /** Reading a quoted message: `im:message` is the read/write superset, so an app holding it can already do so. */
27
- export const FEISHU_MESSAGE_READ_REQUEST = {
28
- request: FEISHU_MESSAGE_READ_SCOPE,
29
- supersets: ["im:message"],
30
- };
31
- /** What `--group-behavior context` REQUESTS in one approval round — not a dependency set. */
32
- export const FEISHU_CONTEXT_ONBOARDING_SCOPES = [
33
- { request: FEISHU_GROUP_CONTEXT_SCOPE },
34
- FEISHU_MESSAGE_READ_REQUEST,
35
- ];
@@ -1,10 +1,28 @@
1
+ /**
2
+ * The mounted channel and proactive tools share credentials, token cache, gateway and retry policy: one transport per
3
+ * app a process holds, found by the app id both hold.
4
+ */
5
+ import { roomThreads } from "../kit/room-threads.ts";
1
6
  import { type FeishuCloudKind } from "./cloud.ts";
2
7
  import { type FeishuApi } from "./feishu-api.ts";
3
8
  /** The delivery methods a proactive sender needs. */
4
9
  export type FeishuTransport = Pick<FeishuApi, "sendText" | "sendMessage">;
5
- /** A remount replaces the transport; separate clouds can share one agent state root. */
6
- export declare function registerFeishuApi(stateRoot: string, kind: FeishuCloudKind, api: FeishuApi): void;
7
- /** Resolve the Feishu transport for a tool's workspace (`ctx.cwd`). */
8
- export declare function feishuTransport(cwd: string): FeishuTransport;
9
- /** Resolve the Lark transport for a tool's workspace (`ctx.cwd`). */
10
- export declare function larkTransport(cwd: string): FeishuTransport;
10
+ /** The app a transport signs in as. */
11
+ export interface FeishuAppCredentials {
12
+ appId: string;
13
+ appSecret: string;
14
+ }
15
+ /** A remount replaces the transport: the channel's is the authoritative one for its app. */
16
+ export declare function registerFeishuApi(appId: string, kind: FeishuCloudKind, api: FeishuApi): void;
17
+ /** The Feishu transport of an app: its mounted channel's, or the app's own when none is mounted. */
18
+ export declare function feishuTransport(app: FeishuAppCredentials): FeishuTransport;
19
+ /** The Lark transport of an app: its mounted channel's, or the app's own when none is mounted. */
20
+ export declare function larkTransport(app: FeishuAppCredentials): FeishuTransport;
21
+ /**
22
+ * The threads of the group chat the message running in `sessionId` was asked in, and only those
23
+ * (`kit/room-threads.ts`): `list()` its recent threads, `read(threadId)` one of them. Throws outside a Feishu group
24
+ * turn served by this process.
25
+ */
26
+ export declare function feishuThreads(sessionId: string | undefined): ReturnType<typeof roomThreads>;
27
+ /** {@link feishuThreads}, for a Lark group turn. */
28
+ export declare function larkThreads(sessionId: string | undefined): ReturnType<typeof roomThreads>;
@@ -1,38 +1,44 @@
1
- /** The mounted channel and proactive tools share credentials, token cache, gateway and retry policy. */
2
- import { findAgentDir, resolveStateRoot } from "../../paths.js";
1
+ /**
2
+ * The mounted channel and proactive tools share credentials, token cache, gateway and retry policy: one transport per
3
+ * app a process holds, found by the app id both hold.
4
+ */
5
+ import { roomThreads } from "../kit/room-threads.js";
3
6
  import { cloudFor } from "./cloud.js";
4
7
  import { createFeishuApi } from "./feishu-api.js";
5
8
  const byKind = {
6
9
  feishu: new Map(),
7
10
  lark: new Map(),
8
11
  };
9
- /** A remount replaces the transport; separate clouds can share one agent state root. */
10
- export function registerFeishuApi(stateRoot, kind, api) {
11
- byKind[kind].set(stateRoot, api);
12
+ /** A remount replaces the transport: the channel's is the authoritative one for its app. */
13
+ export function registerFeishuApi(appId, kind, api) {
14
+ byKind[kind].set(appId, api);
12
15
  }
13
- function cloudTransport(cwd, kind) {
14
- // Embedded senders may use a bare definition directory or an independent workspace.
15
- const stateRoot = resolveStateRoot(findAgentDir(cwd) ?? cwd);
16
+ function cloudTransport({ appId, appSecret }, kind) {
16
17
  const transports = byKind[kind];
17
- let api = transports.get(stateRoot);
18
+ let api = transports.get(appId);
18
19
  if (!api) {
19
- // fire/invoke/tool do not mount channels, so their credentials come from the agent's environment.
20
- const { apiBase, envPrefix } = cloudFor(kind);
21
- const appId = process.env[`${envPrefix}_APP_ID`];
22
- const appSecret = process.env[`${envPrefix}_APP_SECRET`];
23
- if (!appId || !appSecret) {
24
- throw new Error(`${envPrefix}_APP_ID / ${envPrefix}_APP_SECRET are not set and no ${kind} channel is mounted`);
25
- }
26
- api = createFeishuApi({ kind, baseUrl: apiBase, appId, appSecret });
27
- transports.set(stateRoot, api);
20
+ api = createFeishuApi({ kind, baseUrl: cloudFor(kind).apiBase, appId, appSecret });
21
+ transports.set(appId, api);
28
22
  }
29
23
  return api;
30
24
  }
31
- /** Resolve the Feishu transport for a tool's workspace (`ctx.cwd`). */
32
- export function feishuTransport(cwd) {
33
- return cloudTransport(cwd, "feishu");
25
+ /** The Feishu transport of an app: its mounted channel's, or the app's own when none is mounted. */
26
+ export function feishuTransport(app) {
27
+ return cloudTransport(app, "feishu");
34
28
  }
35
- /** Resolve the Lark transport for a tool's workspace (`ctx.cwd`). */
36
- export function larkTransport(cwd) {
37
- return cloudTransport(cwd, "lark");
29
+ /** The Lark transport of an app: its mounted channel's, or the app's own when none is mounted. */
30
+ export function larkTransport(app) {
31
+ return cloudTransport(app, "lark");
32
+ }
33
+ /**
34
+ * The threads of the group chat the message running in `sessionId` was asked in, and only those
35
+ * (`kit/room-threads.ts`): `list()` its recent threads, `read(threadId)` one of them. Throws outside a Feishu group
36
+ * turn served by this process.
37
+ */
38
+ export function feishuThreads(sessionId) {
39
+ return roomThreads("feishu", sessionId);
40
+ }
41
+ /** {@link feishuThreads}, for a Lark group turn. */
42
+ export function larkThreads(sessionId) {
43
+ return roomThreads("lark", sessionId);
38
44
  }
@@ -1,3 +1,9 @@
1
+ import type { DiscussionSource } from "./place-history.ts";
2
+ /**
3
+ * Char budget for the per-place buffer — bounds the cost of folding it into a prompt; when exceeded the OLDEST
4
+ * un-summoned messages are dropped. Not a time window: a quiet group keeps its sparse-but-relevant lines.
5
+ */
6
+ export declare const BUFFER_MAX_CHARS = 4000;
1
7
  /** Per-message bound INSIDE that budget. */
2
8
  export declare const BUFFER_LINE_MAX_CHARS = 280;
3
9
  /** How many buffered files and images (each, most recent first) a summon pulls in with the folded discussion. */
@@ -10,9 +16,12 @@ export declare function capBufferedRefs<R>(refs: R[]): {
10
16
  kept: R[];
11
17
  skipped: number;
12
18
  };
13
- /** The folded discussion as it reaches the model — the prompt block, or nothing when the buffer is empty. */
19
+ /**
20
+ * The folded discussion as it reaches the model — the prompt block, or nothing when the buffer is empty. "Here", not
21
+ * "group": a direct chat's buffer holds what the agent sent there itself.
22
+ */
14
23
  export declare function discussionBlock(text: string): string;
15
- export interface ContextBuffer<E> {
24
+ export interface ContextBuffer<E> extends DiscussionSource<E> {
16
25
  /** Record an un-summoned message. */
17
26
  push(placeKey: string, entry: E): void;
18
27
  /** Render the fold text and snapshot the consumed entries (see the module header's consume protocol). */
@@ -14,12 +14,12 @@
14
14
  * never flowing in as trusted data.
15
15
  */
16
16
  import { log } from "../../log.js";
17
- import { loadStateFile, saveStateFile } from "./state.js";
17
+ import { loadStateFile, saveStateFile } from "../../state-file.js";
18
18
  /**
19
19
  * Char budget for the per-place buffer — bounds the cost of folding it into a prompt; when exceeded the OLDEST
20
20
  * un-summoned messages are dropped. Not a time window: a quiet group keeps its sparse-but-relevant lines.
21
21
  */
22
- const BUFFER_MAX_CHARS = 4000;
22
+ export const BUFFER_MAX_CHARS = 4000;
23
23
  /** Per-message bound INSIDE that budget. */
24
24
  export const BUFFER_LINE_MAX_CHARS = 280;
25
25
  /** How many buffered files and images (each, most recent first) a summon pulls in with the folded discussion. */
@@ -31,9 +31,12 @@ export const BUFFER_ATTACH_MAX = 3;
31
31
  export function capBufferedRefs(refs) {
32
32
  return { kept: refs.slice(-BUFFER_ATTACH_MAX), skipped: Math.max(0, refs.length - BUFFER_ATTACH_MAX) };
33
33
  }
34
- /** The folded discussion as it reaches the model — the prompt block, or nothing when the buffer is empty. */
34
+ /**
35
+ * The folded discussion as it reaches the model — the prompt block, or nothing when the buffer is empty. "Here", not
36
+ * "group": a direct chat's buffer holds what the agent sent there itself.
37
+ */
35
38
  export function discussionBlock(text) {
36
- return text ? `[recent group discussion:\n${text}\n]\n\n` : "";
39
+ return text ? `[recent discussion here:\n${text}\n]\n\n` : "";
37
40
  }
38
41
  export function createContextBuffer(options) {
39
42
  const { path, label, isEntry, line } = options;
@@ -1,13 +1,29 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import type * as Scope from "effect/Scope";
3
3
  import * as Stream from "effect/Stream";
4
- import type { AgentEvent } from "../../agent.ts";
4
+ import type { TurnEvent } from "./invoke-turn-kit.ts";
5
5
  import type { ChannelFailure } from "./preview-kit.ts";
6
6
  import { PortFailure } from "../../effect-port.ts";
7
+ /** How a turn ended: answered, or the failure to tell the asker about. */
8
+ export type TurnEnd = {
9
+ answered: true;
10
+ } | {
11
+ failure: ChannelFailure;
12
+ };
13
+ /**
14
+ * Whether this event ends the turn, and how: the message was refused, or the turn's message ended. A message the
15
+ * agent stopped answering at its output limit still answers with what was written.
16
+ */
17
+ export declare function turnEnd(event: TurnEvent, messageId: string | undefined): TurnEnd | undefined;
18
+ /**
19
+ * A turn whose stream ended before its message did, after the agent said why (`stream_error`: the message's end could
20
+ * not be recorded, or missed events could not be read back): a failure worth trying again. Undefined when nothing was said.
21
+ */
22
+ export declare function unfinishedTurn(streamError: string | undefined): ChannelFailure | undefined;
7
23
  /** Snapshot/edit renderers share terminal ownership; platform-specific settle policies stay injected. */
8
- export declare function renderReply(events: Stream.Stream<AgentEvent, PortFailure>, opts: {
24
+ export declare function renderReply(events: Stream.Stream<TurnEvent, PortFailure>, opts: {
9
25
  label: string;
10
- onEvent: (event: AgentEvent) => void;
26
+ onEvent: (event: TurnEvent) => void;
11
27
  finish: Effect.Effect<void>;
12
28
  answer: () => string;
13
29
  settle: (text: string) => Effect.Effect<void, PortFailure>;
@@ -6,7 +6,44 @@ import * as Fiber from "effect/Fiber";
6
6
  import * as Queue from "effect/Queue";
7
7
  import * as Stream from "effect/Stream";
8
8
  import { log } from "../../log.js";
9
- import { PortFailure, portJoin } from "../../effect-port.js";
9
+ import { PortFailure, portError, portJoin } from "../../effect-port.js";
10
+ /**
11
+ * Whether this event ends the turn, and how: the message was refused, or the turn's message ended. A message the
12
+ * agent stopped answering at its output limit still answers with what was written.
13
+ */
14
+ export function turnEnd(event, messageId) {
15
+ if (event.type === "rejected") {
16
+ const { code, message, retryable } = event.error;
17
+ return { failure: { details: message, retryable, code } };
18
+ }
19
+ if (event.type !== "done" || event.message.id !== messageId)
20
+ return undefined;
21
+ const { status, error } = event.message;
22
+ switch (status) {
23
+ case "completed":
24
+ case "incomplete":
25
+ return { answered: true };
26
+ case "failed":
27
+ return {
28
+ failure: {
29
+ details: error?.message ?? "the message failed",
30
+ retryable: error?.retryable ?? false,
31
+ code: error?.code ?? "failed",
32
+ },
33
+ };
34
+ case "canceled":
35
+ return { failure: { details: "the message was canceled", retryable: false, code: "canceled" } };
36
+ default:
37
+ return undefined;
38
+ }
39
+ }
40
+ /**
41
+ * A turn whose stream ended before its message did, after the agent said why (`stream_error`: the message's end could
42
+ * not be recorded, or missed events could not be read back): a failure worth trying again. Undefined when nothing was said.
43
+ */
44
+ export function unfinishedTurn(streamError) {
45
+ return streamError === undefined ? undefined : { details: streamError, retryable: true, code: "stream_error" };
46
+ }
10
47
  /** Snapshot/edit renderers share terminal ownership; platform-specific settle policies stay injected. */
11
48
  export function renderReply(events, opts) {
12
49
  return Effect.gen(function* () {
@@ -17,8 +54,19 @@ export function renderReply(events, opts) {
17
54
  if (!finalized)
18
55
  yield* notify({ details: "the turn ended without completing", retryable: false }, "abnormal-turn");
19
56
  }));
57
+ let messageId;
58
+ let streamError;
59
+ const failWith = (failure) => Effect.gen(function* () {
60
+ yield* notify(failure, "agent-failure");
61
+ return yield* Effect.fail(new PortFailure(new Error(`agent failed: ${failure.details} (retryable=${failure.retryable})`)));
62
+ });
20
63
  yield* Stream.runForEachWhile(events, (event) => {
21
- if (event.type !== "completed" && event.type !== "failed") {
64
+ if (event.type === "message")
65
+ messageId = event.message.id;
66
+ if (event.type === "stream_error")
67
+ streamError = event.error.message;
68
+ const end = turnEnd(event, messageId);
69
+ if (end === undefined) {
22
70
  return Effect.try({
23
71
  try: () => {
24
72
  opts.onEvent(event);
@@ -30,7 +78,7 @@ export function renderReply(events, opts) {
30
78
  return Effect.gen(function* () {
31
79
  yield* opts.finish;
32
80
  finalized = true;
33
- if (event.type === "completed") {
81
+ if ("answered" in end) {
34
82
  const answer = opts.answer();
35
83
  yield* Effect.try({
36
84
  try: () => opts.onAnswered?.(answer),
@@ -39,18 +87,20 @@ export function renderReply(events, opts) {
39
87
  yield* opts.settle(answer);
40
88
  }
41
89
  else {
42
- yield* notify({
43
- details: event.details,
44
- retryable: event.retryable,
45
- ...(event.code !== undefined ? { code: event.code } : {}),
46
- }, "agent-failure");
47
- return yield* Effect.fail(new PortFailure(new Error(`agent failed: ${event.details} (retryable=${event.retryable})`)));
90
+ return yield* failWith(end.failure);
48
91
  }
49
92
  return false;
50
93
  }).pipe(Effect.uninterruptible);
51
94
  });
52
- if (!finalized)
53
- yield* Effect.fail(new PortFailure(new Error("stream ended without a terminal event")));
95
+ if (finalized)
96
+ return;
97
+ const unfinished = unfinishedTurn(streamError);
98
+ if (unfinished === undefined) {
99
+ return yield* Effect.fail(new PortFailure(new Error("stream ended without a terminal event")));
100
+ }
101
+ yield* opts.finish;
102
+ finalized = true;
103
+ yield* failWith(unfinished).pipe(Effect.uninterruptible);
54
104
  });
55
105
  }
56
106
  export function previewPump(opts) {
@@ -113,11 +163,20 @@ export function serialWriter() {
113
163
  }
114
164
  }).pipe(Effect.catchTag("Done", () => Effect.void));
115
165
  const fiber = yield* Effect.forkScoped(work);
116
- const finish = Effect.sync(() => {
166
+ const end = Effect.sync(() => {
117
167
  Queue.endUnsafe(queue);
118
- }).pipe(Effect.andThen(Fiber.join(fiber)));
119
- // This finalizer precedes forkScoped's interruption: accepted append/status writes must drain.
120
- yield* Effect.addFinalizer(() => finish.pipe(Effect.orDie));
168
+ });
169
+ let reported = false;
170
+ const finish = end.pipe(Effect.andThen(Fiber.join(fiber)), Effect.onExit((exit) => Effect.sync(() => {
171
+ if (!Exit.hasInterrupts(exit))
172
+ reported = true;
173
+ })));
174
+ // This finalizer precedes forkScoped's interruption: accepted append/status writes must drain. It must not fail:
175
+ // a defect makes the scope's typed failure one too, and a `catchTag` downstream misses it. So a write failure no
176
+ // `finish` reported is said in the log.
177
+ yield* Effect.addFinalizer(() => end.pipe(Effect.andThen(Fiber.await(fiber)), Effect.flatMap((exit) => Exit.isFailure(exit) && !reported && !Cause.hasInterruptsOnly(exit.cause)
178
+ ? Effect.sync(() => log.error(`[delivery] a write failed after its turn stopped: ${String(portError(exit.cause))}`))
179
+ : Effect.void)));
121
180
  return {
122
181
  enqueue: (next) => {
123
182
  if (!Queue.offerUnsafe(queue, next))