@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
@@ -1,44 +1,36 @@
1
1
  /**
2
- * Signing in to a MODEL PROVIDER, for any front end: {@link login} runs one provider's flow over pi-ai's own
2
+ * Signing in to a MODEL PROVIDER, for any front end: {@link loginOver} runs one provider's flow over pi-ai's own
3
3
  * `AuthInteraction` and persists the credential through the same {@link fastagentCredentialStore} the runtime reads
4
- * (one writer, one lock/corruption semantics); {@link loginOptions} is the list a picker offers. `fastagent login` is
5
- * one front end ({@link loginFlow}: terminal menus, then {@link login}); a GUI client is another.
4
+ * (one writer, one lock/corruption semantics); {@link loginOptionsOver} is the list a picker offers. An embedding host
5
+ * reaches both through host.ts (`signIn`, `signInOptions`); `fastagent login` is a front end of its own
6
+ * ({@link loginFlow}: terminal menus, then {@link loginOver}); `login --deployment` drives the same flow on a deployed
7
+ * box over a relay (cli/login-relay.ts).
6
8
  */
7
9
  import { type AuthInteraction, type CredentialStore, type Provider } from "@earendil-works/pi-ai";
8
- export type LoginMethod = "oauth" | "api_key";
9
- /** The user backed out of a prompt/menu, or the flow was aborted — a decision, not a failure. */
10
- export declare class LoginCancelled extends Error {
11
- }
12
- /** One provider sign-in a picker can offer. */
13
- export interface LoginOption {
14
- provider: string;
15
- method: LoginMethod;
16
- /** The method's own name, e.g. "Anthropic (Claude Pro/Max)" or "Anthropic API key". */
17
- label: string;
18
- /** An OAuth login backed by a provider subscription. */
19
- subscription: boolean;
20
- /**
21
- * What the store holds for this provider now. It keeps ONE credential per provider, so signing in with the other
22
- * method replaces it — worth a warning in a client.
23
- */
24
- stored?: LoginMethod;
10
+ import { type SignInMethod, type SignInOption, type SignInResult } from "../../sign-in-api.ts";
11
+ /** A sign-in that cannot start as asked: an unknown provider, or a choice a caller without menus did not make. */
12
+ export declare class LoginRefused extends Error {
13
+ /** The sign-ins it could be instead, for a caller to choose from. */
14
+ readonly choices: readonly SignInOption[];
15
+ constructor(message: string, choices: readonly SignInOption[]);
25
16
  }
26
17
  /**
27
- * Every interactive sign-in of pi's built-in providers, with what `credentials` (a credentials file, or the caller's
28
- * own store) already holds for each. A provider whose key can only come from its env var offers none and is left out.
18
+ * Every sign-in of pi's built-in providers plus `providers` (a same id replaces one), sorted by name as pi's `/login`
19
+ * lists them, with what authenticates each one here: a credential `credentials` (a credentials file, or a store) holds,
20
+ * else this process's environment, which a stored credential outranks.
29
21
  */
30
- export declare function loginOptions(credentials: string | CredentialStore): Promise<LoginOption[]>;
31
- /** {@link loginOptions} over pi's built-ins plus `providers` (a same id replaces one). Not public: a test seam. */
32
- export declare function loginOptionsOver(credentials: string | CredentialStore, providers?: readonly Provider[]): Promise<LoginOption[]>;
22
+ export declare function loginOptionsOver(credentials: string | CredentialStore, providers?: readonly Provider[]): Promise<SignInOption[]>;
23
+ /** An option's status as pi's picker writes it after the provider's name. */
24
+ export declare function loginStatusText(option: SignInOption): string;
33
25
  export type LoginRequest = {
34
26
  /**
35
- * Stable installation identity (Sign in with ChatGPT requires one). With `authPath` it defaults to Pi settings
36
- * beside that file (`settings.json`, written before the flow runs); over a caller's `credentialStore` there is no
37
- * file to keep it beside, so a flow that needs one fails unless this supplies it.
27
+ * Stable installation identity (Sign in with ChatGPT requires one). With `authPath` it defaults to `device.json`
28
+ * beside that file, written before the flow runs; over a caller's `credentialStore` there is no file to keep it
29
+ * beside, so a flow that needs one fails unless this supplies it.
38
30
  */
39
31
  getDeviceId?: () => string;
40
32
  provider: string;
41
- method: LoginMethod;
33
+ method: SignInMethod;
42
34
  /** pi-ai's own interaction: prompts (`text`, `secret`, `select`, `manual_code`) and events (`auth_url`, …). */
43
35
  interaction: AuthInteraction;
44
36
  } & ({
@@ -50,15 +42,23 @@ export type LoginRequest = {
50
42
  credentialStore: CredentialStore;
51
43
  authPath?: never;
52
44
  });
53
- export interface LoginResult {
54
- provider: string;
55
- method: LoginMethod;
56
- /** An API key checked with one minimal request: accepted, or not decidable (kept). OAuth needs no check. */
57
- verified: "ok" | "unknown" | "n/a";
45
+ /** What the CLI adds to a sign-in. */
46
+ export interface LoginInternals {
47
+ /**
48
+ * The definition's providers, added to pi's built-ins (a same id replaces one): a key is verified against the
49
+ * endpoint its turns will reach, which a provider in code can point at a gateway.
50
+ */
51
+ providers?: readonly Provider[];
52
+ /**
53
+ * A model spec to verify an entered key with, when the registry has it as one of this provider's models: the
54
+ * first-run picker's choice, the request the agent is about to make. Otherwise pi's default model for the provider
55
+ * is used, then its first. On one registry, any of a provider's models answers the 401 question.
56
+ */
57
+ verifyWith?: string;
58
58
  }
59
59
  /**
60
- * Sign in to one of pi's built-in providers and persist the credential to `authPath`, or to the caller's
61
- * `credentialStore`.
60
+ * Sign in to one of pi's built-in providers (plus the definition's) and persist the credential to `authPath`, or to
61
+ * the caller's `credentialStore`.
62
62
  *
63
63
  * The store is checked FIRST (a no-op write runs the file store's refuse-corrupt and writability checks, or whatever
64
64
  * the caller's store does on a write), so a flow never runs toward a credential that could not be saved. An entered API key is verified with one minimal request to pi's
@@ -66,32 +66,13 @@ export interface LoginResult {
66
66
  * written. Credentials are provider-scoped, so any of the provider's models answers the question the check asks (does
67
67
  * the provider refuse the key, HTTP 401); a key the provider rejects is never stored, and the provider's key flow runs
68
68
  * again (the provider and method were not the mistake). Progress and outcome go out through `interaction.notify`.
69
- * A provider an agent's models.json points elsewhere is not this call's: store that key with `fastagentCredentialStore`.
70
69
  *
71
- * Aborting `interaction.signal` at any point, the verification included, rejects with {@link LoginCancelled} and
72
- * writes no credential (an OAuth device ID, the installation's identity, is kept once created). Every prompt carries a signal that aborts on it, on the prompt's own signal (a callback server
73
- * beating a `manual_code` prompt), and when the flow settles with a prompt still pending.
70
+ * Aborting `interaction.signal` at any point, the verification included, rejects with {@link SignInCancelled} and
71
+ * writes no credential (an OAuth device ID, the installation's identity, is kept once created). Every prompt carries a
72
+ * signal that aborts on it, on the prompt's own signal (a callback server beating a `manual_code` prompt), and when the
73
+ * flow settles with a prompt still pending.
74
74
  */
75
- export declare function login(request: LoginRequest): Promise<LoginResult>;
76
- /** Not public: what the CLI and the tests add to {@link login}. */
77
- export interface LoginInternals {
78
- /** Providers added to pi's built-ins (a same id replaces one): a test seam. */
79
- providers?: readonly Provider[];
80
- /**
81
- * The agent the key is for. Verification then runs on THAT agent's registry (its models.json and the machine's over
82
- * the built-ins), so the check reaches the endpoint its turns will: a models.json can point a built-in provider at a
83
- * gateway, where the provider's own endpoint would refuse the gateway's key and loop the person through re-entry.
84
- */
85
- agentDir?: string;
86
- /**
87
- * A model spec to verify an entered key with, when the registry has it as one of this provider's models: the
88
- * first-run picker's choice, the request the agent is about to make. Otherwise pi's default model for the provider
89
- * is used, then its first. On one registry, any of a provider's models answers the 401 question.
90
- */
91
- verifyWith?: string;
92
- }
93
- /** {@link login}, plus {@link LoginInternals}. */
94
- export declare function loginOver(request: LoginRequest, internals?: LoginInternals): Promise<LoginResult>;
75
+ export declare function loginOver(request: LoginRequest, internals?: LoginInternals): Promise<SignInResult>;
95
76
  /**
96
77
  * pi's default model per provider (`defaultModelPerProvider` in pi-coding-agent's model-resolver): the general-purpose
97
78
  * model pi itself starts on, so the likeliest to answer a minimal request. COPIED, because pi does not export it;
@@ -108,28 +89,43 @@ export interface IoOption {
108
89
  export interface LoginIO {
109
90
  /** Single-choice picker. */
110
91
  select(message: string, options: IoOption[]): Promise<string | undefined>;
111
- /** Free-text or hidden input. */
92
+ /**
93
+ * Free-text or hidden input. `redirect`: what is asked is the address the browser ended on after signing in, or the
94
+ * code its page showed (pi-ai's `manual_code`).
95
+ */
112
96
  prompt(message: string, opts?: {
113
97
  hidden?: boolean;
98
+ redirect?: boolean;
114
99
  signal?: AbortSignal;
115
100
  }): Promise<string | undefined>;
116
101
  /** Print an informational line (auth URL, device code, progress). */
117
102
  note(message: string): void;
118
- /** Best-effort open a URL in the browser (printed regardless). */
119
- openUrl(url: string): void;
103
+ /** Best-effort open a URL in the browser (printed regardless); `userCode` is what a device sign-in enters there. */
104
+ openUrl(url: string, userCode?: string): void;
120
105
  }
121
106
  /**
122
- * `fastagent login`: ask only what was not given (method, then provider, from {@link loginOptions} with each one's
123
- * stored credential), then {@link login}.
107
+ * `fastagent login` in a terminal: ask only what was not given, as pi's `/login` asks it (how to sign in, then the
108
+ * provider; a provider named by its id or its name, then how, when it offers both), then {@link loginOver}.
124
109
  */
125
110
  export declare function loginFlow(io: LoginIO, options: {
126
111
  authPath: string;
127
112
  provider?: string;
128
- method?: LoginMethod;
129
- providers?: Provider[];
113
+ method?: SignInMethod;
114
+ providers?: readonly Provider[];
130
115
  signal?: AbortSignal;
116
+ /**
117
+ * Where the menus read what authenticates each provider now: the credentials the agent reads (its file over the
118
+ * global one), which can hold more than the file a sign-in writes. Default that file.
119
+ */
120
+ statusFrom?: CredentialStore;
131
121
  /** {@link LoginInternals.verifyWith}. */
132
122
  verifyWith?: string;
133
- /** {@link LoginInternals.agentDir}. */
134
- agentDir?: string;
135
- }): Promise<LoginResult>;
123
+ }): Promise<SignInResult>;
124
+ /**
125
+ * The sign-in `given` names, or, with `menus`, the one a person picks; without them a choice not given refuses
126
+ * ({@link LoginRefused}) naming how to give it.
127
+ */
128
+ export declare function chooseLogin(offered: readonly SignInOption[], given: {
129
+ provider?: string;
130
+ method?: SignInMethod;
131
+ }, menus: LoginIO | undefined): Promise<SignInOption>;
@@ -0,0 +1,439 @@
1
+ /**
2
+ * Signing in to a MODEL PROVIDER, for any front end: {@link loginOver} runs one provider's flow over pi-ai's own
3
+ * `AuthInteraction` and persists the credential through the same {@link fastagentCredentialStore} the runtime reads
4
+ * (one writer, one lock/corruption semantics); {@link loginOptionsOver} is the list a picker offers. An embedding host
5
+ * reaches both through host.ts (`signIn`, `signInOptions`); `fastagent login` is a front end of its own
6
+ * ({@link loginFlow}: terminal menus, then {@link loginOver}); `login --deployment` drives the same flow on a deployed
7
+ * box over a relay (cli/login-relay.ts).
8
+ */
9
+ import { InMemoryCredentialStore, } from "@earendil-works/pi-ai";
10
+ import { randomUUID } from "node:crypto";
11
+ import { mkdir, readFile } from "node:fs/promises";
12
+ import { dirname, join } from "node:path";
13
+ import { writeFileAtomic } from "../../atomic-write.js";
14
+ import { SignInCancelled } from "../../sign-in-api.js";
15
+ import { assertOneCredentialSource, fastagentCredentialStore } from "./credentials.js";
16
+ import { UNNAMED_AUTH_SOURCE, interactiveAuth, loginProviders, modelsOver, probeApiKey } from "./models.js";
17
+ /** A sign-in that cannot start as asked: an unknown provider, or a choice a caller without menus did not make. */
18
+ export class LoginRefused extends Error {
19
+ /** The sign-ins it could be instead, for a caller to choose from. */
20
+ choices;
21
+ constructor(message, choices) {
22
+ super(message);
23
+ this.choices = choices;
24
+ }
25
+ }
26
+ const ACCOUNT_LABEL = "Sign in with an account";
27
+ const API_KEY_LABEL = "Sign in with an API key";
28
+ /** A credentials file path names fastagent's store over it; a store is used as given. */
29
+ const storeOf = (credentials) => typeof credentials === "string" ? fastagentCredentialStore(credentials) : credentials;
30
+ /**
31
+ * Every sign-in of pi's built-in providers plus `providers` (a same id replaces one), sorted by name as pi's `/login`
32
+ * lists them, with what authenticates each one here: a credential `credentials` (a credentials file, or a store) holds,
33
+ * else this process's environment, which a stored credential outranks.
34
+ */
35
+ export async function loginOptionsOver(credentials, providers) {
36
+ // ONE read of the store: `list` is metadata only, and reading per provider would repeat a corrupt file's warning for
37
+ // every provider.
38
+ const held = new Map((await storeOf(credentials).list()).map((info) => [info.providerId, info.type]));
39
+ // The environment alone, over no store: what authenticates a provider nothing is stored for.
40
+ const environment = modelsOver(new InMemoryCredentialStore(), providers);
41
+ const offered = [];
42
+ for (const provider of loginProviders(providers)) {
43
+ const methods = ["oauth", "api_key"].filter((method) => provider.auth[method === "oauth" ? "oauth" : "apiKey"]);
44
+ if (methods.length === 0)
45
+ continue;
46
+ const stored = held.get(provider.id);
47
+ let ambient;
48
+ let problem;
49
+ try {
50
+ const found = await environment.checkAuth(provider.id);
51
+ if (found !== undefined)
52
+ ambient = { type: found.type, source: found.source ?? UNNAMED_AUTH_SOURCE };
53
+ }
54
+ catch (error) {
55
+ problem = error.message;
56
+ }
57
+ const status = stored === undefined ? ambient : { type: stored, source: stored === "oauth" ? "OAuth" : "stored credential" };
58
+ for (const method of methods) {
59
+ offered.push({
60
+ provider: provider.id,
61
+ name: provider.name,
62
+ method,
63
+ label: method === "oauth" ? (provider.auth.oauth?.loginLabel ?? ACCOUNT_LABEL) : API_KEY_LABEL,
64
+ subscription: method === "oauth" && provider.auth.oauth?.isSubscription === true,
65
+ external: interactiveAuth(provider, method) === undefined,
66
+ ...(status ? { status } : {}),
67
+ ...(ambient ? { environment: ambient.source } : {}),
68
+ ...(problem ? { problem } : {}),
69
+ });
70
+ }
71
+ }
72
+ return offered.sort((a, b) => a.name.localeCompare(b.name));
73
+ }
74
+ /** What a credential of `type` is called beside a provider's sign-in, as pi's picker calls it. */
75
+ function credentialKind(type, subscription) {
76
+ if (type === "api_key")
77
+ return "API key";
78
+ return subscription ? "subscription" : "account";
79
+ }
80
+ /** An option's status as pi's picker writes it after the provider's name. */
81
+ export function loginStatusText(option) {
82
+ const { status } = option;
83
+ // A stored credential outranks the environment, so a failed check of the environment does not decide it.
84
+ if (status === undefined)
85
+ return option.problem === undefined ? "not configured" : `auth check failed: ${option.problem}`;
86
+ if (status.type !== option.method)
87
+ return `${credentialKind(status.type, option.subscription)} configured`;
88
+ if (status.source === "OAuth" || status.source === "stored credential")
89
+ return "✓ configured";
90
+ return /^[A-Z][A-Z0-9_]*(?:, [A-Z][A-Z0-9_]*)*$/.test(status.source)
91
+ ? `✓ env: ${status.source}`
92
+ : `✓ ${status.source}`;
93
+ }
94
+ /** Combine present abort signals into one (no-op when none/one). */
95
+ function anySignal(...signals) {
96
+ const present = signals.filter((s) => s !== undefined);
97
+ return present.length === 0 ? undefined : present.length === 1 ? present[0] : AbortSignal.any(present);
98
+ }
99
+ /**
100
+ * Sign in to one of pi's built-in providers (plus the definition's) and persist the credential to `authPath`, or to
101
+ * the caller's `credentialStore`.
102
+ *
103
+ * The store is checked FIRST (a no-op write runs the file store's refuse-corrupt and writability checks, or whatever
104
+ * the caller's store does on a write), so a flow never runs toward a credential that could not be saved. An entered API key is verified with one minimal request to pi's
105
+ * default model for the provider (its first model when pi names none), at pi's built-in endpoint, BEFORE it is
106
+ * written. Credentials are provider-scoped, so any of the provider's models answers the question the check asks (does
107
+ * the provider refuse the key, HTTP 401); a key the provider rejects is never stored, and the provider's key flow runs
108
+ * again (the provider and method were not the mistake). Progress and outcome go out through `interaction.notify`.
109
+ *
110
+ * Aborting `interaction.signal` at any point, the verification included, rejects with {@link SignInCancelled} and
111
+ * writes no credential (an OAuth device ID, the installation's identity, is kept once created). Every prompt carries a
112
+ * signal that aborts on it, on the prompt's own signal (a callback server beating a `manual_code` prompt), and when the
113
+ * flow settles with a prompt still pending.
114
+ */
115
+ export async function loginOver(request, internals = {}) {
116
+ const { providers, verifyWith } = internals;
117
+ const { method, interaction } = request;
118
+ assertOneCredentialSource(request);
119
+ if (request.credentialStore === undefined && typeof request.authPath !== "string") {
120
+ throw new Error("login needs authPath or credentialStore: where the sign-in is written");
121
+ }
122
+ const provider = loginProviders(providers).find((p) => p.id === request.provider);
123
+ if (!provider)
124
+ throw new Error(`unknown provider "${request.provider}"`);
125
+ const auth = interactiveAuth(provider, method);
126
+ if (!auth?.login)
127
+ throw new Error(`provider "${provider.id}" has no interactive ${method} login`);
128
+ const flow = auth.login.bind(auth);
129
+ // The key under test lives here, not in the file, until it passes.
130
+ const trial = new InMemoryCredentialStore();
131
+ // Only a key needs the registry: it is checked with one request before it is kept.
132
+ const models = method === "api_key" ? modelsOver(trial, providers) : undefined;
133
+ const store = request.credentialStore ?? fastagentCredentialStore(request.authPath);
134
+ await store.modify(provider.id, async () => undefined);
135
+ const signal = interaction.signal ?? new AbortController().signal;
136
+ const notify = (event) => interaction.notify(event);
137
+ let getDeviceId = request.getDeviceId;
138
+ if (method === "oauth" && !getDeviceId && request.authPath !== undefined) {
139
+ const deviceId = await deviceIdBeside(request.authPath);
140
+ getDeviceId = () => deviceId;
141
+ }
142
+ for (;;) {
143
+ const credential = await runFlow((io) => flow(io, { getDeviceId }), interaction, signal);
144
+ let verified = "n/a";
145
+ if (models) {
146
+ await trial.modify(provider.id, async () => credential);
147
+ const verdict = await verifyApiKey(models, provider.id, verifyWith, notify, signal);
148
+ if (signal.aborted)
149
+ throw new SignInCancelled("cancelled");
150
+ if (verdict === "rejected")
151
+ continue;
152
+ verified = verdict;
153
+ }
154
+ await store.modify(provider.id, async () => credential);
155
+ return { provider: provider.id, method, verified };
156
+ }
157
+ }
158
+ /**
159
+ * The installation's identity, kept beside the credentials file it signs in to: created once, then read. Settled before
160
+ * the flow runs, so a sign-in never completes with an identity that was not kept.
161
+ */
162
+ async function deviceIdBeside(authPath) {
163
+ const path = join(dirname(authPath), "device.json");
164
+ try {
165
+ return await readOrCreateDeviceId(path);
166
+ }
167
+ catch (error) {
168
+ throw new Error(`could not persist login device ID in ${path}: ${error.message}`, { cause: error });
169
+ }
170
+ }
171
+ async function readOrCreateDeviceId(path) {
172
+ let text;
173
+ try {
174
+ text = await readFile(path, "utf8");
175
+ }
176
+ catch (error) {
177
+ if (error.code !== "ENOENT")
178
+ throw error;
179
+ }
180
+ if (text !== undefined) {
181
+ const stored = JSON.parse(text).deviceId;
182
+ if (typeof stored !== "string" || stored === "")
183
+ throw new Error("it holds no deviceId");
184
+ return stored;
185
+ }
186
+ const deviceId = randomUUID();
187
+ await mkdir(dirname(path), { recursive: true });
188
+ writeFileAtomic(path, `${JSON.stringify({ deviceId })}\n`);
189
+ return deviceId;
190
+ }
191
+ async function runFlow(flow, interaction, signal) {
192
+ if (signal.aborted)
193
+ throw new SignInCancelled("cancelled");
194
+ const done = new AbortController();
195
+ let backedOut;
196
+ let credential;
197
+ try {
198
+ credential = await flow({
199
+ signal,
200
+ prompt: (prompt) => {
201
+ const asked = anySignal(prompt.signal, signal, done.signal);
202
+ return interaction.prompt({ ...prompt, signal: asked }).catch((error) => {
203
+ // Remembered here: a provider may wrap the rejection it is handed in an error of its own. Only a question
204
+ // still asked: one the flow withdrew (its callback came first) rejects too, and nobody backed out of it.
205
+ if (error instanceof SignInCancelled && !asked?.aborted)
206
+ backedOut = error;
207
+ throw error;
208
+ });
209
+ },
210
+ notify: (event) => interaction.notify(event),
211
+ });
212
+ }
213
+ catch (error) {
214
+ // Whatever the provider made of the abort (its own error, a rejected prompt), it was the caller's decision.
215
+ if (signal.aborted)
216
+ throw new SignInCancelled("cancelled");
217
+ if (backedOut !== undefined)
218
+ throw backedOut;
219
+ throw error;
220
+ }
221
+ finally {
222
+ done.abort();
223
+ }
224
+ // A provider that ignored the abort and finished anyway: the caller still cancelled.
225
+ if (signal.aborted)
226
+ throw new SignInCancelled("cancelled");
227
+ return credential;
228
+ }
229
+ /**
230
+ * pi's default model per provider (`defaultModelPerProvider` in pi-coding-agent's model-resolver): the general-purpose
231
+ * model pi itself starts on, so the likeliest to answer a minimal request. COPIED, because pi does not export it;
232
+ * `test/login.test.ts` compares this against pi's own table, so a pi release that changes a default fails there.
233
+ */
234
+ export const PI_DEFAULT_MODELS = {
235
+ "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
236
+ "ant-ling": "Ring-2.6-1T",
237
+ anthropic: "claude-opus-4-8",
238
+ openai: "gpt-5.5",
239
+ azure: "gpt-5.4",
240
+ "openai-codex": "gpt-6.1-sol",
241
+ radius: "balanced",
242
+ nvidia: "nvidia/nemotron-3-ultra-550b-a55b",
243
+ deepseek: "deepseek-v4-pro",
244
+ google: "gemini-3.1-pro-preview",
245
+ "google-vertex": "gemini-3.1-pro-preview",
246
+ "github-copilot": "gpt-5.4",
247
+ openrouter: "moonshotai/kimi-k2.6",
248
+ "vercel-ai-gateway": "zai/glm-5.1",
249
+ xai: "grok-4.7",
250
+ groq: "openai/gpt-oss-120b",
251
+ cerebras: "gpt-oss-120b",
252
+ zai: "glm-5.3",
253
+ "zai-coding-cn": "glm-5.3",
254
+ mistral: "devstral-medium-latest",
255
+ minimax: "MiniMax-M2.7",
256
+ "minimax-cn": "MiniMax-M2.7",
257
+ moonshotai: "kimi-k2.6",
258
+ "moonshotai-cn": "kimi-k2.6",
259
+ huggingface: "moonshotai/Kimi-K2.6",
260
+ fireworks: "accounts/fireworks/models/kimi-k3",
261
+ together: "moonshotai/Kimi-K3",
262
+ baseten: "zai-org/GLM-5.2",
263
+ opencode: "kimi-k2.6",
264
+ "opencode-go": "kimi-k3",
265
+ "kimi-coding": "kimi-for-coding",
266
+ meta: "muse-spark-1.3",
267
+ "cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6",
268
+ "cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6",
269
+ "qwen-token-plan": "qwen3.7-max",
270
+ "qwen-token-plan-cn": "qwen3.7-max",
271
+ "qwen-token-plan-individual": "qwen3.8-max",
272
+ xiaomi: "mimo-v2.5-pro",
273
+ "xiaomi-token-plan-cn": "mimo-v2.5-pro",
274
+ "xiaomi-token-plan-ams": "mimo-v2.5-pro",
275
+ "xiaomi-token-plan-sgp": "mimo-v2.5-pro",
276
+ };
277
+ /** One minimal request with the key under test. */
278
+ async function verifyApiKey(models, providerId, verifyWith, notify, signal) {
279
+ const chosen = verifyWith?.startsWith(`${providerId}/`)
280
+ ? models.getModel(providerId, verifyWith.slice(providerId.length + 1))
281
+ : undefined;
282
+ const piDefault = PI_DEFAULT_MODELS[providerId];
283
+ // The provider's first model is the last resort only: catalogs are alphabetical, and a first model can be a
284
+ // special-purpose one that rejects a minimal request (which still answers the 401 question).
285
+ const model = chosen ??
286
+ (piDefault ? models.getModel(providerId, piDefault) : undefined) ??
287
+ models.getProvider(providerId)?.getModels()[0];
288
+ if (!model) {
289
+ notify({
290
+ type: "info",
291
+ message: `cannot verify the key: provider "${providerId}" lists no models — kept as entered`,
292
+ });
293
+ return "unknown";
294
+ }
295
+ const label = `${model.provider}/${model.id}`;
296
+ notify({ type: "progress", message: `verifying the key with ${label}…` });
297
+ const probe = await probeApiKey(models, model, signal);
298
+ if (signal.aborted)
299
+ return "unknown"; // the caller reports the cancel, not a verdict on the key
300
+ if (probe.state === "ok") {
301
+ notify({ type: "info", message: `key verified — ${label} responded` });
302
+ }
303
+ else if (probe.state === "rejected") {
304
+ notify({
305
+ type: "info",
306
+ message: `${providerId} rejected the API key (HTTP 401): ${probe.message} — enter it again (or cancel)`,
307
+ });
308
+ }
309
+ else {
310
+ notify({
311
+ type: "info",
312
+ message: `could not verify the key with ${label}: ${probe.message} — kept; invokes surface the provider's error`,
313
+ });
314
+ }
315
+ return probe.state;
316
+ }
317
+ /** {@link LoginIO} as pi-ai's `AuthInteraction`: what {@link loginOver} drives for the terminal. */
318
+ function terminalInteraction(io, signal) {
319
+ return {
320
+ ...(signal ? { signal } : {}),
321
+ prompt: async (p) => {
322
+ // A select is UNCANCELLABLE here: `LoginIO.select` takes no signal.
323
+ const v = p.type === "select"
324
+ ? await io.select(p.message, p.options.map((o) => ({
325
+ value: o.id,
326
+ label: o.label,
327
+ ...(o.description ? { hint: o.description } : {}),
328
+ })))
329
+ : await io.prompt(p.message, {
330
+ hidden: p.type === "secret",
331
+ redirect: p.type === "manual_code",
332
+ ...(p.signal ? { signal: p.signal } : {}),
333
+ });
334
+ if (v === undefined)
335
+ throw new SignInCancelled("cancelled");
336
+ return v;
337
+ },
338
+ notify: (e) => {
339
+ if (e.type === "auth_url") {
340
+ io.note(`Open this URL to authorize:\n ${e.url}`);
341
+ if (e.instructions)
342
+ io.note(e.instructions);
343
+ io.openUrl(e.url);
344
+ }
345
+ else if (e.type === "device_code") {
346
+ io.note(`Go to ${e.verificationUri} and enter the code: ${e.userCode}`);
347
+ io.openUrl(e.verificationUri, e.userCode);
348
+ }
349
+ else {
350
+ io.note(e.message);
351
+ }
352
+ },
353
+ };
354
+ }
355
+ /**
356
+ * `fastagent login` in a terminal: ask only what was not given, as pi's `/login` asks it (how to sign in, then the
357
+ * provider; a provider named by its id or its name, then how, when it offers both), then {@link loginOver}.
358
+ */
359
+ export async function loginFlow(io, options) {
360
+ const offered = await loginOptionsOver(options.statusFrom ?? options.authPath, options.providers);
361
+ const choice = await chooseLogin(offered, options, io);
362
+ if (choice.external) {
363
+ throw new LoginRefused(`${choice.name} is configured outside fastagent: set its key in the environment`, []);
364
+ }
365
+ return loginOver({
366
+ provider: choice.provider,
367
+ method: choice.method,
368
+ authPath: options.authPath,
369
+ interaction: terminalInteraction(io, options.signal),
370
+ }, {
371
+ ...(options.providers ? { providers: options.providers } : {}),
372
+ ...(options.verifyWith ? { verifyWith: options.verifyWith } : {}),
373
+ });
374
+ }
375
+ /** The flag that gives a method, as a refusal names it. */
376
+ const METHOD_FLAGS = "--method account or --method api-key";
377
+ /**
378
+ * The sign-in `given` names, or, with `menus`, the one a person picks; without them a choice not given refuses
379
+ * ({@link LoginRefused}) naming how to give it.
380
+ */
381
+ export async function chooseLogin(offered, given, menus) {
382
+ const picked = async (message, options, from, by) => {
383
+ const value = await menus.select(message, options);
384
+ const option = from.find((o) => by(o) === value);
385
+ if (option === undefined)
386
+ throw new SignInCancelled("nothing selected");
387
+ return option;
388
+ };
389
+ if (given.provider !== undefined) {
390
+ const named = given.provider.trim().toLowerCase();
391
+ const ofProvider = offered.filter((o) => o.provider.toLowerCase() === named || o.name.toLowerCase() === named);
392
+ if (ofProvider.length === 0) {
393
+ throw new LoginRefused(`unknown provider "${given.provider}": ${providerIds(offered)}`, offered);
394
+ }
395
+ const name = ofProvider[0].name;
396
+ const ofMethod = given.method === undefined ? ofProvider : ofProvider.filter((o) => o.method === given.method);
397
+ if (ofMethod.length === 0) {
398
+ const only = ofProvider[0].method === "oauth" ? "an account" : "an API key";
399
+ throw new LoginRefused(`${name} signs in only with ${only}`, ofProvider);
400
+ }
401
+ if (ofMethod.length === 1)
402
+ return ofMethod[0];
403
+ if (menus === undefined)
404
+ throw new LoginRefused(`${name} signs in with an account or an API key: pass ${METHOD_FLAGS}`, ofMethod);
405
+ return picked(`Select authentication method for ${name}:`, ofMethod.map((o) => ({ value: o.method, label: o.label })), ofMethod, (o) => o.method);
406
+ }
407
+ if (menus === undefined) {
408
+ throw new LoginRefused(`name the provider to sign in to (${providerIds(offered)}), and ${METHOD_FLAGS} for one that offers both`, given.method === undefined ? offered : offered.filter((o) => o.method === given.method));
409
+ }
410
+ let method = given.method;
411
+ if (method === undefined) {
412
+ // pi offers its own gateway beside the two ways of signing in.
413
+ const radius = offered.find((o) => o.provider === "radius" && o.method === "oauth");
414
+ const ways = [
415
+ ...(offered.some((o) => o.method === "oauth") ? [{ value: "oauth", label: ACCOUNT_LABEL }] : []),
416
+ ...(offered.some((o) => o.method === "api_key") ? [{ value: "api_key", label: API_KEY_LABEL }] : []),
417
+ ...(radius ? [{ value: "radius", label: `Sign in with ${radius.name}${statusSuffix(radius)}` }] : []),
418
+ ];
419
+ const way = await menus.select("Select authentication method:", ways);
420
+ if (way === "radius" && radius)
421
+ return radius;
422
+ if (way !== "oauth" && way !== "api_key")
423
+ throw new SignInCancelled("no authentication method selected");
424
+ method = way;
425
+ }
426
+ const candidates = offered.filter((o) => o.method === method);
427
+ if (candidates.length === 0)
428
+ throw new LoginRefused(`no provider signs in with ${method === "oauth" ? "an account" : "an API key"}`, []);
429
+ return picked("Select provider to configure:", candidates.map((o) => ({ value: o.provider, label: `${o.name}${statusSuffix(o)}` })), candidates, (o) => o.provider);
430
+ }
431
+ /** An option's status after its name on every row, as pi's picker writes it: ` ✓ configured`, ` • not configured`. */
432
+ function statusSuffix(option) {
433
+ const text = loginStatusText(option);
434
+ return text.startsWith("✓") ? ` ${text}` : ` • ${text}`;
435
+ }
436
+ /** Every provider id a sign-in can name, for a refusal that must say which. */
437
+ function providerIds(offered) {
438
+ return `one of ${[...new Set(offered.map((o) => o.provider))].sort().join(", ")}`;
439
+ }