@fastagent-sh/fastagent 0.24.4 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (487) hide show
  1. package/README.md +54 -82
  2. package/dist/agent-api.d.ts +460 -0
  3. package/dist/agent-api.js +209 -0
  4. package/dist/answer-text.d.ts +8 -0
  5. package/dist/answer-text.js +18 -0
  6. package/dist/atomic-write.d.ts +2 -2
  7. package/dist/atomic-write.js +2 -2
  8. package/dist/authoring.d.ts +81 -0
  9. package/dist/authoring.js +245 -0
  10. package/dist/channel.d.ts +29 -15
  11. package/dist/channel.js +4 -1
  12. package/dist/channels/agentcore-protocol.d.ts +36 -50
  13. package/dist/channels/agentcore-protocol.js +3 -3
  14. package/dist/channels/agentcore-service.d.ts +8 -18
  15. package/dist/channels/agentcore-service.js +108 -127
  16. package/dist/channels/agentcore.d.ts +8 -24
  17. package/dist/channels/agentcore.js +32 -65
  18. package/dist/channels/body.d.ts +0 -16
  19. package/dist/channels/body.js +0 -25
  20. package/dist/channels/busy.js +6 -1
  21. package/dist/channels/context.d.ts +4 -0
  22. package/dist/channels/context.js +35 -0
  23. package/dist/channels/discover.d.ts +18 -20
  24. package/dist/channels/discover.js +41 -99
  25. package/dist/channels/feishu/feishu-api.d.ts +54 -4
  26. package/dist/channels/feishu/feishu-api.js +192 -149
  27. package/dist/channels/feishu/feishu.d.ts +3 -0
  28. package/dist/channels/feishu/feishu.js +117 -106
  29. package/dist/channels/feishu/history.d.ts +55 -0
  30. package/dist/channels/feishu/history.js +199 -0
  31. package/dist/channels/feishu/invoke-turn.d.ts +13 -11
  32. package/dist/channels/feishu/invoke-turn.js +9 -10
  33. package/dist/channels/feishu/model.d.ts +2 -1
  34. package/dist/channels/feishu/normalize.js +100 -0
  35. package/dist/channels/feishu/preview.d.ts +2 -2
  36. package/dist/channels/feishu/register-webhook.d.ts +1 -0
  37. package/dist/channels/feishu/register-webhook.js +3 -2
  38. package/dist/channels/feishu/scaffold/channel.ts +65 -46
  39. package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
  40. package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
  41. package/dist/channels/feishu/setup-mode.d.ts +45 -25
  42. package/dist/channels/feishu/setup-mode.js +74 -23
  43. package/dist/channels/feishu/shared-api.d.ts +24 -6
  44. package/dist/channels/feishu/shared-api.js +30 -24
  45. package/dist/channels/kit/context-buffer.d.ts +11 -2
  46. package/dist/channels/kit/context-buffer.js +7 -4
  47. package/dist/channels/kit/delivery.d.ts +19 -3
  48. package/dist/channels/kit/delivery.js +74 -15
  49. package/dist/channels/kit/event-stream.js +2 -1
  50. package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
  51. package/dist/channels/kit/invoke-turn-kit.js +62 -71
  52. package/dist/channels/kit/place-history.d.ts +107 -0
  53. package/dist/channels/kit/place-history.js +182 -0
  54. package/dist/channels/kit/preview-kit.d.ts +9 -5
  55. package/dist/channels/kit/preview-kit.js +36 -15
  56. package/dist/channels/kit/room-threads.d.ts +46 -0
  57. package/dist/channels/kit/room-threads.js +81 -0
  58. package/dist/channels/kit/seen.js +1 -1
  59. package/dist/channels/kit/stop-command.d.ts +6 -3
  60. package/dist/channels/kit/stop-command.js +20 -18
  61. package/dist/channels/kit/thread-fork.d.ts +17 -0
  62. package/dist/channels/kit/thread-fork.js +43 -0
  63. package/dist/channels/kit/thread-participants.js +1 -1
  64. package/dist/channels/kit/transport.d.ts +5 -0
  65. package/dist/channels/kit/transport.js +5 -0
  66. package/dist/channels/kit/turn-runner.d.ts +10 -9
  67. package/dist/channels/kit/turn-runner.js +17 -19
  68. package/dist/channels/kit/turn-store.d.ts +5 -4
  69. package/dist/channels/kit/turn-store.js +5 -5
  70. package/dist/channels/lark/lark.d.ts +3 -0
  71. package/dist/channels/lark/lark.js +5 -0
  72. package/dist/channels/lark/onboard.d.ts +5 -3
  73. package/dist/channels/lark/onboard.js +14 -14
  74. package/dist/channels/lark/scaffold/channel.ts +67 -44
  75. package/dist/channels/lark/scaffold/lark-send.ts +38 -33
  76. package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
  77. package/dist/channels/respond.d.ts +0 -4
  78. package/dist/channels/respond.js +1 -1
  79. package/dist/channels/router.d.ts +18 -0
  80. package/dist/channels/router.js +68 -0
  81. package/dist/channels/serve.d.ts +5 -70
  82. package/dist/channels/serve.js +105 -287
  83. package/dist/channels/slack/history.d.ts +42 -0
  84. package/dist/channels/slack/history.js +282 -0
  85. package/dist/channels/slack/invoke-turn.d.ts +12 -5
  86. package/dist/channels/slack/invoke-turn.js +7 -5
  87. package/dist/channels/slack/manifest.d.ts +1 -4
  88. package/dist/channels/slack/manifest.js +22 -10
  89. package/dist/channels/slack/model.d.ts +2 -1
  90. package/dist/channels/slack/onboard.d.ts +0 -2
  91. package/dist/channels/slack/onboard.js +2 -4
  92. package/dist/channels/slack/onboarding-state.d.ts +0 -2
  93. package/dist/channels/slack/onboarding-state.js +0 -1
  94. package/dist/channels/slack/parse.d.ts +0 -5
  95. package/dist/channels/slack/parse.js +1 -16
  96. package/dist/channels/slack/preview.d.ts +2 -3
  97. package/dist/channels/slack/preview.js +78 -52
  98. package/dist/channels/slack/register-webhook.d.ts +1 -0
  99. package/dist/channels/slack/register-webhook.js +6 -5
  100. package/dist/channels/slack/scaffold/channel.ts +23 -24
  101. package/dist/channels/slack/scaffold/slack-send.ts +50 -43
  102. package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
  103. package/dist/channels/slack/setup-server.d.ts +19 -0
  104. package/dist/channels/slack/setup-server.js +113 -11
  105. package/dist/channels/slack/shared-api.d.ts +15 -4
  106. package/dist/channels/slack/shared-api.js +19 -14
  107. package/dist/channels/slack/slack-api.d.ts +61 -2
  108. package/dist/channels/slack/slack-api.js +28 -5
  109. package/dist/channels/slack/slack.js +97 -52
  110. package/dist/channels/slack/welcomed.js +1 -1
  111. package/dist/channels/telegram/context-buffer.d.ts +6 -1
  112. package/dist/channels/telegram/context-buffer.js +19 -0
  113. package/dist/channels/telegram/invoke-turn.d.ts +8 -4
  114. package/dist/channels/telegram/invoke-turn.js +7 -7
  115. package/dist/channels/telegram/parse.d.ts +7 -2
  116. package/dist/channels/telegram/parse.js +7 -0
  117. package/dist/channels/telegram/preview.d.ts +2 -2
  118. package/dist/channels/telegram/register-webhook.d.ts +1 -0
  119. package/dist/channels/telegram/register-webhook.js +3 -2
  120. package/dist/channels/telegram/scaffold/channel.ts +27 -28
  121. package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
  122. package/dist/channels/telegram/shared-api.d.ts +34 -0
  123. package/dist/channels/telegram/shared-api.js +79 -0
  124. package/dist/channels/telegram/telegram-api.d.ts +15 -5
  125. package/dist/channels/telegram/telegram-api.js +10 -6
  126. package/dist/channels/telegram/telegram.js +27 -21
  127. package/dist/channels/telegram/turn-store.js +1 -1
  128. package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
  129. package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
  130. package/dist/cli/add-feishu.d.ts +42 -12
  131. package/dist/cli/add-feishu.js +177 -124
  132. package/dist/cli/add-slack.d.ts +8 -3
  133. package/dist/cli/add-slack.js +116 -64
  134. package/dist/cli/auth-view.d.ts +10 -3
  135. package/dist/cli/auth-view.js +8 -6
  136. package/dist/cli/box-login.d.ts +18 -7
  137. package/dist/cli/box-login.js +46 -12
  138. package/dist/cli/commands/add.d.ts +15 -4
  139. package/dist/cli/commands/add.js +253 -143
  140. package/dist/cli/commands/auth.d.ts +6 -0
  141. package/dist/cli/commands/auth.js +50 -0
  142. package/dist/cli/commands/content.d.ts +6 -0
  143. package/dist/cli/commands/content.js +45 -0
  144. package/dist/cli/commands/deploy/agentcore.js +50 -77
  145. package/dist/cli/commands/deploy/docker.js +38 -39
  146. package/dist/cli/commands/deploy/fly.js +21 -18
  147. package/dist/cli/commands/deploy/host-login.d.ts +26 -0
  148. package/dist/cli/commands/deploy/host-login.js +90 -0
  149. package/dist/cli/commands/deploy/railway.js +21 -26
  150. package/dist/cli/commands/deploy/shared.d.ts +45 -17
  151. package/dist/cli/commands/deploy/shared.js +34 -15
  152. package/dist/cli/commands/deploy.d.ts +0 -4
  153. package/dist/cli/commands/deploy.js +237 -50
  154. package/dist/cli/commands/destroy.js +8 -6
  155. package/dist/cli/commands/dev.d.ts +0 -2
  156. package/dist/cli/commands/dev.js +20 -19
  157. package/dist/cli/commands/env.d.ts +12 -0
  158. package/dist/cli/commands/env.js +191 -0
  159. package/dist/cli/commands/info.js +95 -156
  160. package/dist/cli/commands/init.d.ts +2 -2
  161. package/dist/cli/commands/init.js +35 -33
  162. package/dist/cli/commands/invoke.d.ts +6 -0
  163. package/dist/cli/commands/invoke.js +132 -16
  164. package/dist/cli/commands/login.d.ts +31 -2
  165. package/dist/cli/commands/login.js +416 -65
  166. package/dist/cli/commands/logout.d.ts +4 -0
  167. package/dist/cli/commands/logout.js +53 -0
  168. package/dist/cli/commands/logs.js +8 -6
  169. package/dist/cli/commands/models.d.ts +2 -1
  170. package/dist/cli/commands/models.js +39 -37
  171. package/dist/cli/commands/schedules.d.ts +21 -0
  172. package/dist/cli/commands/schedules.js +143 -0
  173. package/dist/cli/commands/start.d.ts +11 -10
  174. package/dist/cli/commands/start.js +35 -62
  175. package/dist/cli/content-view.d.ts +9 -0
  176. package/dist/cli/content-view.js +26 -0
  177. package/dist/cli/fail.d.ts +9 -12
  178. package/dist/cli/fail.js +17 -14
  179. package/dist/cli/invoke-stream.d.ts +12 -3
  180. package/dist/cli/invoke-stream.js +85 -23
  181. package/dist/cli/kernel.d.ts +9 -2
  182. package/dist/cli/kernel.js +15 -6
  183. package/dist/cli/login-relay.d.ts +6 -4
  184. package/dist/cli/login-relay.js +19 -7
  185. package/dist/cli/models-view.d.ts +5 -1
  186. package/dist/cli/models-view.js +1 -1
  187. package/dist/cli/person.d.ts +10 -0
  188. package/dist/cli/person.js +23 -0
  189. package/dist/cli/program.js +421 -203
  190. package/dist/cli/serve.d.ts +13 -63
  191. package/dist/cli/serve.js +31 -121
  192. package/dist/cli/shared.d.ts +21 -48
  193. package/dist/cli/shared.js +70 -89
  194. package/dist/cli/ticket.d.ts +136 -0
  195. package/dist/cli/ticket.js +476 -0
  196. package/dist/client.d.ts +7 -0
  197. package/dist/client.js +2 -0
  198. package/dist/content/declare.d.ts +70 -0
  199. package/dist/content/declare.js +163 -0
  200. package/dist/content/file.d.ts +32 -0
  201. package/dist/content/file.js +119 -0
  202. package/dist/content/git.d.ts +41 -0
  203. package/dist/content/git.js +220 -0
  204. package/dist/content/mount.d.ts +2 -0
  205. package/dist/content/mount.js +15 -0
  206. package/dist/content/resolve.d.ts +52 -0
  207. package/dist/content/resolve.js +151 -0
  208. package/dist/content/source.d.ts +27 -0
  209. package/dist/content/source.js +53 -0
  210. package/dist/conversation-sessions.d.ts +26 -0
  211. package/dist/conversation-sessions.js +99 -0
  212. package/dist/declared-secrets.d.ts +19 -37
  213. package/dist/declared-secrets.js +16 -58
  214. package/dist/deploy/agentcore/destroy.d.ts +3 -3
  215. package/dist/deploy/agentcore/destroy.js +46 -28
  216. package/dist/deploy/agentcore/forwarder.js +105 -22
  217. package/dist/deploy/agentcore/logs.js +1 -1
  218. package/dist/deploy/agentcore/plan.d.ts +10 -24
  219. package/dist/deploy/agentcore/plan.js +31 -135
  220. package/dist/deploy/agentcore/run.d.ts +1 -1
  221. package/dist/deploy/agentcore/run.js +8 -5
  222. package/dist/deploy/agentcore/shell.d.ts +1 -1
  223. package/dist/deploy/agentcore/shell.js +5 -5
  224. package/dist/deploy/api.d.ts +34 -0
  225. package/dist/deploy/api.js +50 -0
  226. package/dist/deploy/box-shell.d.ts +6 -0
  227. package/dist/deploy/box-shell.js +7 -0
  228. package/dist/deploy/build-context.d.ts +7 -5
  229. package/dist/deploy/build-context.js +41 -35
  230. package/dist/deploy/channel-ingress.d.ts +6 -1
  231. package/dist/deploy/channel-ingress.js +18 -5
  232. package/dist/deploy/container.d.ts +22 -9
  233. package/dist/deploy/container.js +96 -55
  234. package/dist/deploy/docker/plan.d.ts +3 -2
  235. package/dist/deploy/docker/plan.js +13 -14
  236. package/dist/deploy/docker/run.d.ts +2 -2
  237. package/dist/deploy/docker/run.js +8 -4
  238. package/dist/deploy/fly/plan.d.ts +1 -1
  239. package/dist/deploy/fly/plan.js +12 -21
  240. package/dist/deploy/fly/run.d.ts +10 -6
  241. package/dist/deploy/fly/run.js +11 -6
  242. package/dist/deploy/hosts.d.ts +2 -1
  243. package/dist/deploy/hosts.js +2 -1
  244. package/dist/deploy/preflight.d.ts +21 -23
  245. package/dist/deploy/preflight.js +223 -202
  246. package/dist/deploy/railway/plan.d.ts +8 -6
  247. package/dist/deploy/railway/plan.js +15 -58
  248. package/dist/deploy/railway/run.d.ts +8 -3
  249. package/dist/deploy/railway/run.js +12 -8
  250. package/dist/deploy/residency.d.ts +5 -11
  251. package/dist/deploy/residency.js +0 -6
  252. package/dist/deploy/runner.d.ts +1 -1
  253. package/dist/deploy/runner.js +1 -1
  254. package/dist/deploy/secrets.d.ts +16 -2
  255. package/dist/deploy/secrets.js +33 -6
  256. package/dist/deploy/workspace.d.ts +11 -6
  257. package/dist/deploy/workspace.js +55 -38
  258. package/dist/dev-supervisor.d.ts +11 -2
  259. package/dist/dev-supervisor.js +52 -27
  260. package/dist/effect-port.d.ts +4 -4
  261. package/dist/effect-port.js +4 -4
  262. package/dist/env.d.ts +0 -13
  263. package/dist/env.js +1 -44
  264. package/dist/environment/apt.d.ts +4 -0
  265. package/dist/environment/apt.js +32 -0
  266. package/dist/environment/declare.d.ts +59 -0
  267. package/dist/environment/declare.js +210 -0
  268. package/dist/environment/mise.d.ts +51 -0
  269. package/dist/environment/mise.js +272 -0
  270. package/dist/feishu.d.ts +2 -2
  271. package/dist/feishu.js +2 -2
  272. package/dist/git.d.ts +13 -0
  273. package/dist/git.js +55 -0
  274. package/dist/harnesses/models/catalog.d.ts +20 -0
  275. package/dist/harnesses/models/catalog.js +254 -0
  276. package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
  277. package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
  278. package/dist/harnesses/models/environment.d.ts +48 -0
  279. package/dist/harnesses/models/environment.js +88 -0
  280. package/dist/harnesses/models/host.d.ts +30 -0
  281. package/dist/harnesses/models/host.js +97 -0
  282. package/dist/{engines/pi → harnesses/models}/login.d.ts +66 -70
  283. package/dist/harnesses/models/login.js +439 -0
  284. package/dist/harnesses/models/models.d.ts +84 -0
  285. package/dist/harnesses/models/models.js +197 -0
  286. package/dist/harnesses/pi-durable/agent.d.ts +42 -0
  287. package/dist/harnesses/pi-durable/agent.js +581 -0
  288. package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
  289. package/dist/harnesses/pi-durable/defaults.js +26 -0
  290. package/dist/harnesses/pi-durable/define.d.ts +77 -0
  291. package/dist/harnesses/pi-durable/define.js +28 -0
  292. package/dist/harnesses/pi-durable/entries.d.ts +32 -0
  293. package/dist/harnesses/pi-durable/entries.js +126 -0
  294. package/dist/harnesses/pi-durable/events.d.ts +25 -0
  295. package/dist/harnesses/pi-durable/events.js +238 -0
  296. package/dist/harnesses/pi-durable/ids.d.ts +16 -0
  297. package/dist/harnesses/pi-durable/ids.js +55 -0
  298. package/dist/harnesses/pi-durable/messages.d.ts +41 -0
  299. package/dist/harnesses/pi-durable/messages.js +116 -0
  300. package/dist/harnesses/pi-durable/open.d.ts +95 -0
  301. package/dist/harnesses/pi-durable/open.js +330 -0
  302. package/dist/harnesses/pi-durable/reads.d.ts +30 -0
  303. package/dist/harnesses/pi-durable/reads.js +94 -0
  304. package/dist/harnesses/pi-durable/registry.d.ts +18 -0
  305. package/dist/harnesses/pi-durable/registry.js +56 -0
  306. package/dist/harnesses/pi-durable/release.d.ts +13 -0
  307. package/dist/harnesses/pi-durable/release.js +40 -0
  308. package/dist/harnesses/pi-durable/serve.d.ts +90 -0
  309. package/dist/harnesses/pi-durable/serve.js +186 -0
  310. package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
  311. package/dist/harnesses/pi-durable/sessions.js +47 -0
  312. package/dist/harnesses/pi-durable/storage.d.ts +4 -0
  313. package/dist/harnesses/pi-durable/storage.js +49 -0
  314. package/dist/harnesses/pi-durable/stream.d.ts +20 -0
  315. package/dist/harnesses/pi-durable/stream.js +221 -0
  316. package/dist/harnesses/pi-durable/text.d.ts +30 -0
  317. package/dist/harnesses/pi-durable/text.js +292 -0
  318. package/dist/harnesses/pi-durable/wake.d.ts +11 -0
  319. package/dist/harnesses/pi-durable/wake.js +90 -0
  320. package/dist/http/client.d.ts +6 -0
  321. package/dist/http/client.js +256 -0
  322. package/dist/http/contract.d.ts +541 -0
  323. package/dist/http/contract.js +179 -0
  324. package/dist/http/server.d.ts +34 -0
  325. package/dist/http/server.js +194 -0
  326. package/dist/index.d.ts +19 -4
  327. package/dist/index.js +13 -5
  328. package/dist/lark.d.ts +2 -2
  329. package/dist/lark.js +2 -2
  330. package/dist/locked-file.d.ts +17 -0
  331. package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
  332. package/dist/once.d.ts +6 -0
  333. package/dist/once.js +23 -0
  334. package/dist/paths.d.ts +36 -52
  335. package/dist/paths.js +66 -182
  336. package/dist/runtime.d.ts +25 -2
  337. package/dist/runtime.js +27 -0
  338. package/dist/scaffold/add-channel.d.ts +14 -27
  339. package/dist/scaffold/add-channel.js +82 -180
  340. package/dist/scaffold/init.d.ts +17 -18
  341. package/dist/scaffold/init.js +87 -99
  342. package/dist/scaffold/templates/SYSTEM.md +12 -0
  343. package/dist/scaffold/templates/env.example +4 -10
  344. package/dist/scaffold/templates/fastagent.config.ts +21 -33
  345. package/dist/scaffold/templates/gitignore +8 -1
  346. package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
  347. package/dist/scaffold/templates.d.ts +9 -4
  348. package/dist/scaffold/templates.js +7 -5
  349. package/dist/scaffold/vendor-skill.d.ts +1 -4
  350. package/dist/scaffold/vendor-skill.js +5 -6
  351. package/dist/schedule/cron.d.ts +13 -0
  352. package/dist/schedule/cron.js +52 -0
  353. package/dist/schedule/eventbridge-cron.d.ts +17 -0
  354. package/dist/schedule/eventbridge-cron.js +88 -0
  355. package/dist/schedule/schedule.d.ts +23 -0
  356. package/dist/schedule/schedule.js +34 -0
  357. package/dist/schedule/scheduler.d.ts +34 -33
  358. package/dist/schedule/scheduler.js +251 -137
  359. package/dist/schedule/state.d.ts +4 -14
  360. package/dist/schedule/state.js +7 -7
  361. package/dist/schedule/wake-alarm.d.ts +22 -2
  362. package/dist/schedule/wake-alarm.js +66 -20
  363. package/dist/schedule/wakeups.d.ts +16 -15
  364. package/dist/schedule/wakeups.js +47 -45
  365. package/dist/service.d.ts +56 -169
  366. package/dist/service.js +91 -187
  367. package/dist/serving-command.d.ts +10 -0
  368. package/dist/serving-command.js +16 -0
  369. package/dist/sign-in-api.d.ts +147 -0
  370. package/dist/sign-in-api.js +9 -0
  371. package/dist/slack.d.ts +1 -1
  372. package/dist/slack.js +1 -1
  373. package/dist/{channels/kit/state.js → state-file.js} +6 -6
  374. package/dist/telegram.d.ts +1 -0
  375. package/dist/telegram.js +1 -0
  376. package/dist/tunnel.d.ts +3 -0
  377. package/dist/tunnel.js +14 -5
  378. package/dist/value-file.d.ts +40 -0
  379. package/dist/value-file.js +164 -0
  380. package/dist/version.d.ts +5 -0
  381. package/dist/version.js +14 -1
  382. package/package.json +18 -28
  383. package/dist/agent.d.ts +0 -106
  384. package/dist/agent.js +0 -21
  385. package/dist/channels/control.d.ts +0 -31
  386. package/dist/channels/control.js +0 -329
  387. package/dist/channels/define-channel.d.ts +0 -39
  388. package/dist/channels/define-channel.js +0 -9
  389. package/dist/channels/feishu/context-buffer.d.ts +0 -35
  390. package/dist/channels/feishu/context-buffer.js +0 -65
  391. package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
  392. package/dist/channels/http.d.ts +0 -23
  393. package/dist/channels/http.js +0 -57
  394. package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
  395. package/dist/channels/slack/context-buffer.d.ts +0 -23
  396. package/dist/channels/slack/context-buffer.js +0 -37
  397. package/dist/channels/sse.d.ts +0 -4
  398. package/dist/channels/sse.js +0 -66
  399. package/dist/cli/commands/chat.d.ts +0 -3
  400. package/dist/cli/commands/chat.js +0 -14
  401. package/dist/cli/commands/routine-run.d.ts +0 -6
  402. package/dist/cli/commands/routine-run.js +0 -46
  403. package/dist/cli/commands/routine.d.ts +0 -25
  404. package/dist/cli/commands/routine.js +0 -122
  405. package/dist/cli/commands/tool.d.ts +0 -1
  406. package/dist/cli/commands/tool.js +0 -57
  407. package/dist/collect.d.ts +0 -63
  408. package/dist/collect.js +0 -90
  409. package/dist/core.d.ts +0 -13
  410. package/dist/core.js +0 -15
  411. package/dist/engines/pi/agent-models.d.ts +0 -83
  412. package/dist/engines/pi/agent-models.js +0 -120
  413. package/dist/engines/pi/agent-session-factory.d.ts +0 -121
  414. package/dist/engines/pi/agent-session-factory.js +0 -535
  415. package/dist/engines/pi/chat.d.ts +0 -3
  416. package/dist/engines/pi/chat.js +0 -8
  417. package/dist/engines/pi/config.d.ts +0 -71
  418. package/dist/engines/pi/config.js +0 -199
  419. package/dist/engines/pi/create.d.ts +0 -170
  420. package/dist/engines/pi/create.js +0 -343
  421. package/dist/engines/pi/definition.d.ts +0 -74
  422. package/dist/engines/pi/definition.js +0 -236
  423. package/dist/engines/pi/entry-images.d.ts +0 -14
  424. package/dist/engines/pi/entry-images.js +0 -23
  425. package/dist/engines/pi/invoke-session.d.ts +0 -18
  426. package/dist/engines/pi/invoke-session.js +0 -403
  427. package/dist/engines/pi/locked-file.d.ts +0 -9
  428. package/dist/engines/pi/login.js +0 -331
  429. package/dist/engines/pi/machine.d.ts +0 -53
  430. package/dist/engines/pi/machine.js +0 -137
  431. package/dist/engines/pi/models.d.ts +0 -200
  432. package/dist/engines/pi/models.js +0 -533
  433. package/dist/engines/pi/open.d.ts +0 -180
  434. package/dist/engines/pi/open.js +0 -270
  435. package/dist/engines/pi/openai-account-models.d.ts +0 -35
  436. package/dist/engines/pi/openai-account-models.js +0 -116
  437. package/dist/engines/pi/report.d.ts +0 -18
  438. package/dist/engines/pi/report.js +0 -59
  439. package/dist/engines/pi/retry-event.d.ts +0 -6
  440. package/dist/engines/pi/retry-event.js +0 -15
  441. package/dist/engines/pi/service.d.ts +0 -9
  442. package/dist/engines/pi/service.js +0 -14
  443. package/dist/engines/pi/session-builder.d.ts +0 -13
  444. package/dist/engines/pi/session-builder.js +0 -117
  445. package/dist/engines/pi/session-control.d.ts +0 -77
  446. package/dist/engines/pi/session-control.js +0 -908
  447. package/dist/engines/pi/session-effects.d.ts +0 -14
  448. package/dist/engines/pi/session-effects.js +0 -30
  449. package/dist/engines/pi/session-inheritance.d.ts +0 -21
  450. package/dist/engines/pi/session-inheritance.js +0 -204
  451. package/dist/engines/pi/session-markers.d.ts +0 -49
  452. package/dist/engines/pi/session-markers.js +0 -60
  453. package/dist/engines/pi/session-settings.d.ts +0 -67
  454. package/dist/engines/pi/session-settings.js +0 -119
  455. package/dist/engines/pi/session-store.d.ts +0 -69
  456. package/dist/engines/pi/session-store.js +0 -430
  457. package/dist/engines/pi/tool-context.d.ts +0 -45
  458. package/dist/engines/pi/tool-context.js +0 -31
  459. package/dist/engines/pi/tool.d.ts +0 -99
  460. package/dist/engines/pi/tool.js +0 -118
  461. package/dist/engines/pi/turn-kit.d.ts +0 -92
  462. package/dist/engines/pi/turn-kit.js +0 -288
  463. package/dist/engines/pi/wake-tool.d.ts +0 -9
  464. package/dist/engines/pi/wake-tool.js +0 -92
  465. package/dist/loader.d.ts +0 -41
  466. package/dist/loader.js +0 -116
  467. package/dist/node.d.ts +0 -3
  468. package/dist/node.js +0 -4
  469. package/dist/observe.d.ts +0 -6
  470. package/dist/observe.js +0 -47
  471. package/dist/pi.d.ts +0 -25
  472. package/dist/pi.js +0 -15
  473. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  474. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  475. package/dist/schedule/discover.d.ts +0 -17
  476. package/dist/schedule/discover.js +0 -61
  477. package/dist/schedule/routine.d.ts +0 -43
  478. package/dist/schedule/routine.js +0 -32
  479. package/dist/schedule/run.d.ts +0 -55
  480. package/dist/schedule/run.js +0 -146
  481. package/dist/secrets-gate.d.ts +0 -32
  482. package/dist/secrets-gate.js +0 -33
  483. package/dist/session-remote.d.ts +0 -47
  484. package/dist/session-remote.js +0 -460
  485. package/dist/session.d.ts +0 -466
  486. package/dist/session.js +0 -57
  487. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
@@ -0,0 +1,254 @@
1
+ /**
2
+ * The model lists a pi-ai release does not carry yet. pi publishes the catalog of every built-in provider at pi.dev;
3
+ * when it is newer than the data this pi-ai was built with, each built-in provider lists its models too, so a model a
4
+ * provider releases can be named without a FastAgent release. A provider that fetches its own list (radius, a provider
5
+ * in code with `fetchModels`) is refreshed by pi-ai, with the credential that needs. Both are kept in the instance's
6
+ * state, so a start without the network, or whose refresh fails, has the lists it last had.
7
+ *
8
+ * Adapted from pi-coding-agent's `remote-catalog-provider` (MIT). Two differences: one request answers for every
9
+ * provider, and the published catalog is fetched whatever this machine authenticates (pi-ai's refresh asks the network
10
+ * only for a provider holding a credential), because a deployed box opens its agent, default model checked, before
11
+ * anyone logs in on it.
12
+ */
13
+ import { readFileSync, rmSync } from "node:fs";
14
+ import { dirname, join } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ import { InMemoryModelsStore, getModelType, isModelType, } from "@earendil-works/pi-ai";
17
+ import { getBuiltinModelDataGeneratedAt } from "@earendil-works/pi-ai/providers/all";
18
+ import { writeFileAtomic } from "../../atomic-write.js";
19
+ const PUBLISHED_URL = "https://pi.dev/api/models";
20
+ /** The model types this pi-ai takes; asked for, since pi.dev answers a client that names none with chat models only. */
21
+ const MODEL_TYPES = ["chat", "image", "classifier"];
22
+ /** How long a fetched catalog counts as current, so a start inside it asks the network nothing. pi's own. */
23
+ const FRESH_MS = 4 * 60 * 60 * 1000;
24
+ const TIMEOUT_MS = 5_000;
25
+ /** `1`: no model list is fetched, and each is the one last kept. */
26
+ const OFFLINE_VARIABLE = "FASTAGENT_OFFLINE";
27
+ /** The catalogs kept in `dir`, or, without one, for this process alone. `url` and `timeoutMs` are for tests. */
28
+ export function catalogsIn(dir, options = {}) {
29
+ const builtAt = getBuiltinModelDataGeneratedAt();
30
+ const timeoutMs = options.timeoutMs ?? TIMEOUT_MS;
31
+ const fetched = dir === undefined ? new InMemoryModelsStore() : providerFiles(join(dir, "providers"));
32
+ const publishedPath = dir === undefined ? undefined : join(dir, "published.json");
33
+ let inMemory;
34
+ let listed = {};
35
+ const show = (kept) => {
36
+ listed = kept && (builtAt === undefined || kept.lastModified > builtAt) ? kept.models : {};
37
+ };
38
+ const keep = (published) => {
39
+ if (publishedPath === undefined)
40
+ inMemory = published;
41
+ // Shared writers: a serve and an `invoke` may refresh at once, and either whole catalog is a good one.
42
+ else
43
+ writeFileAtomic(publishedPath, JSON.stringify(published), undefined, true);
44
+ };
45
+ async function refreshPublished(network, force, signal) {
46
+ const piAi = piAiVersion();
47
+ const found = publishedPath === undefined ? inMemory : readJson(publishedPath);
48
+ const kept = found?.piAi === piAi ? found : undefined;
49
+ show(kept);
50
+ if (!network || (!force && kept && Date.now() - kept.checkedAt < FRESH_MS))
51
+ return;
52
+ try {
53
+ await askPublished(piAi, kept, signal);
54
+ }
55
+ catch (error) {
56
+ // A failed ask counts as one: the catalog kept stays, and is not asked for again for FRESH_MS. With none kept,
57
+ // the next open asks, since a default model may need it.
58
+ if (kept)
59
+ keep({ ...kept, checkedAt: Date.now() });
60
+ throw error;
61
+ }
62
+ }
63
+ async function askPublished(piAi, kept, signal) {
64
+ const url = new URL(options.url ?? PUBLISHED_URL);
65
+ url.searchParams.set("types", MODEL_TYPES.join(","));
66
+ url.searchParams.set("pi-version", piAi);
67
+ const validator = kept?.etag;
68
+ const response = await fetch(url, {
69
+ headers: { accept: "application/json", ...(validator === undefined ? {} : { "if-none-match": validator }) },
70
+ signal,
71
+ });
72
+ if (response.status === 304 && kept) {
73
+ keep({ ...kept, checkedAt: Date.now() });
74
+ return;
75
+ }
76
+ if (response.status === 404 || response.status === 501) {
77
+ // pi.dev publishes no catalog for this pi-ai, so its own lists are the current ones.
78
+ const none = { piAi, models: {}, lastModified: 0, checkedAt: Date.now() };
79
+ keep(none);
80
+ show(none);
81
+ return;
82
+ }
83
+ if (!response.ok)
84
+ throw new Error(`${url.origin} answered ${response.status}`);
85
+ const needs = response.headers.get("x-pi-model-catalog-minimum-version");
86
+ if (needs !== null && olderThan(piAi, needs)) {
87
+ throw new Error(`${url.origin} answered a catalog for pi-ai ${needs} or later, and this is ${piAi}`);
88
+ }
89
+ const lastModified = Date.parse(response.headers.get("last-modified") ?? "");
90
+ const etag = response.headers.get("etag");
91
+ const published = {
92
+ piAi,
93
+ models: parseCatalog(await response.json()),
94
+ lastModified: Number.isNaN(lastModified) ? 0 : lastModified,
95
+ ...(etag === null ? {} : { etag }),
96
+ checkedAt: Date.now(),
97
+ };
98
+ keep(published);
99
+ show(published);
100
+ }
101
+ return {
102
+ over(provider) {
103
+ if (provider.refreshModels !== undefined)
104
+ return provider;
105
+ const added = () => listed[provider.id] ?? [];
106
+ return {
107
+ ...provider,
108
+ getModels: () => merged(provider.getModels(), added().filter((model) => isModelType(model, "chat"))),
109
+ getAllModels: () => merged(provider.getAllModels?.() ?? provider.getModels(), added()),
110
+ };
111
+ },
112
+ fetched,
113
+ async refresh(models, { force = false } = {}) {
114
+ const offline = process.env[OFFLINE_VARIABLE] === "1";
115
+ const signal = AbortSignal.timeout(timeoutMs);
116
+ const why = (error) => signal.aborted ? `no answer within ${timeoutMs / 1000}s` : messageOf(error);
117
+ const [published, own] = await Promise.all([
118
+ refreshPublished(!offline, force, signal).then(() => undefined, (error) => `pi's published catalog: ${why(error)}`),
119
+ refreshOwn(models, !offline, force, signal),
120
+ ]);
121
+ const problems = [
122
+ ...(offline && force ? [`${OFFLINE_VARIABLE}=1 is set`] : []),
123
+ ...(published === undefined ? [] : [published]),
124
+ ...[...own.errors].map(([provider, error]) => `${provider}: ${messageOf(error)}`),
125
+ ...(own.aborted ? [`the providers that fetch their own: ${why(undefined)}`] : []),
126
+ ];
127
+ if (problems.length === 0)
128
+ return undefined;
129
+ const skip = offline ? "" : `; ${OFFLINE_VARIABLE}=1 skips the refresh`;
130
+ return `model lists not refreshed (${problems.join("; ")}): each lists what it last had${skip}`;
131
+ },
132
+ };
133
+ /**
134
+ * The providers that fetch their own list, each asked only once what it last fetched is {@link FRESH_MS} old: pi-ai
135
+ * asks every one of them each refresh it allows the network, and keeps when it last did beside the list.
136
+ */
137
+ async function refreshOwn(models, network, force, signal) {
138
+ // An entry it cannot read counts as none: pi-ai's refresh reads it again, and reports why it could not.
139
+ const keptOf = (id) => fetched.read(id).then((entry) => entry, () => undefined);
140
+ const own = models.getProviders().filter((provider) => provider.refreshModels !== undefined);
141
+ const stale = new Map();
142
+ const fresh = [];
143
+ for (const { id } of own) {
144
+ const kept = await keptOf(id);
145
+ const checkedAt = kept?.checkedAt;
146
+ if (force || checkedAt === undefined || Date.now() - checkedAt >= FRESH_MS)
147
+ stale.set(id, kept);
148
+ else
149
+ fresh.push(id);
150
+ }
151
+ const [restored, asked] = await Promise.all([
152
+ models.refresh({ allowNetwork: false, providers: fresh, signal }),
153
+ models.refresh({ allowNetwork: network, providers: [...stale.keys()], force, signal }),
154
+ ]);
155
+ // A failed ask counts as one, as for the published catalog: pi-ai replaces an entry only when it fetched a list, so
156
+ // one it left as it was is a list asked for and kept, not asked for again for FRESH_MS.
157
+ const unwritten = new Map();
158
+ for (const [id, kept] of network ? stale : []) {
159
+ if (kept === undefined || (await keptOf(id))?.checkedAt !== kept.checkedAt)
160
+ continue;
161
+ await fetched
162
+ .write(id, { ...kept, checkedAt: Date.now() })
163
+ .catch((error) => void unwritten.set(id, error));
164
+ }
165
+ return {
166
+ aborted: restored.aborted || asked.aborted,
167
+ errors: new Map([...restored.errors, ...asked.errors, ...unwritten]),
168
+ };
169
+ }
170
+ }
171
+ /** The pi-ai this process runs, from its own package.json, which its exports do not reach. */
172
+ function piAiVersion() {
173
+ let dir = dirname(fileURLToPath(import.meta.resolve("@earendil-works/pi-ai")));
174
+ for (;;) {
175
+ const pkg = readJson(join(dir, "package.json"));
176
+ if (pkg?.name === "@earendil-works/pi-ai" && typeof pkg.version === "string")
177
+ return pkg.version;
178
+ const parent = dirname(dir);
179
+ if (parent === dir)
180
+ throw new Error("no package.json of @earendil-works/pi-ai above its entry");
181
+ dir = parent;
182
+ }
183
+ }
184
+ /** Whether release `version` is older than `than`, by `major.minor.patch`. */
185
+ function olderThan(version, than) {
186
+ const parts = (release) => {
187
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(release);
188
+ if (match === null)
189
+ throw new Error(`"${release}" is not a version`);
190
+ return match.slice(1).map(Number);
191
+ };
192
+ const [ours, theirs] = [parts(version), parts(than)];
193
+ for (const [index, part] of ours.entries()) {
194
+ const other = theirs[index];
195
+ if (part !== other)
196
+ return part < other;
197
+ }
198
+ return false;
199
+ }
200
+ /** Published models override built-in ones of the same type and id, as in pi. */
201
+ function merged(builtIn, published) {
202
+ const byKey = new Map();
203
+ for (const model of [...builtIn, ...published])
204
+ byKey.set(`${getModelType(model)}\0${model.id}`, model);
205
+ return [...byKey.values()];
206
+ }
207
+ /** pi.dev's answer: each provider's models, as a list (with `types`) or keyed by id (without). */
208
+ function parseCatalog(value) {
209
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
210
+ throw new Error("the answer is not a model catalog");
211
+ }
212
+ const catalog = {};
213
+ for (const [provider, models] of Object.entries(value)) {
214
+ const entries = Array.isArray(models) ? models : typeof models === "object" ? Object.values(models) : [];
215
+ catalog[provider] = entries
216
+ .filter((entry) => typeof entry === "object" &&
217
+ entry !== null &&
218
+ typeof entry.id === "string" &&
219
+ [undefined, ...MODEL_TYPES].includes(entry.type))
220
+ .map((model) => ({ ...model, provider }));
221
+ }
222
+ return catalog;
223
+ }
224
+ /** One file per provider: each is written by its own refresh, so none waits on another's lock. */
225
+ function providerFiles(dir) {
226
+ const path = (providerId) => join(dir, `${encodeURIComponent(providerId)}.json`);
227
+ return {
228
+ read: async (providerId) => readJson(path(providerId)),
229
+ write: async (providerId, entry) => writeFileAtomic(path(providerId), JSON.stringify(entry), undefined, true),
230
+ delete: async (providerId) => rmSync(path(providerId), { force: true }),
231
+ };
232
+ }
233
+ function readJson(path) {
234
+ let text;
235
+ try {
236
+ text = readFileSync(path, "utf8");
237
+ }
238
+ catch (error) {
239
+ if (error.code === "ENOENT")
240
+ return undefined;
241
+ throw error;
242
+ }
243
+ try {
244
+ return JSON.parse(text);
245
+ }
246
+ catch {
247
+ throw new Error(`${path} is not JSON: delete it, and the next refresh fetches it again`);
248
+ }
249
+ }
250
+ /** pi-ai and `fetch` put the reason in the cause ("fetch failed", "Model refresh failed for radius"). */
251
+ function messageOf(error) {
252
+ const cause = error.cause instanceof Error ? `: ${error.cause.message}` : "";
253
+ return `${error.message}${cause}`;
254
+ }
@@ -11,8 +11,8 @@ export interface FastagentAuthOptions {
11
11
  /**
12
12
  * Where a caller's model credentials live: fastagent's JSON file (`authPath`, or the default layers when neither is
13
13
  * given), or a {@link CredentialStore} the caller owns (an OS keychain, Electron `safeStorage`). A supplied store
14
- * replaces the FILES: none is read or written, and no global layer applies. Env variables and `models.json` keys still
15
- * apply, below a stored credential, exactly as with `authPath`. The store owns what the file store does
14
+ * replaces the FILES: none is read or written, and no global layer applies. Env variables and a provider's own auth
15
+ * still apply, below a stored credential, exactly as with `authPath`. The store owns what the file store does
16
16
  * for its file: `modify` for one provider must run one at a time, because an OAuth refresh happens inside it and a
17
17
  * rotated refresh token must never be used twice; and a store it cannot parse must not be answered as empty, or the
18
18
  * next write replaces every provider's credential.
@@ -45,7 +45,7 @@ export type FastagentCredentialStore = CredentialStore & {
45
45
  * to avoid. A provider present in neither layer is new, and new credentials belong to the primary.
46
46
  *
47
47
  * The fallback also yields to `projectAuthenticates`: a provider the project authenticates some other way (an env key,
48
- * its own models.json) never reaches it — for reading, listing or writing back.
48
+ * a provider in its config that authenticates itself) never reaches it — for reading, listing or writing back.
49
49
  */
50
50
  export declare function fastagentCredentialStore(path?: string, options?: FastagentAuthOptions & {
51
51
  fallbackPath?: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Auth for the pi engine: a read-WRITE {@link CredentialStore} over a fastagent credentials file, consumed by the
2
+ * Auth for the pi harness: a read-WRITE {@link CredentialStore} over a fastagent credentials file, consumed by the
3
3
  * `Models` collection (models.ts), and which of those files an agent reads. The write path refuses to overwrite a
4
4
  * corrupt file, so a torn read never clobbers the other providers' credentials.
5
5
  */
@@ -7,7 +7,7 @@ import { existsSync, readFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { globalHome, SECRETS_DIRNAME, resolveOverridePath, resolveSecretsDir } from "../../paths.js";
9
9
  import { log } from "../../log.js";
10
- import { withLockedFile } from "./locked-file.js";
10
+ import { withLockedFile } from "../../locked-file.js";
11
11
  /**
12
12
  * The GLOBAL fastagent credentials file (distinct from pi's `~/.pi`), under the user-global machinery home
13
13
  * `~/.fastagent/`.
@@ -80,7 +80,7 @@ function parseForWrite(raw, where) {
80
80
  * to avoid. A provider present in neither layer is new, and new credentials belong to the primary.
81
81
  *
82
82
  * The fallback also yields to `projectAuthenticates`: a provider the project authenticates some other way (an env key,
83
- * its own models.json) never reaches it — for reading, listing or writing back.
83
+ * a provider in its config that authenticates itself) never reaches it — for reading, listing or writing back.
84
84
  */
85
85
  export function fastagentCredentialStore(path, options = {}) {
86
86
  const warn = options.warn ?? ((message) => log.warn(message));
@@ -0,0 +1,48 @@
1
+ import type { Credential, CredentialStore, Models, Provider } from "@earendil-works/pi-ai";
2
+ import { type AuthLayers, type CredentialSourceOptions, type FastagentAuthOptions } from "./credentials.ts";
3
+ /** What authenticates one provider for an agent ({@link ModelEnvironment.authStatus}). */
4
+ interface AuthStatus {
5
+ /** What satisfies it now (`OAuth`, `stored credential`, an env variable's name), after a due refresh. */
6
+ source?: string;
7
+ /** Why resolving it failed (a corrupt entry, a refresh the provider refused), when it did. */
8
+ error?: string;
9
+ /** The file that holds the stored credential, or the one a login would write; absent over a caller's store. */
10
+ path?: string;
11
+ /** The kind of credential the store holds for it, usable or not. */
12
+ stored?: Credential["type"];
13
+ /**
14
+ * An env variable that also authenticates it but goes unused, because a stored credential owns its provider: a key
15
+ * added to a deployment's value file after the box was logged in, typically.
16
+ */
17
+ shadowed?: string;
18
+ }
19
+ export interface ModelEnvironment {
20
+ /** The credentials files behind {@link credentials}; absent when the caller supplied its own store. */
21
+ auth?: AuthLayers;
22
+ credentials: CredentialStore;
23
+ /** pi's built-ins and the definition's providers, over {@link credentials}. */
24
+ models: Models;
25
+ /** Why a model list is not current, when one is not (logged already): each lists what it last had. */
26
+ catalogProblem?: string;
27
+ /**
28
+ * What authenticates `provider` here, resolved exactly as a message resolves it: the one answer the startup report
29
+ * and a deployed box's `login --if-missing` both give. `modelId` picks the model to probe with; the provider's first
30
+ * one otherwise (auth is provider-scoped).
31
+ */
32
+ authStatus(provider: string, modelId?: string): Promise<AuthStatus>;
33
+ }
34
+ /**
35
+ * The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: the named or global
36
+ * credentials file). A supplied `credentialStore` is used as given; otherwise the files: the directory's own layers
37
+ * ({@link resolveAuthLayers}), or the named file, or the global one. `providers` are the definition's. `environment` is
38
+ * what the agent's environment adds to this process's: its keys, the requests' settings, and where its files are
39
+ * (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) are all read there first. The model lists are refreshed before it
40
+ * resolves, kept in the instance's state root (`stateRoot`, else the directory's); `force` asks the network even when
41
+ * they are fresh.
42
+ */
43
+ export declare function modelEnvironment(agentDir: string | undefined, source?: CredentialSourceOptions & FastagentAuthOptions, providers?: readonly Provider[], options?: {
44
+ force?: boolean;
45
+ stateRoot?: string;
46
+ environment?: Readonly<Record<string, string>>;
47
+ }): Promise<ModelEnvironment>;
48
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * An agent's MODEL ENVIRONMENT as one value: the credential store its messages authenticate through, the models they
3
+ * resolve against (models.ts) with their lists refreshed (catalog.ts), and what authenticates a provider there. Every
4
+ * reader builds it here (the serve, the startup report, a deployed box's `login --if-missing`, `info`, `login`, the
5
+ * model list, `deploy`'s pre-flight), so none can read other credential layers, another registry, or older lists than
6
+ * the Agent does.
7
+ */
8
+ import { join } from "node:path";
9
+ import { log } from "../../log.js";
10
+ import { resolveStateRoot } from "../../paths.js";
11
+ import { catalogsIn } from "./catalog.js";
12
+ import { GLOBAL_AUTH_PATH, assertOneCredentialSource, fastagentCredentialStore, resolveAuthLayers, } from "./credentials.js";
13
+ import { environmentAuth, modelsOver, probeAuthSource } from "./models.js";
14
+ /**
15
+ * The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: the named or global
16
+ * credentials file). A supplied `credentialStore` is used as given; otherwise the files: the directory's own layers
17
+ * ({@link resolveAuthLayers}), or the named file, or the global one. `providers` are the definition's. `environment` is
18
+ * what the agent's environment adds to this process's: its keys, the requests' settings, and where its files are
19
+ * (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) are all read there first. The model lists are refreshed before it
20
+ * resolves, kept in the instance's state root (`stateRoot`, else the directory's); `force` asks the network even when
21
+ * they are fresh.
22
+ */
23
+ export async function modelEnvironment(agentDir, source = {}, providers = [], options = {}) {
24
+ const environment = options.environment ?? {};
25
+ const env = { ...process.env, ...environment };
26
+ assertOneCredentialSource(source);
27
+ const files = source.credentialStore
28
+ ? undefined
29
+ : agentDir
30
+ ? resolveAuthLayers(agentDir, source.authPath, env)
31
+ : { path: source.authPath ?? GLOBAL_AUTH_PATH };
32
+ const store = files === undefined ? undefined : layeredStore(files, providers, source.warn, environment);
33
+ const credentials = source.credentialStore ?? store;
34
+ const stateRoot = options.stateRoot ?? (agentDir === undefined ? undefined : resolveStateRoot(agentDir, env));
35
+ const catalogs = catalogsIn(stateRoot === undefined ? undefined : join(stateRoot, "models"));
36
+ const models = modelsOver(credentials, providers, catalogs, environment);
37
+ const catalogProblem = await catalogs.refresh(models, { force: options.force === true });
38
+ if (catalogProblem !== undefined)
39
+ log.warn(`[fastagent] ${catalogProblem}`);
40
+ return {
41
+ ...(files ? { auth: files } : {}),
42
+ credentials,
43
+ models,
44
+ ...(catalogProblem === undefined ? {} : { catalogProblem }),
45
+ async authStatus(provider, modelId) {
46
+ const id = modelId ?? models.getProvider(provider)?.getModels()[0]?.id;
47
+ let resolved = {};
48
+ if (id !== undefined) {
49
+ try {
50
+ const found = await probeAuthSource(models, `${provider}/${id}`);
51
+ if (found !== undefined)
52
+ resolved = { source: found };
53
+ }
54
+ catch (error) {
55
+ // The first line: pi folds the request's stack into the message of a failed refresh.
56
+ resolved = { error: String(error.message).split("\n")[0] };
57
+ }
58
+ }
59
+ // `read` never refreshes: this is the kind on file, whatever became of it.
60
+ const stored = (await credentials.read(provider))?.type;
61
+ const fromEnvironment = stored === undefined ? undefined : (await environmentAuth(provider, env, undefined, providers))?.source;
62
+ const shadowed = resolved.source !== undefined && fromEnvironment !== resolved.source ? fromEnvironment : undefined;
63
+ return {
64
+ ...(store ? { path: await store.layerOf(provider) } : {}),
65
+ ...resolved,
66
+ ...(stored !== undefined ? { stored } : {}),
67
+ ...(shadowed !== undefined ? { shadowed } : {}),
68
+ };
69
+ },
70
+ };
71
+ }
72
+ /**
73
+ * The credential store an agent reads through: its own file, then the user-global one for a provider the PROJECT
74
+ * authenticates no other way (its own file, a provider in code that needs no key, or the environment). A deployment
75
+ * never has the global file, so a global login that outranked, say, an `ANTHROPIC_API_KEY` in `.secrets/.env` would
76
+ * run one credential here and a different one deployed.
77
+ */
78
+ function layeredStore(auth, providers, warn, environment) {
79
+ const own = () => fastagentCredentialStore(auth.path, { ...(warn ? { warn } : {}) });
80
+ if (auth.fallback === undefined)
81
+ return own();
82
+ const project = modelsOver(own(), providers, undefined, environment);
83
+ return fastagentCredentialStore(auth.path, {
84
+ ...(warn ? { warn } : {}),
85
+ fallbackPath: auth.fallback,
86
+ projectAuthenticates: async (providerId) => (await project.checkAuth(providerId)) !== undefined,
87
+ });
88
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * What an embedding host calls for model credentials (sign-in-api.ts has the types): a store kept where the host
3
+ * chooses, a sign-in in its own UI, and what authenticates a provider. Thin over what `fastagent login`, `logout` and
4
+ * `auth check` run, so each rule keeps one implementation. The one translation between FastAgent's types and pi-ai's
5
+ * is here, written without casts and with every function checked as a function, so it stops compiling when the two
6
+ * drift apart.
7
+ */
8
+ import type { CredentialStore as PiCredentialStore } from "@earendil-works/pi-ai";
9
+ import type { CredentialStore, ProviderAuth, SignInOption, SignInRequest, SignInResult } from "../../sign-in-api.ts";
10
+ /**
11
+ * FastAgent's locked JSON credentials file at `path` (`~` is the home directory, a relative one resolves against the
12
+ * working directory now). A corrupt or unreadable file rejects every call.
13
+ */
14
+ export declare function credentialFile(path: string): CredentialStore;
15
+ /** A host's store as pi-ai reads it. */
16
+ export declare function piCredentials(store: CredentialStore): PiCredentialStore;
17
+ /** Every sign-in of the built-in providers, by name, with what authenticates each one now. */
18
+ export declare function signInOptions(credentials: CredentialStore): Promise<SignInOption[]>;
19
+ /**
20
+ * Sign in to a built-in provider and store the credential. The store is written to first, so a sign-in never runs
21
+ * toward a store that refuses it; an API key is checked with one request before it is stored, and one the provider
22
+ * rejects is asked for again. Aborting the interaction's signal rejects with `SignInCancelled` and stores nothing.
23
+ */
24
+ export declare function signIn(request: SignInRequest): Promise<SignInResult>;
25
+ /**
26
+ * What a request to a built-in provider authenticates with, resolved as a message resolves it: a stored credential
27
+ * (refreshing an OAuth token that is due, through the store's `modify`), else the environment. Undefined when nothing
28
+ * does; rejects when resolving fails, such as a refresh the provider refused.
29
+ */
30
+ export declare function providerAuth(credentials: CredentialStore, provider: string): Promise<ProviderAuth | undefined>;
@@ -0,0 +1,97 @@
1
+ import { resolveOverridePath } from "../../paths.js";
2
+ import { fastagentCredentialStore } from "./credentials.js";
3
+ import { loginOptionsOver, loginOver } from "./login.js";
4
+ import { modelsOver } from "./models.js";
5
+ /** The file behind each store {@link credentialFile} made: a sign-in keeps the installation's id beside it. */
6
+ const files = new WeakMap();
7
+ /**
8
+ * FastAgent's locked JSON credentials file at `path` (`~` is the home directory, a relative one resolves against the
9
+ * working directory now). A corrupt or unreadable file rejects every call.
10
+ */
11
+ export function credentialFile(path) {
12
+ // Resolved once, so the credentials and the installation's id beside them stay in one directory.
13
+ const resolved = resolveOverridePath(path);
14
+ if (resolved === undefined)
15
+ throw new Error("credentialFile needs the path of a credentials file");
16
+ const file = fastagentCredentialStore(resolved, {
17
+ warn: (message) => {
18
+ throw new Error(message);
19
+ },
20
+ });
21
+ const store = {
22
+ read: (provider) => file.read(provider),
23
+ list: async () => (await file.list()).map(({ providerId, type }) => ({ provider: providerId, type })),
24
+ modify: (provider, change) => file.modify(provider, change),
25
+ delete: (provider) => file.delete(provider),
26
+ };
27
+ files.set(store, resolved);
28
+ return store;
29
+ }
30
+ /** A host's store as pi-ai reads it. */
31
+ export function piCredentials(store) {
32
+ return {
33
+ read: (provider) => store.read(provider),
34
+ list: async () => (await store.list()).map(({ provider, type }) => ({ providerId: provider, type })),
35
+ modify: (provider, change) => store.modify(provider, change),
36
+ delete: (provider) => store.delete(provider),
37
+ };
38
+ }
39
+ /**
40
+ * A host's interaction as pi-ai drives it. Arrow functions, not the object as it is: both declare `prompt` and
41
+ * `notify` as methods, whose parameters TypeScript checks in either direction, so a kind of prompt or event pi-ai adds
42
+ * would pass unseen; a call checks it.
43
+ */
44
+ function piInteraction(interaction) {
45
+ return {
46
+ ...(interaction.signal === undefined ? {} : { signal: interaction.signal }),
47
+ prompt: (prompt) => interaction.prompt(prompt),
48
+ notify: (event) => interaction.notify(event),
49
+ };
50
+ }
51
+ /** Every sign-in of the built-in providers, by name, with what authenticates each one now. */
52
+ export function signInOptions(credentials) {
53
+ return loginOptionsOver(piCredentials(credentials));
54
+ }
55
+ /**
56
+ * Sign in to a built-in provider and store the credential. The store is written to first, so a sign-in never runs
57
+ * toward a store that refuses it; an API key is checked with one request before it is stored, and one the provider
58
+ * rejects is asked for again. Aborting the interaction's signal rejects with `SignInCancelled` and stores nothing.
59
+ */
60
+ export function signIn(request) {
61
+ const { credentials, provider, method, interaction, deviceId } = request;
62
+ const file = files.get(credentials);
63
+ return loginOver({
64
+ provider,
65
+ method,
66
+ interaction: piInteraction(interaction),
67
+ ...(file === undefined ? { credentialStore: piCredentials(credentials) } : { authPath: file }),
68
+ ...(deviceId === undefined ? {} : { getDeviceId: () => deviceId }),
69
+ });
70
+ }
71
+ /**
72
+ * What a request to a built-in provider authenticates with, resolved as a message resolves it: a stored credential
73
+ * (refreshing an OAuth token that is due, through the store's `modify`), else the environment. Undefined when nothing
74
+ * does; rejects when resolving fails, such as a refresh the provider refused.
75
+ */
76
+ export async function providerAuth(credentials, provider) {
77
+ const models = modelsOver(piCredentials(credentials));
78
+ if (models.getProvider(provider) === undefined)
79
+ throw new Error(`unknown provider "${provider}"`);
80
+ const checked = await models.checkAuth(provider);
81
+ if (checked === undefined)
82
+ return undefined;
83
+ const resolved = await models.getAuth(provider);
84
+ if (resolved === undefined)
85
+ return undefined;
86
+ const source = resolved.source ?? checked.source;
87
+ const { apiKey, baseUrl } = resolved.auth;
88
+ // pi's null header is one a request leaves out, which is what a header not given does.
89
+ const headers = Object.entries(resolved.auth.headers ?? {}).flatMap(([name, value]) => value === null ? [] : [[name, value]]);
90
+ return {
91
+ type: checked.type,
92
+ ...(source === undefined ? {} : { source }),
93
+ ...(apiKey === undefined ? {} : { apiKey }),
94
+ ...(headers.length === 0 ? {} : { headers: Object.fromEntries(headers) }),
95
+ ...(baseUrl === undefined ? {} : { baseUrl }),
96
+ };
97
+ }