@fastagent-sh/fastagent 0.21.0 → 0.22.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 (388) hide show
  1. package/README.md +9 -12
  2. package/dist/agent.d.ts +24 -37
  3. package/dist/agent.js +7 -15
  4. package/dist/atomic-write.d.ts +13 -17
  5. package/dist/atomic-write.js +18 -29
  6. package/dist/bind.d.ts +12 -22
  7. package/dist/bind.js +18 -27
  8. package/dist/channel.d.ts +11 -33
  9. package/dist/channels/agentcore-limits.d.ts +2 -13
  10. package/dist/channels/agentcore-limits.js +2 -13
  11. package/dist/channels/agentcore-protocol.d.ts +67 -58
  12. package/dist/channels/agentcore-protocol.js +23 -15
  13. package/dist/channels/agentcore-service.d.ts +19 -33
  14. package/dist/channels/agentcore-service.js +176 -59
  15. package/dist/channels/agentcore.d.ts +39 -33
  16. package/dist/channels/agentcore.js +90 -224
  17. package/dist/channels/body.d.ts +15 -2
  18. package/dist/channels/body.js +24 -2
  19. package/dist/channels/busy.d.ts +1 -21
  20. package/dist/channels/busy.js +2 -39
  21. package/dist/channels/control.d.ts +17 -62
  22. package/dist/channels/control.js +74 -182
  23. package/dist/channels/define-channel.d.ts +39 -0
  24. package/dist/channels/define-channel.js +9 -0
  25. package/dist/channels/discover.d.ts +10 -31
  26. package/dist/channels/discover.js +37 -31
  27. package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
  28. package/dist/channels/feishu/bootstrap-token.js +7 -23
  29. package/dist/channels/feishu/card.d.ts +14 -28
  30. package/dist/channels/feishu/card.js +16 -30
  31. package/dist/channels/feishu/cloud.d.ts +1 -5
  32. package/dist/channels/feishu/context-buffer.d.ts +4 -23
  33. package/dist/channels/feishu/context-buffer.js +8 -31
  34. package/dist/channels/feishu/crypto.d.ts +10 -5
  35. package/dist/channels/feishu/crypto.js +12 -19
  36. package/dist/channels/feishu/feishu-api.d.ts +2 -1
  37. package/dist/channels/feishu/feishu-api.js +5 -4
  38. package/dist/channels/feishu/feishu.d.ts +4 -10
  39. package/dist/channels/feishu/feishu.js +113 -227
  40. package/dist/channels/feishu/invoke-turn.d.ts +5 -4
  41. package/dist/channels/feishu/invoke-turn.js +36 -54
  42. package/dist/channels/feishu/model.d.ts +4 -21
  43. package/dist/channels/feishu/model.js +1 -8
  44. package/dist/channels/feishu/normalize.d.ts +2 -10
  45. package/dist/channels/feishu/normalize.js +7 -42
  46. package/dist/channels/feishu/parse.d.ts +9 -29
  47. package/dist/channels/feishu/parse.js +8 -26
  48. package/dist/channels/feishu/preview.d.ts +12 -14
  49. package/dist/channels/feishu/preview.js +108 -207
  50. package/dist/channels/feishu/register-app.d.ts +8 -23
  51. package/dist/channels/feishu/register-app.js +7 -20
  52. package/dist/channels/feishu/register-webhook.d.ts +2 -9
  53. package/dist/channels/feishu/register-webhook.js +9 -33
  54. package/dist/channels/feishu/scaffold/channel.ts +27 -19
  55. package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
  56. package/dist/channels/feishu/setup-mode.d.ts +11 -36
  57. package/dist/channels/feishu/setup-mode.js +7 -26
  58. package/dist/channels/feishu/ws-ingress.d.ts +1 -2
  59. package/dist/channels/feishu/ws-ingress.js +6 -8
  60. package/dist/channels/http.d.ts +15 -15
  61. package/dist/channels/http.js +19 -9
  62. package/dist/channels/kit/attachment-path.d.ts +4 -5
  63. package/dist/channels/kit/attachment-path.js +10 -34
  64. package/dist/channels/kit/context-buffer.d.ts +14 -21
  65. package/dist/channels/kit/context-buffer.js +25 -38
  66. package/dist/channels/kit/delivery.d.ts +35 -0
  67. package/dist/channels/kit/delivery.js +129 -0
  68. package/dist/channels/kit/event-stream.d.ts +5 -0
  69. package/dist/channels/kit/event-stream.js +29 -0
  70. package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
  71. package/dist/channels/kit/invoke-turn-kit.js +89 -32
  72. package/dist/channels/kit/preview-kit.d.ts +18 -72
  73. package/dist/channels/kit/preview-kit.js +22 -88
  74. package/dist/channels/kit/signature.d.ts +3 -9
  75. package/dist/channels/kit/signature.js +3 -9
  76. package/dist/channels/kit/state.d.ts +1 -2
  77. package/dist/channels/kit/state.js +5 -14
  78. package/dist/channels/kit/stop-command.d.ts +1 -2
  79. package/dist/channels/kit/stop-command.js +6 -9
  80. package/dist/channels/kit/tasks.d.ts +1 -1
  81. package/dist/channels/kit/tasks.js +8 -21
  82. package/dist/channels/kit/text.d.ts +2 -16
  83. package/dist/channels/kit/text.js +23 -31
  84. package/dist/channels/kit/thread-participants.d.ts +4 -17
  85. package/dist/channels/kit/thread-participants.js +8 -55
  86. package/dist/channels/kit/transport.d.ts +26 -0
  87. package/dist/channels/kit/transport.js +20 -0
  88. package/dist/channels/kit/turn-queue.d.ts +7 -11
  89. package/dist/channels/kit/turn-queue.js +21 -39
  90. package/dist/channels/kit/turn-runner.d.ts +32 -18
  91. package/dist/channels/kit/turn-runner.js +98 -53
  92. package/dist/channels/kit/turn-store.d.ts +32 -32
  93. package/dist/channels/kit/turn-store.js +45 -88
  94. package/dist/channels/lark/lark.d.ts +1 -5
  95. package/dist/channels/lark/onboard.d.ts +3 -12
  96. package/dist/channels/lark/onboard.js +3 -13
  97. package/dist/channels/lark/scaffold/channel.ts +27 -19
  98. package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
  99. package/dist/channels/registration.d.ts +9 -35
  100. package/dist/channels/registration.js +6 -37
  101. package/dist/channels/secret.js +2 -5
  102. package/dist/channels/serve.d.ts +47 -44
  103. package/dist/channels/serve.js +165 -101
  104. package/dist/channels/slack/config-api.d.ts +4 -27
  105. package/dist/channels/slack/config-api.js +5 -34
  106. package/dist/channels/slack/context-buffer.d.ts +2 -3
  107. package/dist/channels/slack/context-buffer.js +5 -5
  108. package/dist/channels/slack/invoke-turn.d.ts +3 -1
  109. package/dist/channels/slack/invoke-turn.js +25 -34
  110. package/dist/channels/slack/manifest.js +1 -3
  111. package/dist/channels/slack/model.d.ts +1 -2
  112. package/dist/channels/slack/model.js +1 -1
  113. package/dist/channels/slack/onboard.d.ts +4 -3
  114. package/dist/channels/slack/onboard.js +8 -11
  115. package/dist/channels/slack/onboarding-state.d.ts +3 -5
  116. package/dist/channels/slack/onboarding-state.js +9 -6
  117. package/dist/channels/slack/parse.d.ts +4 -13
  118. package/dist/channels/slack/parse.js +7 -28
  119. package/dist/channels/slack/preview.d.ts +12 -2
  120. package/dist/channels/slack/preview.js +292 -269
  121. package/dist/channels/slack/reaction.d.ts +11 -3
  122. package/dist/channels/slack/reaction.js +19 -8
  123. package/dist/channels/slack/register-webhook.js +4 -7
  124. package/dist/channels/slack/scaffold/channel.ts +20 -13
  125. package/dist/channels/slack/scaffold/slack-send.ts +2 -2
  126. package/dist/channels/slack/setup-server.d.ts +1 -5
  127. package/dist/channels/slack/setup-server.js +1 -5
  128. package/dist/channels/slack/shared-api.d.ts +1 -4
  129. package/dist/channels/slack/shared-api.js +2 -13
  130. package/dist/channels/slack/slack-api.d.ts +2 -1
  131. package/dist/channels/slack/slack-api.js +6 -4
  132. package/dist/channels/slack/slack.d.ts +11 -14
  133. package/dist/channels/slack/slack.js +121 -122
  134. package/dist/channels/sse.d.ts +1 -1
  135. package/dist/channels/sse.js +1 -1
  136. package/dist/channels/telegram/context-buffer.d.ts +7 -22
  137. package/dist/channels/telegram/context-buffer.js +6 -23
  138. package/dist/channels/telegram/invoke-turn.d.ts +7 -7
  139. package/dist/channels/telegram/invoke-turn.js +24 -43
  140. package/dist/channels/telegram/parse.d.ts +24 -51
  141. package/dist/channels/telegram/parse.js +32 -84
  142. package/dist/channels/telegram/preview.d.ts +10 -1
  143. package/dist/channels/telegram/preview.js +79 -100
  144. package/dist/channels/telegram/register-webhook.js +5 -23
  145. package/dist/channels/telegram/scaffold/channel.ts +25 -18
  146. package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
  147. package/dist/channels/telegram/telegram-api.d.ts +4 -3
  148. package/dist/channels/telegram/telegram-api.js +9 -7
  149. package/dist/channels/telegram/telegram.d.ts +4 -18
  150. package/dist/channels/telegram/telegram.js +38 -94
  151. package/dist/channels/telegram/turn-store.d.ts +11 -14
  152. package/dist/channels/telegram/turn-store.js +8 -6
  153. package/dist/channels/wait-health.d.ts +4 -6
  154. package/dist/channels/wait-health.js +10 -14
  155. package/dist/cli/add-feishu.d.ts +2 -12
  156. package/dist/cli/add-feishu.js +24 -71
  157. package/dist/cli/add-slack.d.ts +5 -3
  158. package/dist/cli/add-slack.js +53 -76
  159. package/dist/cli/auth-view.d.ts +2 -16
  160. package/dist/cli/auth-view.js +2 -16
  161. package/dist/cli/commands/add.js +25 -43
  162. package/dist/cli/commands/chat.d.ts +0 -1
  163. package/dist/cli/commands/chat.js +5 -6
  164. package/dist/cli/commands/deploy/agentcore.js +60 -65
  165. package/dist/cli/commands/deploy/docker.js +50 -23
  166. package/dist/cli/commands/deploy/fly.js +23 -61
  167. package/dist/cli/commands/deploy/railway.js +23 -18
  168. package/dist/cli/commands/deploy/shared.d.ts +38 -57
  169. package/dist/cli/commands/deploy/shared.js +40 -59
  170. package/dist/cli/commands/deploy.d.ts +10 -27
  171. package/dist/cli/commands/deploy.js +38 -60
  172. package/dist/cli/commands/destroy.d.ts +4 -0
  173. package/dist/cli/commands/destroy.js +56 -0
  174. package/dist/cli/commands/dev.d.ts +2 -1
  175. package/dist/cli/commands/dev.js +20 -23
  176. package/dist/cli/commands/info.d.ts +0 -2
  177. package/dist/cli/commands/info.js +83 -49
  178. package/dist/cli/commands/init.d.ts +1 -2
  179. package/dist/cli/commands/init.js +16 -55
  180. package/dist/cli/commands/invoke.d.ts +0 -1
  181. package/dist/cli/commands/invoke.js +5 -7
  182. package/dist/cli/commands/login.d.ts +2 -1
  183. package/dist/cli/commands/login.js +33 -38
  184. package/dist/cli/commands/logs.js +3 -4
  185. package/dist/cli/commands/models.js +1 -5
  186. package/dist/cli/commands/routine-run.d.ts +6 -0
  187. package/dist/cli/commands/routine-run.js +47 -0
  188. package/dist/cli/commands/routine.d.ts +25 -0
  189. package/dist/cli/commands/routine.js +122 -0
  190. package/dist/cli/commands/start.d.ts +31 -3
  191. package/dist/cli/commands/start.js +131 -81
  192. package/dist/cli/commands/tool.js +26 -12
  193. package/dist/cli/fail.d.ts +13 -23
  194. package/dist/cli/fail.js +20 -23
  195. package/dist/cli/invoke-stream.d.ts +1 -8
  196. package/dist/cli/kernel.d.ts +6 -38
  197. package/dist/cli/kernel.js +14 -77
  198. package/dist/cli/models-view.d.ts +1 -9
  199. package/dist/cli/models-view.js +11 -22
  200. package/dist/cli/program.d.ts +5 -6
  201. package/dist/cli/program.js +120 -157
  202. package/dist/cli/serve.d.ts +57 -41
  203. package/dist/cli/serve.js +94 -121
  204. package/dist/cli/shared.d.ts +15 -61
  205. package/dist/cli/shared.js +55 -181
  206. package/dist/cli.js +1 -8
  207. package/dist/collect.d.ts +6 -0
  208. package/dist/collect.js +6 -0
  209. package/dist/core.d.ts +4 -1
  210. package/dist/core.js +9 -7
  211. package/dist/declared-secrets.d.ts +55 -0
  212. package/dist/declared-secrets.js +88 -0
  213. package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
  214. package/dist/deploy/agentcore/aws-cli.js +64 -0
  215. package/dist/deploy/agentcore/destroy.d.ts +51 -0
  216. package/dist/deploy/agentcore/destroy.js +273 -0
  217. package/dist/deploy/agentcore/forwarder.js +28 -79
  218. package/dist/deploy/agentcore/logs.d.ts +2 -18
  219. package/dist/deploy/agentcore/logs.js +27 -51
  220. package/dist/deploy/agentcore/plan.d.ts +85 -105
  221. package/dist/deploy/agentcore/plan.js +152 -314
  222. package/dist/deploy/agentcore/run.d.ts +28 -28
  223. package/dist/deploy/agentcore/run.js +227 -306
  224. package/dist/deploy/agentcore/zip.d.ts +2 -15
  225. package/dist/deploy/agentcore/zip.js +3 -17
  226. package/dist/deploy/build-context.d.ts +21 -0
  227. package/dist/deploy/build-context.js +137 -0
  228. package/dist/deploy/channel-ingress.d.ts +26 -39
  229. package/dist/deploy/channel-ingress.js +57 -28
  230. package/dist/deploy/container.d.ts +20 -34
  231. package/dist/deploy/container.js +49 -100
  232. package/dist/deploy/docker/plan.d.ts +12 -11
  233. package/dist/deploy/docker/plan.js +43 -24
  234. package/dist/deploy/docker/run.d.ts +22 -25
  235. package/dist/deploy/docker/run.js +39 -37
  236. package/dist/deploy/fly/plan.d.ts +15 -44
  237. package/dist/deploy/fly/plan.js +41 -73
  238. package/dist/deploy/fly/run.d.ts +16 -51
  239. package/dist/deploy/fly/run.js +42 -95
  240. package/dist/deploy/hosts.d.ts +24 -3
  241. package/dist/deploy/hosts.js +24 -3
  242. package/dist/deploy/preflight.d.ts +47 -41
  243. package/dist/deploy/preflight.js +282 -383
  244. package/dist/deploy/railway/plan.d.ts +16 -42
  245. package/dist/deploy/railway/plan.js +42 -60
  246. package/dist/deploy/railway/run.d.ts +30 -68
  247. package/dist/deploy/railway/run.js +86 -100
  248. package/dist/deploy/registration-gate.d.ts +6 -13
  249. package/dist/deploy/residency.d.ts +37 -0
  250. package/dist/deploy/residency.js +24 -0
  251. package/dist/deploy/runner.d.ts +10 -9
  252. package/dist/deploy/runner.js +7 -18
  253. package/dist/deploy/secrets.d.ts +40 -41
  254. package/dist/deploy/secrets.js +117 -103
  255. package/dist/deploy/workspace.d.ts +50 -0
  256. package/dist/deploy/workspace.js +197 -0
  257. package/dist/dev-supervisor.d.ts +1 -9
  258. package/dist/dev-supervisor.js +21 -64
  259. package/dist/effect-port.d.ts +50 -0
  260. package/dist/effect-port.js +77 -0
  261. package/dist/engines/pi/agent-session-factory.d.ts +50 -69
  262. package/dist/engines/pi/agent-session-factory.js +249 -146
  263. package/dist/engines/pi/auth.d.ts +15 -11
  264. package/dist/engines/pi/auth.js +96 -99
  265. package/dist/engines/pi/chat.d.ts +1 -5
  266. package/dist/engines/pi/chat.js +2 -14
  267. package/dist/engines/pi/config.d.ts +53 -51
  268. package/dist/engines/pi/config.js +83 -92
  269. package/dist/engines/pi/create.d.ts +60 -75
  270. package/dist/engines/pi/create.js +108 -141
  271. package/dist/engines/pi/definition.d.ts +14 -58
  272. package/dist/engines/pi/definition.js +21 -85
  273. package/dist/engines/pi/invoke-session.d.ts +5 -25
  274. package/dist/engines/pi/invoke-session.js +209 -244
  275. package/dist/engines/pi/login.d.ts +95 -35
  276. package/dist/engines/pi/login.js +259 -107
  277. package/dist/engines/pi/machine.d.ts +31 -0
  278. package/dist/engines/pi/machine.js +106 -0
  279. package/dist/engines/pi/models.d.ts +70 -89
  280. package/dist/engines/pi/models.js +188 -81
  281. package/dist/engines/pi/open.d.ts +65 -53
  282. package/dist/engines/pi/open.js +129 -69
  283. package/dist/engines/pi/report.d.ts +3 -12
  284. package/dist/engines/pi/report.js +2 -16
  285. package/dist/engines/pi/search-tools.d.ts +1 -9
  286. package/dist/engines/pi/search-tools.js +26 -57
  287. package/dist/engines/pi/service.d.ts +2 -16
  288. package/dist/engines/pi/service.js +2 -16
  289. package/dist/engines/pi/session-builder.d.ts +5 -9
  290. package/dist/engines/pi/session-builder.js +44 -82
  291. package/dist/engines/pi/session-control.js +286 -269
  292. package/dist/engines/pi/session-effects.d.ts +14 -0
  293. package/dist/engines/pi/session-effects.js +30 -0
  294. package/dist/engines/pi/session-inheritance.d.ts +9 -18
  295. package/dist/engines/pi/session-inheritance.js +103 -160
  296. package/dist/engines/pi/session-markers.d.ts +31 -30
  297. package/dist/engines/pi/session-markers.js +26 -25
  298. package/dist/engines/pi/session-settings.d.ts +6 -22
  299. package/dist/engines/pi/session-settings.js +10 -21
  300. package/dist/engines/pi/session-store.d.ts +26 -92
  301. package/dist/engines/pi/session-store.js +96 -310
  302. package/dist/engines/pi/tool-context.d.ts +17 -45
  303. package/dist/engines/pi/tool-context.js +7 -26
  304. package/dist/engines/pi/tool.d.ts +45 -36
  305. package/dist/engines/pi/tool.js +43 -57
  306. package/dist/engines/pi/turn-kit.d.ts +14 -52
  307. package/dist/engines/pi/turn-kit.js +18 -67
  308. package/dist/engines/pi/wake-tool.d.ts +2 -10
  309. package/dist/engines/pi/wake-tool.js +7 -27
  310. package/dist/env.d.ts +27 -28
  311. package/dist/env.js +45 -50
  312. package/dist/index.js +0 -2
  313. package/dist/lark.d.ts +1 -2
  314. package/dist/lark.js +1 -2
  315. package/dist/loader.d.ts +14 -55
  316. package/dist/loader.js +24 -43
  317. package/dist/log.d.ts +2 -12
  318. package/dist/log.js +3 -24
  319. package/dist/node.d.ts +2 -15
  320. package/dist/node.js +2 -18
  321. package/dist/observe.d.ts +2 -8
  322. package/dist/open-url.d.ts +1 -1
  323. package/dist/open-url.js +1 -1
  324. package/dist/paths.d.ts +70 -134
  325. package/dist/paths.js +165 -232
  326. package/dist/pi.d.ts +4 -6
  327. package/dist/pi.js +3 -3
  328. package/dist/proxy.d.ts +16 -6
  329. package/dist/proxy.js +52 -16
  330. package/dist/runtime.d.ts +18 -12
  331. package/dist/runtime.js +18 -11
  332. package/dist/scaffold/add-channel.d.ts +14 -31
  333. package/dist/scaffold/add-channel.js +48 -112
  334. package/dist/scaffold/init.d.ts +9 -31
  335. package/dist/scaffold/init.js +45 -138
  336. package/dist/scaffold/templates/env.example +6 -4
  337. package/dist/scaffold/templates/fastagent.config.ts +36 -0
  338. package/dist/scaffold/templates.d.ts +3 -3
  339. package/dist/scaffold/templates.js +5 -8
  340. package/dist/scaffold/vendor-skill.d.ts +2 -5
  341. package/dist/scaffold/vendor-skill.js +19 -29
  342. package/dist/schedule/cron.d.ts +9 -5
  343. package/dist/schedule/cron.js +9 -11
  344. package/dist/schedule/discover.d.ts +12 -12
  345. package/dist/schedule/discover.js +43 -34
  346. package/dist/schedule/routine.d.ts +43 -0
  347. package/dist/schedule/routine.js +32 -0
  348. package/dist/schedule/run.d.ts +55 -0
  349. package/dist/schedule/run.js +146 -0
  350. package/dist/schedule/scheduler.d.ts +44 -55
  351. package/dist/schedule/scheduler.js +291 -265
  352. package/dist/schedule/state.d.ts +105 -10
  353. package/dist/schedule/state.js +293 -19
  354. package/dist/schedule/wake-alarm.d.ts +10 -23
  355. package/dist/schedule/wake-alarm.js +91 -121
  356. package/dist/schedule/wakeups.d.ts +23 -39
  357. package/dist/schedule/wakeups.js +33 -75
  358. package/dist/secrets-gate.d.ts +32 -0
  359. package/dist/secrets-gate.js +33 -0
  360. package/dist/service.d.ts +137 -98
  361. package/dist/service.js +250 -252
  362. package/dist/session-remote.d.ts +33 -44
  363. package/dist/session-remote.js +203 -192
  364. package/dist/session.d.ts +153 -228
  365. package/dist/session.js +33 -55
  366. package/dist/tunnel.d.ts +8 -36
  367. package/dist/tunnel.js +24 -65
  368. package/dist/version.d.ts +2 -2
  369. package/dist/version.js +2 -2
  370. package/package.json +10 -16
  371. package/dist/channels/agentcore-state.d.ts +0 -77
  372. package/dist/channels/agentcore-state.js +0 -276
  373. package/dist/channels/github/github.d.ts +0 -31
  374. package/dist/channels/github/github.js +0 -89
  375. package/dist/channels/github/scaffold/channel.ts +0 -25
  376. package/dist/cli/commands/attach.d.ts +0 -108
  377. package/dist/cli/commands/attach.js +0 -637
  378. package/dist/cli/commands/fire.d.ts +0 -7
  379. package/dist/cli/commands/fire.js +0 -38
  380. package/dist/cli/commands/schedule.d.ts +0 -12
  381. package/dist/cli/commands/schedule.js +0 -87
  382. package/dist/github.d.ts +0 -3
  383. package/dist/github.js +0 -2
  384. package/dist/scaffold/templates/fastagent.config.mjs +0 -23
  385. package/dist/schedule/audit.d.ts +0 -18
  386. package/dist/schedule/audit.js +0 -57
  387. package/dist/schedule/schedule.d.ts +0 -35
  388. package/dist/schedule/schedule.js +0 -23
@@ -1,12 +1,7 @@
1
- /**
2
- * Per-turn capabilities shared by every FastAgent-defined tool. A tool is built once and reused across
3
- * turns, so current cwd/session/activation bindings ride AsyncLocalStorage rather than definition
4
- * closures. Deploy-time ambients a tool closes over at build time do NOT belong here.
5
- */
1
+ /** Per-turn capabilities shared by every FastAgent-defined tool. */
6
2
  import { AsyncLocalStorage } from "node:async_hooks";
7
3
  import type { SessionEntry as PiSessionEntry, AgentSession } from "@earendil-works/pi-coding-agent";
8
- /** FastAgent's read-only port over the current conversation manager. Serving and chat adapt their
9
- * different concrete session implementations to this one tool-runtime contract. */
4
+ /** FastAgent's read-only port over the current conversation manager. */
10
5
  export interface ReadonlySessionManager {
11
6
  getSessionId(): string;
12
7
  getHeader(): Promise<{
@@ -15,22 +10,16 @@ export interface ReadonlySessionManager {
15
10
  }>;
16
11
  getBranch(): Promise<PiSessionEntry[]>;
17
12
  }
18
- /**
19
- * pi's AgentSession as the port above — the SAME adapter for both of its consumers: chat's resident
20
- * session (session-builder.ts) and serving's per-invoke one (agent-session-factory.ts).
21
- *
22
- * `sessionId` is the CALLER's, not pi's. A tool correlates its own state by the id the channel
23
- * minted; pi's is that id encoded into a filename-safe record name, and leaking the encoding here
24
- * would hand a telegram tool `s-1001234567890` for a room it knows as `-1001234567890`.
25
- */
13
+ /** pi's AgentSession as the port above — the SAME adapter for both of its consumers. */
26
14
  export declare function agentSessionManager(session: AgentSession, sessionId: string): ReadonlySessionManager;
27
15
  /**
28
- * The turn's tool-activation bridge — narrow closures over the CURRENT session (bound per turn), so
29
- * a loader tool can activate deferred tools mid-turn without tool.ts importing the engine. pi records
30
- * the change in the session (`active_tools_change`) and the per-invoke restore
31
- * (agent-session-factory.ts) carries it into later turns; defineTool's wrapper stamps the newly-activated names on
32
- * the tool result (`addedToolNames`) — the load point native deferred-loading providers preserve the
33
- * prompt-cache prefix with.
16
+ * The turn's tool-activation bridge — narrow closures over the CURRENT session (bound per turn), so a loader tool can
17
+ * activate deferred tools mid-turn without tool.ts importing the engine. pi anchors the addition in the transcript
18
+ * (a system message carrying `toolsAdded`, written after the batch of tool results the activating call belongs to),
19
+ * so providers with native deferred loading keep their
20
+ * prompt-cache prefix; that message describes the run that wrote it, so what carries an activation into LATER turns
21
+ * is fastagent's own `fastagent:tool-activation` entry, replayed by the per-invoke restore
22
+ * (agent-session-factory.ts).
34
23
  */
35
24
  export interface ToolActivation {
36
25
  /** Names of the currently ACTIVE tools. */
@@ -40,36 +29,19 @@ export interface ToolActivation {
40
29
  name: string;
41
30
  description: string;
42
31
  }>;
43
- /** ADDITIVE activation. Unknown names are filtered out before reaching pi (whose `setActiveTools`
44
- * THROWS on them); answers the names actually newly activated (already-active names don't repeat).
45
- *
46
- * SYNCHRONOUS, and that is the contract, not an implementation detail: read-modify-write against
47
- * pi's active set cannot be interleaved as long as no caller can await inside it. An async
48
- * signature would need a lock to say the same thing, and the lock is what a previous version had
49
- * — one rebuilt per tool call, so the parallel batch it existed for never met on it. If pi's
50
- * setters ever become async, this signature is where that breaks, loudly. */
32
+ /**
33
+ * ADDITIVE activation. Returns ONLY the names this call actually added — a name a batch sibling activated first
34
+ * is not in it. That return value is the caller's truth for both what to report and what to charge an activation
35
+ * cap: the call is atomic, but an `active()` -> await -> `activate()` sequence around it is not.
36
+ */
51
37
  activate(names: string[]): string[];
52
38
  }
53
- /**
54
- * The activation bridge over a live pi session — the ONE implementation, for both consumers.
55
- *
56
- * Serving (`agent-session-factory.ts`) and chat (`session-builder.ts`) had a copy each, identical
57
- * but for the persistence line; the neighbouring `definitionResourceLoaderOptions` exists because
58
- * that exact duplication drifted once before. The difference is a PARAMETER now: `onActivated` is
59
- * what a served session uses to record the delta that carries the discovery into its next turn,
60
- * and chat has nowhere to put one (pi's SessionContext has no active-tool set).
61
- *
62
- * Bind it to the SESSION, never to a tool call: the next call has to see what this one activated.
63
- */
39
+ /** The activation bridge over a live pi session — the ONE implementation, for both consumers. */
64
40
  export declare function sessionToolActivation(session: AgentSession, onActivated?: (added: string[]) => void): ToolActivation;
65
41
  export interface TurnContext {
66
- /** Working directory for this execution. Falls back to process.cwd() only for an unbound direct call. */
42
+ /** Working directory for this execution. */
67
43
  cwd?: string;
68
- /** Current conversation manager. Absent outside a FastAgent-managed agent turn. */
69
44
  sessionManager?: ReadonlySessionManager;
70
- /** Tool activation for the current turn. Two producers, one consumer surface: agent-session-factory.ts
71
- * bridges the served session; session-builder.ts bridges the resident one (chat emulates deferral —
72
- * same loader, same semantics). Absent only outside any turn (a bare `fastagent tool` run). */
73
45
  tools?: ToolActivation;
74
46
  }
75
47
  export declare const turnContext: AsyncLocalStorage<TurnContext>;
@@ -1,17 +1,6 @@
1
- /**
2
- * Per-turn capabilities shared by every FastAgent-defined tool. A tool is built once and reused across
3
- * turns, so current cwd/session/activation bindings ride AsyncLocalStorage rather than definition
4
- * closures. Deploy-time ambients a tool closes over at build time do NOT belong here.
5
- */
1
+ /** Per-turn capabilities shared by every FastAgent-defined tool. */
6
2
  import { AsyncLocalStorage } from "node:async_hooks";
7
- /**
8
- * pi's AgentSession as the port above — the SAME adapter for both of its consumers: chat's resident
9
- * session (session-builder.ts) and serving's per-invoke one (agent-session-factory.ts).
10
- *
11
- * `sessionId` is the CALLER's, not pi's. A tool correlates its own state by the id the channel
12
- * minted; pi's is that id encoded into a filename-safe record name, and leaking the encoding here
13
- * would hand a telegram tool `s-1001234567890` for a room it knows as `-1001234567890`.
14
- */
3
+ /** pi's AgentSession as the port above — the SAME adapter for both of its consumers. */
15
4
  export function agentSessionManager(session, sessionId) {
16
5
  return {
17
6
  getSessionId: () => sessionId,
@@ -26,17 +15,7 @@ export function agentSessionManager(session, sessionId) {
26
15
  },
27
16
  };
28
17
  }
29
- /**
30
- * The activation bridge over a live pi session — the ONE implementation, for both consumers.
31
- *
32
- * Serving (`agent-session-factory.ts`) and chat (`session-builder.ts`) had a copy each, identical
33
- * but for the persistence line; the neighbouring `definitionResourceLoaderOptions` exists because
34
- * that exact duplication drifted once before. The difference is a PARAMETER now: `onActivated` is
35
- * what a served session uses to record the delta that carries the discovery into its next turn,
36
- * and chat has nowhere to put one (pi's SessionContext has no active-tool set).
37
- *
38
- * Bind it to the SESSION, never to a tool call: the next call has to see what this one activated.
39
- */
18
+ /** The activation bridge over a live pi session — the ONE implementation, for both consumers. */
40
19
  export function sessionToolActivation(session, onActivated) {
41
20
  return {
42
21
  active: () => session.getActiveToolNames(),
@@ -53,8 +32,10 @@ export function sessionToolActivation(session, onActivated) {
53
32
  };
54
33
  }
55
34
  export const turnContext = new AsyncLocalStorage();
56
- /** dedupe → keep registered names only (pi's setters THROW on unknown) → exclude already-active →
57
- * the names to actually add (empty = nothing to set). */
35
+ /**
36
+ * dedupe → keep registered names only (pi's setters THROW on unknown) → exclude already-active → the names to actually
37
+ * add (empty = nothing to set).
38
+ */
58
39
  function additiveActivation(registered, current, names) {
59
40
  const known = new Set(registered);
60
41
  const active = new Set(current);
@@ -2,77 +2,86 @@ import type { AgentTool } from "@earendil-works/pi-agent-core";
2
2
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
3
  import { z } from "zod";
4
4
  import { type ModuleLoadFailure } from "../../loader.ts";
5
+ import { type DeclaredSecret } from "../../declared-secrets.ts";
5
6
  import { type ReadonlySessionManager, type ToolActivation } from "./tool-context.ts";
6
7
  export interface ToolContext {
7
8
  /** Working directory for this execution. */
8
9
  cwd: string;
9
10
  /** Abort signal for the current turn — honor it to cancel in-flight work on cancellation. */
10
11
  signal?: AbortSignal;
11
- /** Current conversation manager. Present during serving/chat; absent for sessionless direct calls. */
12
12
  sessionManager?: ReadonlySessionManager;
13
- /** Tool activation for the current turn (a loader tool activates {@link DefineToolOptions.deferred}
14
- * tools with it — the built-in `search_tools` is one consumer). Provided by both the serving path
15
- * (agent-session-factory.ts) and chat (session-builder.ts); undefined only outside any turn
16
- * (a bare `fastagent tool` run). */
13
+ /**
14
+ * Tool activation for the current turn (a loader tool activates {@link DefineToolOptions.deferred} tools with it —
15
+ * the built-in `search_tools` is one consumer).
16
+ */
17
17
  tools?: ToolActivation;
18
+ /** THE values of {@link DefineToolOptions.secrets}, keyed by the names this tool declared — read
19
+ * from the process environment per call, so a value rotated THERE takes effect without a restart
20
+ * (one rotated in `.secrets/.env` does not: that file is loaded once at startup). This is how a
21
+ * tool gets a credential: reaching into `process.env` instead leaves the name undeclared, which
22
+ * means nothing carries it to a deployed box and nothing checks it before the call fails. */
23
+ secrets: Record<string, string>;
18
24
  }
19
- export interface DefineToolOptions<I extends z.ZodType> {
20
- /** Explicit name. Usually omitted — a `tools/<name>.ts` tool is named from its filename. */
25
+ export interface DefineToolOptions<I extends z.ZodType, S extends readonly string[] = readonly []> {
21
26
  name?: string;
22
27
  description: string;
23
28
  input: I;
24
29
  /**
25
- * Registered but NOT initially active: the tool's schema stays out of every request (and the model's
26
- * sight) until a loader — the built-in `search_tools`, mounted automatically when any deferred tool
27
- * exists — activates it mid-turn. For tool-heavy agents: fewer schemas per turn, and on providers
28
- * with native deferred loading the activation preserves the prompt-cache prefix. The trade-off:
29
- * discovery rides entirely on this description — write it for the search. Default: false.
30
+ * Registered but NOT initially active: the tool's schema stays out of every request (and the model's sight) until a
31
+ * loader.
30
32
  */
31
33
  deferred?: boolean;
32
- /** pi's per-tool execution mode: "sequential" makes pi run any batch containing this tool serially.
33
- * REQUIRED for a tool that activates others (a loader): pi's own diff around SDK tools (chat)
34
- * would attribute one activation to two parallel calls otherwise. */
34
+ /** pi's per-tool execution mode: "sequential" makes pi run any batch containing this tool serially. */
35
35
  executionMode?: "sequential" | "parallel";
36
- execute: (input: z.infer<I>, ctx: ToolContext) => unknown | Promise<unknown>;
36
+ /**
37
+ * Env vars this tool needs (`secrets: ["X_API_KEY"]`). Their values arrive as `ctx.secrets.X_API_KEY`,
38
+ * typed from this list. Declaring buys the two guarantees a bare `process.env` read cannot have:
39
+ * `deploy` carries the value to the host without it being listed anywhere else, and `dev`/`start`
40
+ * REFUSE TO START while it is unset, naming this file — instead of the tool failing on its first
41
+ * real call (see src/declared-secrets.ts).
42
+ */
43
+ secrets?: S;
44
+ execute: (input: z.infer<I>, ctx: Omit<ToolContext, "secrets"> & {
45
+ secrets: Record<S[number], string>;
46
+ }) => unknown | Promise<unknown>;
37
47
  }
38
48
  /** AgentTool with Pi's optional per-call context; absent for sessionless CLI execution. */
39
49
  export type MountedTool = Omit<AgentTool, "execute"> & {
40
50
  execute(...args: [...Parameters<AgentTool["execute"]>, context?: ExtensionContext]): ReturnType<AgentTool["execute"]>;
41
51
  };
42
- /** An AgentTool with fastagent's deferral marker — the type for raw tools handed to fastagent
43
- * (`config.tools`, L1/L2 `tools`): plain `AgentTool` has no `deferred`, so an object literal with the
44
- * marker would fail excess-property checking against upstream's type. `defineTool` produces it. */
52
+ /** An AgentTool with fastagent's deferral marker. */
45
53
  export type FastagentTool = AgentTool & {
46
54
  deferred?: boolean;
55
+ /** {@link DefineToolOptions.secrets} — read back by `readSecretDeclaration`; pi ignores it. */
56
+ secrets?: readonly string[];
47
57
  };
48
- /** Read the {@link DefineToolOptions.deferred} marker off a mounted tool (extra property on the
49
- * AgentTool object — pi ignores it). */
58
+ /**
59
+ * Read the {@link DefineToolOptions.deferred} marker off a mounted tool (extra property on the AgentTool object — pi
60
+ * ignores it).
61
+ */
50
62
  export declare function isDeferredTool(tool: MountedTool): boolean;
51
- /** The same tool without the deferred marker — for a loader that must stay active (a deferred loader
52
- * could never be activated and would strand every deferred tool). */
63
+ /**
64
+ * The same tool without the deferred marker — for a loader that must stay active (a deferred loader could never be
65
+ * activated and would strand every deferred tool).
66
+ */
53
67
  export declare function stripDeferredMarker(tool: MountedTool): MountedTool;
54
- export declare function defineTool<I extends z.ZodType>(options: DefineToolOptions<I>): FastagentTool;
55
- /** A discarded same-name tool (within `tools/`, or against an existing tool). Surfaced, never silent. */
68
+ export declare function defineTool<I extends z.ZodType, const S extends readonly string[] = readonly []>(options: DefineToolOptions<I, S>): FastagentTool;
69
+ /** A discarded same-name tool (within `tools/`, or against an existing tool). */
56
70
  export interface ToolCollision {
57
71
  name: string;
58
72
  source: string;
59
73
  }
60
- /**
61
- * Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its
62
- * filename. A file broken for ANY reason — a failed import (from {@link loadModuleDir}) or not being a
63
- * tool (no `execute`) — is ISOLATED into `failures` (skipped + reported, not thrown) so one broken file
64
- * can't crash `start`; the agent serves the tools that loaded. A repo turned into an agent often has a
65
- * `tools/` dir of its OWN scripts, which is exactly this case.
66
- */
74
+ /** Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its filename. */
67
75
  export declare function loadTools(dir: string): Promise<{
68
76
  tools: AgentTool[];
77
+ /** What each loaded tool declared it needs, BY TOOL NAME and attributed to the file. Per tool
78
+ * because whether a declaration counts depends on whether that tool ends up MOUNTED — a name
79
+ * shadowed by a coding tool never runs ({@link resolveAgentTools} makes that call). */
80
+ secrets: Map<string, DeclaredSecret[]>;
69
81
  collisions: ToolCollision[];
70
82
  failures: ModuleLoadFailure[];
71
83
  }>;
72
- /**
73
- * Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name.
74
- * Existing tools win; dropped discovered tools surface as collisions.
75
- */
84
+ /** Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name. */
76
85
  export declare function mergeDiscoveredTools(existing: MountedTool[], discovered: AgentTool[]): {
77
86
  tools: MountedTool[];
78
87
  collisions: ToolCollision[];
@@ -1,33 +1,40 @@
1
- /**
2
- * Tool authoring: `defineTool` (the authoring surface) and `loadTools` (filesystem discovery).
3
- * Drop a file in `tools/`, default-export `defineTool({...})`, and it is discovered, named from
4
- * the filename, validated, and injected.
5
- *
6
- * // tools/lookup-order.ts → tool "lookup-order"
7
- * export default defineTool({
8
- * description: "Look up an order by id.",
9
- * input: z.object({ orderId: z.string() }),
10
- * async execute({ orderId }) { return await db.find(orderId); },
11
- * });
12
- */
1
+ /** Tool authoring: `defineTool` (the authoring surface) and `loadTools` (filesystem discovery). */
13
2
  import { join } from "node:path";
14
3
  import { assertInsideAgentDir } from "../../paths.js";
15
4
  import { z } from "zod";
16
5
  import { loadModuleDir } from "../../loader.js";
6
+ import { readSecretDeclaration, secretValues } from "../../declared-secrets.js";
17
7
  import { turnContext } from "./tool-context.js";
18
- /** Read the {@link DefineToolOptions.deferred} marker off a mounted tool (extra property on the
19
- * AgentTool object — pi ignores it). */
8
+ /**
9
+ * Read the {@link DefineToolOptions.deferred} marker off a mounted tool (extra property on the AgentTool object — pi
10
+ * ignores it).
11
+ */
20
12
  export function isDeferredTool(tool) {
21
13
  return tool.deferred === true;
22
14
  }
23
- /** The same tool without the deferred marker — for a loader that must stay active (a deferred loader
24
- * could never be activated and would strand every deferred tool). */
15
+ /**
16
+ * The same tool without the deferred marker — for a loader that must stay active (a deferred loader could never be
17
+ * activated and would strand every deferred tool).
18
+ */
25
19
  export function stripDeferredMarker(tool) {
26
20
  if (!isDeferredTool(tool))
27
21
  return tool;
28
22
  const { deferred: _drop, ...active } = tool;
29
23
  return active;
30
24
  }
25
+ /**
26
+ * Ask the provider to constrain sampling to the tool's schema, the posture pi's own built-ins take. `"prefer"`
27
+ * rather than `"require"`: a schema pi cannot express strictly, or a provider without strict mode, silently falls
28
+ * back to an ordinary function tool instead of failing the turn.
29
+ *
30
+ * Nothing here has to undo pi's strict rewrite. To express "optional" strictly, pi marks every property required and
31
+ * unions the optional ones with `null`, so a constrained model emits `null` where it would have omitted the key — and
32
+ * pi-ai's `validateToolArguments` drops those nulls (`normalizeOptionalNulls`) before `execute` is called. It drops
33
+ * one only where the author's OWN schema rejects null, so any nullable property keeps its `null`, and a
34
+ * `.nullable().optional()` property can no longer distinguish "absent" from "null" — the one place this changes
35
+ * what an author's `execute` receives.
36
+ */
37
+ const CONSTRAINED_SAMPLING = { type: "json_schema", strict: "prefer" };
31
38
  /** Wrap a plain return value into pi's tool-result shape; pass a full result through unchanged. */
32
39
  function wrapResult(value) {
33
40
  if (value && typeof value === "object" && Array.isArray(value.content)) {
@@ -36,6 +43,10 @@ function wrapResult(value) {
36
43
  const text = typeof value === "string" ? value : JSON.stringify(value ?? null);
37
44
  return { content: [{ type: "text", text }], details: value };
38
45
  }
46
+ // `S` defaults to the EMPTY tuple, not `readonly string[]`: a tool that declares nothing then gets
47
+ // `ctx.secrets` with no keys, so `ctx.secrets.X_API_KEY` fails to compile instead of type-checking as
48
+ // a `string` that is `undefined` at run time. "A typo is a compile error" has to hold for the tool
49
+ // that forgot to declare, which is the one making the mistake.
39
50
  export function defineTool(options) {
40
51
  const { $schema: _drop, ...parameters } = z.toJSONSchema(options.input);
41
52
  const tool = {
@@ -43,8 +54,10 @@ export function defineTool(options) {
43
54
  label: options.name ?? "",
44
55
  description: options.description,
45
56
  parameters,
57
+ constrainedSampling: CONSTRAINED_SAMPLING,
46
58
  ...(options.deferred ? { deferred: true } : {}),
47
59
  ...(options.executionMode ? { executionMode: options.executionMode } : {}),
60
+ ...(options.secrets?.length ? { secrets: options.secrets } : {}),
48
61
  async execute(_toolCallId, rawParams, signal) {
49
62
  const parsed = options.input.safeParse(rawParams);
50
63
  if (!parsed.success) {
@@ -53,73 +66,46 @@ export function defineTool(options) {
53
66
  return { content: [{ type: "text", text: `Invalid arguments: ${detail}` }], details: { error: detail } };
54
67
  }
55
68
  const store = turnContext.getStore();
56
- // Stamp tools THIS execute activates on its result — the load point that lets native
57
- // deferred-loading providers add the definitions at this transcript position without
58
- // invalidating the cached prompt prefix. The names come from this execute's OWN activate()
59
- // calls (accumulated below), NOT from an active-set before/after diff: pi runs tool calls of a
60
- // batch in parallel, and a snapshot diff would stamp a sibling's activation onto the wrong tool
61
- // result, drifting the load point.
62
- const added = [];
63
- const tools = store?.tools
64
- ? {
65
- ...store.tools,
66
- activate: (names) => {
67
- // biome-ignore lint/style/noNonNullAssertion: guarded by the ternary above
68
- const activated = store.tools.activate(names);
69
- added.push(...activated);
70
- return activated;
71
- },
72
- }
73
- : undefined;
74
- const result = wrapResult(await options.execute(parsed.data, {
69
+ return wrapResult(await options.execute(parsed.data, {
75
70
  cwd: store?.cwd ?? process.cwd(),
76
71
  signal,
77
72
  sessionManager: store?.sessionManager,
78
- tools,
73
+ tools: store?.tools,
74
+ secrets: secretValues(options.secrets),
79
75
  }));
80
- if (added.length > 0) {
81
- // A copy, not a mutation: wrapResult passes a full AgentToolResult through by REFERENCE, and an
82
- // author may legally return a shared/frozen result object — stamping in place would corrupt it
83
- // across calls (or throw on frozen), only on the rare activating path.
84
- return { ...result, addedToolNames: [...new Set([...(result.addedToolNames ?? []), ...added])] };
85
- }
86
- return result;
87
76
  },
88
77
  };
89
78
  return tool;
90
79
  }
91
- /**
92
- * Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its
93
- * filename. A file broken for ANY reason — a failed import (from {@link loadModuleDir}) or not being a
94
- * tool (no `execute`) — is ISOLATED into `failures` (skipped + reported, not thrown) so one broken file
95
- * can't crash `start`; the agent serves the tools that loaded. A repo turned into an agent often has a
96
- * `tools/` dir of its OWN scripts, which is exactly this case.
97
- */
80
+ /** Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its filename. */
98
81
  export async function loadTools(dir) {
99
- // The same containment guard channels/schedules/skills get — and `tools/` is the one that gets
100
- // IMPORTED AND EXECUTED, so a symlink escaping the agent dir is exactly what it must refuse.
82
+ // The same containment guard channels/routines/skills get.
101
83
  await assertInsideAgentDir(dir, "tools");
102
84
  const { modules, failures } = await loadModuleDir(join(dir, "tools"));
103
85
  const byName = new Map();
104
86
  const collisions = [];
87
+ const secrets = new Map();
105
88
  for (const { name, label, file, mod } of modules) {
106
89
  const tool = mod.default;
107
90
  if (!tool || typeof tool.execute !== "function") {
108
91
  failures.push({ label, file, message: `${label} must default-export defineTool({...})` });
109
92
  continue;
110
93
  }
94
+ const declaration = readSecretDeclaration(tool, label);
95
+ if (declaration.error !== undefined) {
96
+ failures.push({ label, file, message: declaration.error });
97
+ continue;
98
+ }
111
99
  if (byName.has(name)) {
112
100
  collisions.push({ name, source: label });
113
101
  continue;
114
102
  }
115
103
  byName.set(name, { ...tool, name });
104
+ secrets.set(name, declaration.secrets);
116
105
  }
117
- return { tools: [...byName.values()], collisions, failures };
106
+ return { tools: [...byName.values()], secrets, collisions, failures };
118
107
  }
119
- /**
120
- * Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name.
121
- * Existing tools win; dropped discovered tools surface as collisions.
122
- */
108
+ /** Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name. */
123
109
  export function mergeDiscoveredTools(existing, discovered) {
124
110
  const names = new Set(existing.map((t) => t.name));
125
111
  const tools = [...existing];
@@ -1,25 +1,10 @@
1
- /**
2
- * The turn mechanism's ENGINE-agnostic half: the parts that describe a turn rather than pi. Not
3
- * "engine-neutral" in this repo's sense — that term is reserved for code with no engine import at
4
- * all (src/agent.ts), and the terminals here read pi's message shape. What they do not touch is how
5
- * a turn is driven, which is why they survived the engine change unaltered.
6
- *
7
- * Lease — single-writer concurrency floor (injectable port + in-process default)
8
- * Terminals — a settled pi message or a thrown error → the SPEC terminal, `retryable` included
9
- * EventQueue — push→pull plumbing for engines that emit events beside their result
10
- * Prompt prep — SPEC images → pi's ImageContent
11
- * Projection — the rich SessionEvent stream → the narrow SPEC one
12
- * Observation — the seam a control-plane hub attaches to (RunControls + SessionObserver)
13
- *
14
- * What is NOT neutral — pi's event vocabulary and how a turn is driven — stays in invoke-session.ts,
15
- * the L0 that owns it.
16
- */
1
+ /** The turn mechanism's ENGINE-agnostic half: the parts that describe a turn rather than pi. */
17
2
  import type { AssistantMessage, ImageContent } from "@earendil-works/pi-ai";
18
3
  import { type AgentEvent, type Prompt } from "../../agent.ts";
19
4
  import type { SessionEvent } from "../../session.ts";
20
5
  export type Release = () => void;
21
6
  export interface Lease {
22
- /** Try to acquire exclusive write access for the session (fail-fast). Returns null if held. */
7
+ /** Try to acquire exclusive write access for the session (fail-fast). */
23
8
  tryAcquire(session: string): Release | null;
24
9
  }
25
10
  export declare function inProcessLease(): Lease;
@@ -29,54 +14,31 @@ export declare function classifyRetryable(details: string, signal: {
29
14
  code?: unknown;
30
15
  }): boolean;
31
16
  /**
32
- * Terminal mapping, decided by the resolved message's stopReason: pi's prompt() resolves a message
33
- * with stopReason "error"/"aborted" rather than throwing, so relying on catch alone would miss this
34
- * entire failure class (violating SPEC MUST 1).
17
+ * Terminal mapping, decided by the resolved message's stopReason: pi's `prompt()` RESOLVES a message with stopReason
18
+ * "error"/"aborted" rather than throwing, so relying on catch alone would miss that whole failure class and violate
19
+ * SPEC MUST 1.
35
20
  */
36
21
  export declare function toTerminal(message: AssistantMessage): AgentEvent;
37
22
  export declare function errorToTerminal(error: unknown): Extract<AgentEvent, {
38
23
  type: "failed";
39
24
  }>;
40
25
  /**
41
- * Map prompt images to pi ImageContent, resizing each to model-friendly dimensions/size with pi's
42
- * Photon resizer (reused from pi-coding-agent, lazy-imported so the common no-image headless path never
43
- * loads the TUI module graph). A null resize (unresizable / Photon unavailable) keeps the original
44
- * bytes — the provider then applies its own limit.
26
+ * Map prompt images to pi ImageContent. `prompt()` normalizes its images itself, with the bound model's resize
27
+ * profile (`inputLimits.images.resize`) and format conversion, so a `"prompt"` delivery hands them over as-is.
28
+ * `steer`/`followUp` do not, so a `"queued"` delivery resizes here with pi's default profile: the model is not known
29
+ * yet when a queued message is prepared. pi's resizer is lazy-imported so the no-image path never loads its module
30
+ * graph.
45
31
  */
46
- export declare function toPiPromptOptions(prompt: Prompt): Promise<{
32
+ export declare function toPiPromptOptions(prompt: Prompt, delivery: "prompt" | "queued"): Promise<{
47
33
  images?: ImageContent[];
48
34
  } | undefined>;
49
- /** Live modulation handles for one active run — what the control plane's `dispatch` routes to.
50
- * Built inside the turn (it owns the engine instance); registered with the observer at
51
- * run_started, gone after run_settled. RACE WINDOW (all three commands, symmetric): the run may
52
- * resolve between the settled-check and the engine call landing — an accepted `abort` can still
53
- * settle `completed`, and an accepted `steer`/`followUp` can settle without the prompt ever being
54
- * consumed. Acceptance is not outcome; the settlement is the truth. */
35
+ /** Live modulation handles for one active run — what the control plane's `dispatch` routes to. */
55
36
  export interface RunControls {
56
37
  steer(prompt: Prompt): Promise<void>;
57
38
  followUp(prompt: Prompt): Promise<void>;
58
39
  abort(): Promise<void>;
59
40
  }
60
- /** The DATA-plane observation seam: every rich event of every run, pushed as it happens. `run`
61
- * carries the live {@link RunControls}, attached to the `run_started` event only. A hub
62
- * (session-control.ts) implements this to serve `events()`/`state()`/`dispatch`; absent = zero
63
- * overhead. Scope: RUN events only — the hub's own boundary-mutation events (`state_changed`,
64
- * `compaction_*`) originate in the hub and reach full-vocabulary taps via the hub's `tap` option,
65
- * not this seam. TRUST BOUNDARY: this seam hands every wired observer the run's modulation handles — it is the trusted hub seam, not a public fan-out point. Do not wire
66
- * untrusted taps here; give third parties the read-only `events()` stream instead. */
41
+ /** The DATA-plane observation seam: every rich event of every run, pushed as it happens. */
67
42
  export type SessionObserver = (session: string, event: SessionEvent, run?: RunControls) => void;
68
- /** The SPEC projection of the rich stream. Events with no `AgentEvent` counterpart (progress,
69
- * message boundaries, run boundaries) project to null — the invoke terminal is produced from the
70
- * resolved message ({@link toTerminal}), not from `run_settled`. */
43
+ /** The SPEC projection of the rich stream. */
71
44
  export declare function projectAgentEvent(se: SessionEvent): AgentEvent | null;
72
- export declare class EventQueue<T> {
73
- private buffer;
74
- private wake?;
75
- push(item: T): void;
76
- /**
77
- * Yield pushed events in order until `done` settles AND the buffer is drained. The terminal is
78
- * produced separately (toTerminal); rejections of `done` are swallowed here (the caller awaits
79
- * `run` itself) to avoid unhandled rejections.
80
- */
81
- drainUntil(done: Promise<unknown>): AsyncGenerator<T>;
82
- }