@fastagent-sh/fastagent 0.24.3 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/README.md +53 -82
  2. package/dist/agent-api.d.ts +452 -0
  3. package/dist/agent-api.js +201 -0
  4. package/dist/answer-text.d.ts +8 -0
  5. package/dist/answer-text.js +18 -0
  6. package/dist/atomic-write.d.ts +2 -2
  7. package/dist/atomic-write.js +2 -2
  8. package/dist/authoring.d.ts +77 -0
  9. package/dist/authoring.js +238 -0
  10. package/dist/channel.d.ts +29 -15
  11. package/dist/channel.js +4 -1
  12. package/dist/channels/agentcore-protocol.d.ts +36 -50
  13. package/dist/channels/agentcore-protocol.js +3 -3
  14. package/dist/channels/agentcore-service.d.ts +8 -18
  15. package/dist/channels/agentcore-service.js +108 -127
  16. package/dist/channels/agentcore.d.ts +8 -24
  17. package/dist/channels/agentcore.js +32 -65
  18. package/dist/channels/body.d.ts +0 -16
  19. package/dist/channels/body.js +0 -25
  20. package/dist/channels/busy.js +6 -1
  21. package/dist/channels/context.d.ts +4 -0
  22. package/dist/channels/context.js +35 -0
  23. package/dist/channels/discover.d.ts +18 -20
  24. package/dist/channels/discover.js +41 -99
  25. package/dist/channels/feishu/feishu-api.d.ts +54 -4
  26. package/dist/channels/feishu/feishu-api.js +192 -149
  27. package/dist/channels/feishu/feishu.d.ts +3 -0
  28. package/dist/channels/feishu/feishu.js +117 -106
  29. package/dist/channels/feishu/history.d.ts +55 -0
  30. package/dist/channels/feishu/history.js +199 -0
  31. package/dist/channels/feishu/invoke-turn.d.ts +13 -11
  32. package/dist/channels/feishu/invoke-turn.js +9 -10
  33. package/dist/channels/feishu/model.d.ts +2 -1
  34. package/dist/channels/feishu/normalize.js +100 -0
  35. package/dist/channels/feishu/preview.d.ts +2 -2
  36. package/dist/channels/feishu/register-webhook.d.ts +1 -0
  37. package/dist/channels/feishu/register-webhook.js +3 -2
  38. package/dist/channels/feishu/scaffold/channel.ts +65 -46
  39. package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
  40. package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
  41. package/dist/channels/feishu/setup-mode.d.ts +45 -25
  42. package/dist/channels/feishu/setup-mode.js +74 -23
  43. package/dist/channels/feishu/shared-api.d.ts +24 -6
  44. package/dist/channels/feishu/shared-api.js +30 -24
  45. package/dist/channels/kit/context-buffer.d.ts +11 -2
  46. package/dist/channels/kit/context-buffer.js +7 -4
  47. package/dist/channels/kit/delivery.d.ts +19 -3
  48. package/dist/channels/kit/delivery.js +74 -15
  49. package/dist/channels/kit/event-stream.js +2 -1
  50. package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
  51. package/dist/channels/kit/invoke-turn-kit.js +62 -71
  52. package/dist/channels/kit/place-history.d.ts +107 -0
  53. package/dist/channels/kit/place-history.js +182 -0
  54. package/dist/channels/kit/preview-kit.d.ts +9 -5
  55. package/dist/channels/kit/preview-kit.js +36 -15
  56. package/dist/channels/kit/room-threads.d.ts +46 -0
  57. package/dist/channels/kit/room-threads.js +81 -0
  58. package/dist/channels/kit/seen.js +1 -1
  59. package/dist/channels/kit/stop-command.d.ts +6 -3
  60. package/dist/channels/kit/stop-command.js +20 -18
  61. package/dist/channels/kit/thread-fork.d.ts +17 -0
  62. package/dist/channels/kit/thread-fork.js +43 -0
  63. package/dist/channels/kit/thread-participants.js +1 -1
  64. package/dist/channels/kit/transport.d.ts +5 -0
  65. package/dist/channels/kit/transport.js +5 -0
  66. package/dist/channels/kit/turn-runner.d.ts +10 -9
  67. package/dist/channels/kit/turn-runner.js +17 -19
  68. package/dist/channels/kit/turn-store.d.ts +5 -4
  69. package/dist/channels/kit/turn-store.js +5 -5
  70. package/dist/channels/lark/lark.d.ts +3 -0
  71. package/dist/channels/lark/lark.js +5 -0
  72. package/dist/channels/lark/onboard.d.ts +5 -3
  73. package/dist/channels/lark/onboard.js +14 -14
  74. package/dist/channels/lark/scaffold/channel.ts +67 -44
  75. package/dist/channels/lark/scaffold/lark-send.ts +38 -33
  76. package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
  77. package/dist/channels/respond.d.ts +0 -4
  78. package/dist/channels/respond.js +1 -1
  79. package/dist/channels/router.d.ts +18 -0
  80. package/dist/channels/router.js +68 -0
  81. package/dist/channels/serve.d.ts +5 -70
  82. package/dist/channels/serve.js +105 -287
  83. package/dist/channels/slack/history.d.ts +42 -0
  84. package/dist/channels/slack/history.js +282 -0
  85. package/dist/channels/slack/invoke-turn.d.ts +12 -5
  86. package/dist/channels/slack/invoke-turn.js +7 -5
  87. package/dist/channels/slack/manifest.d.ts +1 -4
  88. package/dist/channels/slack/manifest.js +22 -10
  89. package/dist/channels/slack/model.d.ts +2 -1
  90. package/dist/channels/slack/onboard.d.ts +0 -2
  91. package/dist/channels/slack/onboard.js +2 -4
  92. package/dist/channels/slack/onboarding-state.d.ts +0 -2
  93. package/dist/channels/slack/onboarding-state.js +0 -1
  94. package/dist/channels/slack/parse.d.ts +0 -5
  95. package/dist/channels/slack/parse.js +1 -16
  96. package/dist/channels/slack/preview.d.ts +2 -3
  97. package/dist/channels/slack/preview.js +78 -52
  98. package/dist/channels/slack/register-webhook.d.ts +1 -0
  99. package/dist/channels/slack/register-webhook.js +6 -5
  100. package/dist/channels/slack/scaffold/channel.ts +23 -24
  101. package/dist/channels/slack/scaffold/slack-send.ts +50 -43
  102. package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
  103. package/dist/channels/slack/setup-server.d.ts +19 -0
  104. package/dist/channels/slack/setup-server.js +113 -11
  105. package/dist/channels/slack/shared-api.d.ts +15 -4
  106. package/dist/channels/slack/shared-api.js +19 -14
  107. package/dist/channels/slack/slack-api.d.ts +61 -2
  108. package/dist/channels/slack/slack-api.js +28 -5
  109. package/dist/channels/slack/slack.js +97 -52
  110. package/dist/channels/slack/welcomed.js +1 -1
  111. package/dist/channels/telegram/context-buffer.d.ts +6 -1
  112. package/dist/channels/telegram/context-buffer.js +19 -0
  113. package/dist/channels/telegram/invoke-turn.d.ts +8 -4
  114. package/dist/channels/telegram/invoke-turn.js +7 -7
  115. package/dist/channels/telegram/parse.d.ts +7 -2
  116. package/dist/channels/telegram/parse.js +7 -0
  117. package/dist/channels/telegram/preview.d.ts +2 -2
  118. package/dist/channels/telegram/register-webhook.d.ts +1 -0
  119. package/dist/channels/telegram/register-webhook.js +3 -2
  120. package/dist/channels/telegram/scaffold/channel.ts +27 -28
  121. package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
  122. package/dist/channels/telegram/shared-api.d.ts +34 -0
  123. package/dist/channels/telegram/shared-api.js +79 -0
  124. package/dist/channels/telegram/telegram-api.d.ts +15 -5
  125. package/dist/channels/telegram/telegram-api.js +10 -6
  126. package/dist/channels/telegram/telegram.js +27 -21
  127. package/dist/channels/telegram/turn-store.js +1 -1
  128. package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
  129. package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
  130. package/dist/cli/add-feishu.d.ts +42 -12
  131. package/dist/cli/add-feishu.js +177 -124
  132. package/dist/cli/add-slack.d.ts +8 -3
  133. package/dist/cli/add-slack.js +115 -63
  134. package/dist/cli/auth-view.d.ts +10 -3
  135. package/dist/cli/auth-view.js +8 -6
  136. package/dist/cli/box-login.d.ts +17 -7
  137. package/dist/cli/box-login.js +46 -12
  138. package/dist/cli/commands/add.d.ts +15 -4
  139. package/dist/cli/commands/add.js +252 -143
  140. package/dist/cli/commands/auth.d.ts +6 -0
  141. package/dist/cli/commands/auth.js +50 -0
  142. package/dist/cli/commands/content.d.ts +6 -0
  143. package/dist/cli/commands/content.js +45 -0
  144. package/dist/cli/commands/deploy/agentcore.js +50 -77
  145. package/dist/cli/commands/deploy/docker.js +38 -39
  146. package/dist/cli/commands/deploy/fly.js +21 -18
  147. package/dist/cli/commands/deploy/host-login.d.ts +26 -0
  148. package/dist/cli/commands/deploy/host-login.js +90 -0
  149. package/dist/cli/commands/deploy/railway.js +21 -26
  150. package/dist/cli/commands/deploy/shared.d.ts +45 -17
  151. package/dist/cli/commands/deploy/shared.js +34 -15
  152. package/dist/cli/commands/deploy.d.ts +0 -4
  153. package/dist/cli/commands/deploy.js +237 -50
  154. package/dist/cli/commands/destroy.js +8 -6
  155. package/dist/cli/commands/dev.d.ts +0 -2
  156. package/dist/cli/commands/dev.js +20 -19
  157. package/dist/cli/commands/env.d.ts +12 -0
  158. package/dist/cli/commands/env.js +190 -0
  159. package/dist/cli/commands/info.js +95 -156
  160. package/dist/cli/commands/init.d.ts +2 -2
  161. package/dist/cli/commands/init.js +35 -33
  162. package/dist/cli/commands/invoke.d.ts +6 -0
  163. package/dist/cli/commands/invoke.js +131 -16
  164. package/dist/cli/commands/login.d.ts +31 -2
  165. package/dist/cli/commands/login.js +413 -64
  166. package/dist/cli/commands/logout.d.ts +4 -0
  167. package/dist/cli/commands/logout.js +53 -0
  168. package/dist/cli/commands/logs.js +8 -6
  169. package/dist/cli/commands/models.d.ts +2 -1
  170. package/dist/cli/commands/models.js +39 -37
  171. package/dist/cli/commands/schedules.d.ts +21 -0
  172. package/dist/cli/commands/schedules.js +143 -0
  173. package/dist/cli/commands/start.d.ts +11 -10
  174. package/dist/cli/commands/start.js +35 -62
  175. package/dist/cli/content-view.d.ts +9 -0
  176. package/dist/cli/content-view.js +26 -0
  177. package/dist/cli/fail.d.ts +9 -12
  178. package/dist/cli/fail.js +17 -14
  179. package/dist/cli/invoke-stream.d.ts +12 -3
  180. package/dist/cli/invoke-stream.js +85 -23
  181. package/dist/cli/kernel.d.ts +9 -2
  182. package/dist/cli/kernel.js +15 -6
  183. package/dist/cli/login-relay.d.ts +3 -2
  184. package/dist/cli/login-relay.js +18 -6
  185. package/dist/cli/models-view.d.ts +5 -1
  186. package/dist/cli/models-view.js +1 -1
  187. package/dist/cli/person.d.ts +10 -0
  188. package/dist/cli/person.js +23 -0
  189. package/dist/cli/program.js +421 -203
  190. package/dist/cli/serve.d.ts +13 -63
  191. package/dist/cli/serve.js +31 -121
  192. package/dist/cli/shared.d.ts +21 -48
  193. package/dist/cli/shared.js +68 -89
  194. package/dist/cli/ticket.d.ts +136 -0
  195. package/dist/cli/ticket.js +476 -0
  196. package/dist/client.d.ts +7 -0
  197. package/dist/client.js +2 -0
  198. package/dist/content/declare.d.ts +70 -0
  199. package/dist/content/declare.js +163 -0
  200. package/dist/content/file.d.ts +32 -0
  201. package/dist/content/file.js +119 -0
  202. package/dist/content/git.d.ts +41 -0
  203. package/dist/content/git.js +217 -0
  204. package/dist/content/mount.d.ts +2 -0
  205. package/dist/content/mount.js +15 -0
  206. package/dist/content/resolve.d.ts +52 -0
  207. package/dist/content/resolve.js +151 -0
  208. package/dist/content/source.d.ts +27 -0
  209. package/dist/content/source.js +53 -0
  210. package/dist/conversation-sessions.d.ts +26 -0
  211. package/dist/conversation-sessions.js +99 -0
  212. package/dist/declared-secrets.d.ts +19 -37
  213. package/dist/declared-secrets.js +16 -58
  214. package/dist/deploy/agentcore/destroy.d.ts +3 -3
  215. package/dist/deploy/agentcore/destroy.js +46 -28
  216. package/dist/deploy/agentcore/forwarder.js +105 -22
  217. package/dist/deploy/agentcore/logs.js +1 -1
  218. package/dist/deploy/agentcore/plan.d.ts +10 -24
  219. package/dist/deploy/agentcore/plan.js +31 -135
  220. package/dist/deploy/agentcore/run.d.ts +1 -1
  221. package/dist/deploy/agentcore/run.js +8 -5
  222. package/dist/deploy/agentcore/shell.d.ts +1 -1
  223. package/dist/deploy/agentcore/shell.js +5 -5
  224. package/dist/deploy/api.d.ts +34 -0
  225. package/dist/deploy/api.js +50 -0
  226. package/dist/deploy/box-shell.d.ts +6 -0
  227. package/dist/deploy/box-shell.js +7 -0
  228. package/dist/deploy/build-context.d.ts +7 -5
  229. package/dist/deploy/build-context.js +41 -35
  230. package/dist/deploy/channel-ingress.d.ts +6 -1
  231. package/dist/deploy/channel-ingress.js +18 -5
  232. package/dist/deploy/container.d.ts +22 -9
  233. package/dist/deploy/container.js +96 -55
  234. package/dist/deploy/docker/plan.d.ts +3 -2
  235. package/dist/deploy/docker/plan.js +13 -14
  236. package/dist/deploy/docker/run.d.ts +2 -2
  237. package/dist/deploy/docker/run.js +8 -4
  238. package/dist/deploy/fly/plan.d.ts +1 -1
  239. package/dist/deploy/fly/plan.js +12 -21
  240. package/dist/deploy/fly/run.d.ts +10 -6
  241. package/dist/deploy/fly/run.js +11 -6
  242. package/dist/deploy/hosts.d.ts +2 -1
  243. package/dist/deploy/hosts.js +2 -1
  244. package/dist/deploy/preflight.d.ts +21 -23
  245. package/dist/deploy/preflight.js +222 -201
  246. package/dist/deploy/railway/plan.d.ts +8 -6
  247. package/dist/deploy/railway/plan.js +15 -58
  248. package/dist/deploy/railway/run.d.ts +8 -3
  249. package/dist/deploy/railway/run.js +12 -8
  250. package/dist/deploy/residency.d.ts +5 -11
  251. package/dist/deploy/residency.js +0 -6
  252. package/dist/deploy/runner.d.ts +1 -1
  253. package/dist/deploy/runner.js +1 -1
  254. package/dist/deploy/secrets.d.ts +16 -2
  255. package/dist/deploy/secrets.js +33 -6
  256. package/dist/deploy/workspace.d.ts +11 -6
  257. package/dist/deploy/workspace.js +55 -38
  258. package/dist/dev-supervisor.d.ts +11 -2
  259. package/dist/dev-supervisor.js +51 -26
  260. package/dist/effect-port.d.ts +4 -4
  261. package/dist/effect-port.js +4 -4
  262. package/dist/environment/apt.d.ts +4 -0
  263. package/dist/environment/apt.js +32 -0
  264. package/dist/environment/declare.d.ts +59 -0
  265. package/dist/environment/declare.js +210 -0
  266. package/dist/environment/mise.d.ts +50 -0
  267. package/dist/environment/mise.js +268 -0
  268. package/dist/feishu.d.ts +2 -2
  269. package/dist/feishu.js +2 -2
  270. package/dist/git.d.ts +13 -0
  271. package/dist/git.js +55 -0
  272. package/dist/harnesses/models/catalog.d.ts +20 -0
  273. package/dist/harnesses/models/catalog.js +254 -0
  274. package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
  275. package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
  276. package/dist/harnesses/models/environment.d.ts +43 -0
  277. package/dist/harnesses/models/environment.js +82 -0
  278. package/dist/{engines/pi → harnesses/models}/login.d.ts +57 -26
  279. package/dist/{engines/pi → harnesses/models}/login.js +187 -79
  280. package/dist/harnesses/models/models.d.ts +83 -0
  281. package/dist/harnesses/models/models.js +158 -0
  282. package/dist/harnesses/pi-durable/agent.d.ts +42 -0
  283. package/dist/harnesses/pi-durable/agent.js +579 -0
  284. package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
  285. package/dist/harnesses/pi-durable/defaults.js +26 -0
  286. package/dist/harnesses/pi-durable/define.d.ts +77 -0
  287. package/dist/harnesses/pi-durable/define.js +28 -0
  288. package/dist/harnesses/pi-durable/entries.d.ts +32 -0
  289. package/dist/harnesses/pi-durable/entries.js +126 -0
  290. package/dist/harnesses/pi-durable/events.d.ts +25 -0
  291. package/dist/harnesses/pi-durable/events.js +238 -0
  292. package/dist/harnesses/pi-durable/ids.d.ts +16 -0
  293. package/dist/harnesses/pi-durable/ids.js +55 -0
  294. package/dist/harnesses/pi-durable/messages.d.ts +41 -0
  295. package/dist/harnesses/pi-durable/messages.js +116 -0
  296. package/dist/harnesses/pi-durable/open.d.ts +88 -0
  297. package/dist/harnesses/pi-durable/open.js +329 -0
  298. package/dist/harnesses/pi-durable/reads.d.ts +30 -0
  299. package/dist/harnesses/pi-durable/reads.js +94 -0
  300. package/dist/harnesses/pi-durable/registry.d.ts +18 -0
  301. package/dist/harnesses/pi-durable/registry.js +56 -0
  302. package/dist/harnesses/pi-durable/release.d.ts +13 -0
  303. package/dist/harnesses/pi-durable/release.js +40 -0
  304. package/dist/harnesses/pi-durable/serve.d.ts +80 -0
  305. package/dist/harnesses/pi-durable/serve.js +156 -0
  306. package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
  307. package/dist/harnesses/pi-durable/sessions.js +47 -0
  308. package/dist/harnesses/pi-durable/storage.d.ts +4 -0
  309. package/dist/harnesses/pi-durable/storage.js +49 -0
  310. package/dist/harnesses/pi-durable/stream.d.ts +20 -0
  311. package/dist/harnesses/pi-durable/stream.js +221 -0
  312. package/dist/harnesses/pi-durable/text.d.ts +30 -0
  313. package/dist/harnesses/pi-durable/text.js +292 -0
  314. package/dist/harnesses/pi-durable/wake.d.ts +4 -0
  315. package/dist/harnesses/pi-durable/wake.js +81 -0
  316. package/dist/http/client.d.ts +6 -0
  317. package/dist/http/client.js +256 -0
  318. package/dist/http/contract.d.ts +541 -0
  319. package/dist/http/contract.js +179 -0
  320. package/dist/http/server.d.ts +34 -0
  321. package/dist/http/server.js +194 -0
  322. package/dist/index.d.ts +17 -4
  323. package/dist/index.js +10 -5
  324. package/dist/lark.d.ts +2 -2
  325. package/dist/lark.js +2 -2
  326. package/dist/locked-file.d.ts +17 -0
  327. package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
  328. package/dist/once.d.ts +6 -0
  329. package/dist/once.js +23 -0
  330. package/dist/paths.d.ts +36 -52
  331. package/dist/paths.js +66 -182
  332. package/dist/runtime.d.ts +25 -2
  333. package/dist/runtime.js +27 -0
  334. package/dist/scaffold/add-channel.d.ts +14 -27
  335. package/dist/scaffold/add-channel.js +82 -180
  336. package/dist/scaffold/init.d.ts +17 -18
  337. package/dist/scaffold/init.js +87 -99
  338. package/dist/scaffold/templates/SYSTEM.md +12 -0
  339. package/dist/scaffold/templates/env.example +4 -10
  340. package/dist/scaffold/templates/fastagent.config.ts +21 -33
  341. package/dist/scaffold/templates/gitignore +8 -1
  342. package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
  343. package/dist/scaffold/templates.d.ts +9 -4
  344. package/dist/scaffold/templates.js +7 -5
  345. package/dist/scaffold/vendor-skill.d.ts +1 -4
  346. package/dist/scaffold/vendor-skill.js +5 -6
  347. package/dist/schedule/cron.d.ts +13 -0
  348. package/dist/schedule/cron.js +52 -0
  349. package/dist/schedule/eventbridge-cron.d.ts +17 -0
  350. package/dist/schedule/eventbridge-cron.js +88 -0
  351. package/dist/schedule/schedule.d.ts +23 -0
  352. package/dist/schedule/schedule.js +34 -0
  353. package/dist/schedule/scheduler.d.ts +34 -33
  354. package/dist/schedule/scheduler.js +251 -137
  355. package/dist/schedule/state.d.ts +4 -14
  356. package/dist/schedule/state.js +7 -7
  357. package/dist/schedule/wake-alarm.d.ts +22 -2
  358. package/dist/schedule/wake-alarm.js +66 -20
  359. package/dist/schedule/wakeups.d.ts +16 -15
  360. package/dist/schedule/wakeups.js +47 -45
  361. package/dist/service.d.ts +56 -169
  362. package/dist/service.js +91 -187
  363. package/dist/serving-command.d.ts +10 -0
  364. package/dist/serving-command.js +16 -0
  365. package/dist/slack.d.ts +1 -1
  366. package/dist/slack.js +1 -1
  367. package/dist/{channels/kit/state.js → state-file.js} +6 -6
  368. package/dist/telegram.d.ts +1 -0
  369. package/dist/telegram.js +1 -0
  370. package/dist/tunnel.d.ts +3 -0
  371. package/dist/tunnel.js +12 -4
  372. package/dist/value-file.d.ts +21 -0
  373. package/dist/value-file.js +108 -0
  374. package/dist/version.d.ts +5 -0
  375. package/dist/version.js +14 -1
  376. package/package.json +18 -28
  377. package/dist/agent.d.ts +0 -106
  378. package/dist/agent.js +0 -21
  379. package/dist/channels/control.d.ts +0 -31
  380. package/dist/channels/control.js +0 -329
  381. package/dist/channels/define-channel.d.ts +0 -39
  382. package/dist/channels/define-channel.js +0 -9
  383. package/dist/channels/feishu/context-buffer.d.ts +0 -35
  384. package/dist/channels/feishu/context-buffer.js +0 -65
  385. package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
  386. package/dist/channels/http.d.ts +0 -23
  387. package/dist/channels/http.js +0 -57
  388. package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
  389. package/dist/channels/slack/context-buffer.d.ts +0 -23
  390. package/dist/channels/slack/context-buffer.js +0 -37
  391. package/dist/channels/sse.d.ts +0 -4
  392. package/dist/channels/sse.js +0 -66
  393. package/dist/cli/commands/chat.d.ts +0 -3
  394. package/dist/cli/commands/chat.js +0 -14
  395. package/dist/cli/commands/routine-run.d.ts +0 -6
  396. package/dist/cli/commands/routine-run.js +0 -46
  397. package/dist/cli/commands/routine.d.ts +0 -25
  398. package/dist/cli/commands/routine.js +0 -122
  399. package/dist/cli/commands/tool.d.ts +0 -1
  400. package/dist/cli/commands/tool.js +0 -57
  401. package/dist/collect.d.ts +0 -63
  402. package/dist/collect.js +0 -90
  403. package/dist/core.d.ts +0 -13
  404. package/dist/core.js +0 -15
  405. package/dist/engines/pi/agent-models.d.ts +0 -83
  406. package/dist/engines/pi/agent-models.js +0 -120
  407. package/dist/engines/pi/agent-session-factory.d.ts +0 -121
  408. package/dist/engines/pi/agent-session-factory.js +0 -517
  409. package/dist/engines/pi/chat.d.ts +0 -3
  410. package/dist/engines/pi/chat.js +0 -8
  411. package/dist/engines/pi/config.d.ts +0 -71
  412. package/dist/engines/pi/config.js +0 -199
  413. package/dist/engines/pi/create.d.ts +0 -170
  414. package/dist/engines/pi/create.js +0 -343
  415. package/dist/engines/pi/definition.d.ts +0 -74
  416. package/dist/engines/pi/definition.js +0 -236
  417. package/dist/engines/pi/entry-images.d.ts +0 -14
  418. package/dist/engines/pi/entry-images.js +0 -23
  419. package/dist/engines/pi/invoke-session.d.ts +0 -18
  420. package/dist/engines/pi/invoke-session.js +0 -403
  421. package/dist/engines/pi/locked-file.d.ts +0 -9
  422. package/dist/engines/pi/machine.d.ts +0 -53
  423. package/dist/engines/pi/machine.js +0 -137
  424. package/dist/engines/pi/models.d.ts +0 -200
  425. package/dist/engines/pi/models.js +0 -533
  426. package/dist/engines/pi/open.d.ts +0 -180
  427. package/dist/engines/pi/open.js +0 -270
  428. package/dist/engines/pi/openai-account-models.d.ts +0 -35
  429. package/dist/engines/pi/openai-account-models.js +0 -116
  430. package/dist/engines/pi/report.d.ts +0 -18
  431. package/dist/engines/pi/report.js +0 -59
  432. package/dist/engines/pi/retry-event.d.ts +0 -6
  433. package/dist/engines/pi/retry-event.js +0 -15
  434. package/dist/engines/pi/service.d.ts +0 -9
  435. package/dist/engines/pi/service.js +0 -14
  436. package/dist/engines/pi/session-builder.d.ts +0 -13
  437. package/dist/engines/pi/session-builder.js +0 -117
  438. package/dist/engines/pi/session-control.d.ts +0 -77
  439. package/dist/engines/pi/session-control.js +0 -908
  440. package/dist/engines/pi/session-effects.d.ts +0 -14
  441. package/dist/engines/pi/session-effects.js +0 -30
  442. package/dist/engines/pi/session-inheritance.d.ts +0 -21
  443. package/dist/engines/pi/session-inheritance.js +0 -204
  444. package/dist/engines/pi/session-markers.d.ts +0 -49
  445. package/dist/engines/pi/session-markers.js +0 -60
  446. package/dist/engines/pi/session-settings.d.ts +0 -67
  447. package/dist/engines/pi/session-settings.js +0 -119
  448. package/dist/engines/pi/session-store.d.ts +0 -69
  449. package/dist/engines/pi/session-store.js +0 -430
  450. package/dist/engines/pi/tool-context.d.ts +0 -45
  451. package/dist/engines/pi/tool-context.js +0 -31
  452. package/dist/engines/pi/tool.d.ts +0 -99
  453. package/dist/engines/pi/tool.js +0 -118
  454. package/dist/engines/pi/turn-kit.d.ts +0 -83
  455. package/dist/engines/pi/turn-kit.js +0 -275
  456. package/dist/engines/pi/wake-tool.d.ts +0 -9
  457. package/dist/engines/pi/wake-tool.js +0 -92
  458. package/dist/loader.d.ts +0 -41
  459. package/dist/loader.js +0 -116
  460. package/dist/node.d.ts +0 -3
  461. package/dist/node.js +0 -4
  462. package/dist/observe.d.ts +0 -6
  463. package/dist/observe.js +0 -47
  464. package/dist/pi.d.ts +0 -25
  465. package/dist/pi.js +0 -15
  466. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  467. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  468. package/dist/schedule/discover.d.ts +0 -17
  469. package/dist/schedule/discover.js +0 -61
  470. package/dist/schedule/routine.d.ts +0 -43
  471. package/dist/schedule/routine.js +0 -32
  472. package/dist/schedule/run.d.ts +0 -55
  473. package/dist/schedule/run.js +0 -146
  474. package/dist/secrets-gate.d.ts +0 -32
  475. package/dist/secrets-gate.js +0 -33
  476. package/dist/session-remote.d.ts +0 -47
  477. package/dist/session-remote.js +0 -460
  478. package/dist/session.d.ts +0 -466
  479. package/dist/session.js +0 -57
  480. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
@@ -2,20 +2,34 @@
2
2
  * Signing in to a MODEL PROVIDER, for any front end: {@link login} 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
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.
5
+ * one front end ({@link loginFlow}: terminal menus, then {@link login}); `login --deployment` drives the same flow on a
6
+ * deployed box over a relay (cli/login-relay.ts).
6
7
  */
7
8
  import { InMemoryCredentialStore, } from "@earendil-works/pi-ai";
8
- import { dirname } from "node:path";
9
- import { SettingsManager } from "@earendil-works/pi-coding-agent";
10
- import { fastagentCredentialStore } from "./auth.js";
11
- import { createPiModelRuntime, interactiveAuth, loginProviders, piModelsOver, probeApiKey } from "./models.js";
12
- import { agentModels } from "./agent-models.js";
9
+ import { randomUUID } from "node:crypto";
10
+ import { mkdir, readFile } from "node:fs/promises";
11
+ import { dirname, join } from "node:path";
12
+ import { writeFileAtomic } from "../../atomic-write.js";
13
+ import { assertOneCredentialSource, fastagentCredentialStore } from "./credentials.js";
14
+ import { UNNAMED_AUTH_SOURCE, interactiveAuth, loginProviders, modelsOver, probeApiKey } from "./models.js";
13
15
  /** The user backed out of a prompt/menu, or the flow was aborted — a decision, not a failure. */
14
16
  export class LoginCancelled extends Error {
15
17
  }
18
+ /** A sign-in that cannot start as asked: an unknown provider, or a choice a caller without menus did not make. */
19
+ export class LoginRefused extends Error {
20
+ /** The sign-ins it could be instead, for a caller to choose from. */
21
+ choices;
22
+ constructor(message, choices) {
23
+ super(message);
24
+ this.choices = choices;
25
+ }
26
+ }
27
+ const ACCOUNT_LABEL = "Sign in with an account";
28
+ const API_KEY_LABEL = "Sign in with an API key";
16
29
  /**
17
- * Every interactive sign-in of pi's built-in providers, with what `credentials` (a credentials file, or the caller's
18
- * own store) already holds for each. A provider whose key can only come from its env var offers none and is left out.
30
+ * Every sign-in of pi's built-in providers, sorted by name as pi's `/login` lists them, with what authenticates each
31
+ * one here: a credential `credentials` (a credentials file, or the caller's own store) holds, else this process's
32
+ * environment, which a stored credential outranks.
19
33
  */
20
34
  export function loginOptions(credentials) {
21
35
  return loginOptionsOver(credentials);
@@ -27,23 +41,64 @@ export async function loginOptionsOver(credentials, providers) {
27
41
  // ONE read of the store: `list` is metadata only, and reading per provider would repeat a corrupt file's warning for
28
42
  // every provider.
29
43
  const held = new Map((await storeOf(credentials).list()).map((info) => [info.providerId, info.type]));
44
+ // The environment alone, over no store: what authenticates a provider nothing is stored for.
45
+ const environment = modelsOver(new InMemoryCredentialStore(), providers);
30
46
  const offered = [];
31
47
  for (const provider of loginProviders(providers)) {
32
- const methods = ["oauth", "api_key"].filter((method) => interactiveAuth(provider, method));
48
+ const methods = ["oauth", "api_key"].filter((method) => provider.auth[method === "oauth" ? "oauth" : "apiKey"]);
33
49
  if (methods.length === 0)
34
50
  continue;
35
51
  const stored = held.get(provider.id);
52
+ let status;
53
+ let problem;
54
+ if (stored !== undefined) {
55
+ status = { type: stored, source: stored === "oauth" ? "OAuth" : "stored credential" };
56
+ }
57
+ else {
58
+ try {
59
+ const found = await environment.checkAuth(provider.id);
60
+ if (found !== undefined)
61
+ status = { type: found.type, source: found.source ?? UNNAMED_AUTH_SOURCE };
62
+ }
63
+ catch (error) {
64
+ problem = error.message;
65
+ }
66
+ }
36
67
  for (const method of methods) {
37
68
  offered.push({
38
69
  provider: provider.id,
70
+ name: provider.name,
39
71
  method,
40
- label: interactiveAuth(provider, method)?.name ?? provider.name,
72
+ label: method === "oauth" ? (provider.auth.oauth?.loginLabel ?? ACCOUNT_LABEL) : API_KEY_LABEL,
41
73
  subscription: method === "oauth" && provider.auth.oauth?.isSubscription === true,
42
- ...(stored ? { stored } : {}),
74
+ external: interactiveAuth(provider, method) === undefined,
75
+ ...(status ? { status } : {}),
76
+ ...(problem ? { problem } : {}),
43
77
  });
44
78
  }
45
79
  }
46
- return offered;
80
+ return offered.sort((a, b) => a.name.localeCompare(b.name));
81
+ }
82
+ /** What a credential of `type` is called beside a provider's sign-in, as pi's picker calls it. */
83
+ function credentialKind(type, subscription) {
84
+ if (type === "api_key")
85
+ return "API key";
86
+ return subscription ? "subscription" : "account";
87
+ }
88
+ /** An option's status as pi's picker writes it after the provider's name. */
89
+ export function loginStatusText(option) {
90
+ if (option.problem !== undefined)
91
+ return `auth check failed: ${option.problem}`;
92
+ const { status } = option;
93
+ if (status === undefined)
94
+ return "not configured";
95
+ if (status.type !== option.method)
96
+ return `${credentialKind(status.type, option.subscription)} configured`;
97
+ if (status.source === "OAuth" || status.source === "stored credential")
98
+ return "✓ configured";
99
+ return /^[A-Z][A-Z0-9_]*(?:, [A-Z][A-Z0-9_]*)*$/.test(status.source)
100
+ ? `✓ env: ${status.source}`
101
+ : `✓ ${status.source}`;
47
102
  }
48
103
  /** Combine present abort signals into one (no-op when none/one). */
49
104
  function anySignal(...signals) {
@@ -60,7 +115,6 @@ function anySignal(...signals) {
60
115
  * written. Credentials are provider-scoped, so any of the provider's models answers the question the check asks (does
61
116
  * the provider refuse the key, HTTP 401); a key the provider rejects is never stored, and the provider's key flow runs
62
117
  * again (the provider and method were not the mistake). Progress and outcome go out through `interaction.notify`.
63
- * A provider an agent's models.json points elsewhere is not this call's: store that key with `fastagentCredentialStore`.
64
118
  *
65
119
  * Aborting `interaction.signal` at any point, the verification included, rejects with {@link LoginCancelled} and
66
120
  * 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
@@ -71,8 +125,9 @@ export function login(request) {
71
125
  }
72
126
  /** {@link login}, plus {@link LoginInternals}. */
73
127
  export async function loginOver(request, internals = {}) {
74
- const { providers, verifyWith, agentDir } = internals;
128
+ const { providers, verifyWith } = internals;
75
129
  const { method, interaction } = request;
130
+ assertOneCredentialSource(request);
76
131
  if (request.credentialStore === undefined && typeof request.authPath !== "string") {
77
132
  throw new Error("login needs authPath or credentialStore: where the sign-in is written");
78
133
  }
@@ -85,32 +140,15 @@ export async function loginOver(request, internals = {}) {
85
140
  const flow = auth.login.bind(auth);
86
141
  // The key under test lives here, not in the file, until it passes.
87
142
  const trial = new InMemoryCredentialStore();
88
- // Only a key needs the registry, and building an agent's fails on a broken models.json: an OAuth sign-in must not.
89
- // For a key it is built BEFORE the flow, so that failure comes before the person types anything.
90
- const models = method !== "api_key"
91
- ? undefined
92
- : agentDir
93
- ? await createPiModelRuntime({
94
- agentDir,
95
- credentials: trial,
96
- ...(providers ? { providers: [...providers] } : {}),
97
- })
98
- : piModelsOver(trial, providers);
99
- const store = agentModels(undefined, request).credentials;
143
+ // Only a key needs the registry: it is checked with one request before it is kept.
144
+ const models = method === "api_key" ? modelsOver(trial, providers) : undefined;
145
+ const store = request.credentialStore ?? fastagentCredentialStore(request.authPath);
100
146
  await store.modify(provider.id, async () => undefined);
101
147
  const signal = interaction.signal ?? new AbortController().signal;
102
148
  const notify = (event) => interaction.notify(event);
103
149
  let getDeviceId = request.getDeviceId;
104
150
  if (method === "oauth" && !getDeviceId && request.authPath !== undefined) {
105
- // Only the settings file beside the credentials: no project's `.pi/settings.json` has a say in this write.
106
- const settings = SettingsManager.create(dirname(request.authPath), dirname(request.authPath), {
107
- projectTrusted: false,
108
- });
109
- const deviceId = settings.getOrCreateDeviceId();
110
- await settings.flush();
111
- const errors = settings.drainErrors();
112
- if (errors.length)
113
- throw new Error(`could not persist login device ID: ${errors.map(({ error }) => error.message).join("; ")}`);
151
+ const deviceId = await deviceIdBeside(request.authPath);
114
152
  getDeviceId = () => deviceId;
115
153
  }
116
154
  for (;;) {
@@ -129,6 +167,39 @@ export async function loginOver(request, internals = {}) {
129
167
  return { provider: provider.id, method, verified };
130
168
  }
131
169
  }
170
+ /**
171
+ * The installation's identity, kept beside the credentials file it signs in to: created once, then read. Settled before
172
+ * the flow runs, so a sign-in never completes with an identity that was not kept.
173
+ */
174
+ async function deviceIdBeside(authPath) {
175
+ const path = join(dirname(authPath), "device.json");
176
+ try {
177
+ return await readOrCreateDeviceId(path);
178
+ }
179
+ catch (error) {
180
+ throw new Error(`could not persist login device ID in ${path}: ${error.message}`, { cause: error });
181
+ }
182
+ }
183
+ async function readOrCreateDeviceId(path) {
184
+ let text;
185
+ try {
186
+ text = await readFile(path, "utf8");
187
+ }
188
+ catch (error) {
189
+ if (error.code !== "ENOENT")
190
+ throw error;
191
+ }
192
+ if (text !== undefined) {
193
+ const stored = JSON.parse(text).deviceId;
194
+ if (typeof stored !== "string" || stored === "")
195
+ throw new Error("it holds no deviceId");
196
+ return stored;
197
+ }
198
+ const deviceId = randomUUID();
199
+ await mkdir(dirname(path), { recursive: true });
200
+ writeFileAtomic(path, `${JSON.stringify({ deviceId })}\n`);
201
+ return deviceId;
202
+ }
132
203
  async function runFlow(flow, interaction, signal) {
133
204
  if (signal.aborted)
134
205
  throw new LoginCancelled("cancelled");
@@ -165,10 +236,10 @@ export const PI_DEFAULT_MODELS = {
165
236
  "ant-ling": "Ring-2.6-1T",
166
237
  anthropic: "claude-opus-4-8",
167
238
  openai: "gpt-5.5",
168
- "azure-openai-responses": "gpt-5.4",
239
+ azure: "gpt-5.4",
169
240
  "openai-codex": "gpt-6.1-sol",
170
241
  radius: "balanced",
171
- nvidia: "nvidia/nemotron-3-super-120b-a12b",
242
+ nvidia: "nvidia/nemotron-3-ultra-550b-a55b",
172
243
  deepseek: "deepseek-v4-pro",
173
244
  google: "gemini-3.1-pro-preview",
174
245
  "google-vertex": "gemini-3.1-pro-preview",
@@ -255,7 +326,11 @@ function terminalInteraction(io, signal) {
255
326
  label: o.label,
256
327
  ...(o.description ? { hint: o.description } : {}),
257
328
  })))
258
- : await io.prompt(p.message, { hidden: p.type === "secret", ...(p.signal ? { signal: p.signal } : {}) });
329
+ : await io.prompt(p.message, {
330
+ hidden: p.type === "secret",
331
+ redirect: p.type === "manual_code",
332
+ ...(p.signal ? { signal: p.signal } : {}),
333
+ });
259
334
  if (v === undefined)
260
335
  throw new LoginCancelled("cancelled");
261
336
  return v;
@@ -269,7 +344,7 @@ function terminalInteraction(io, signal) {
269
344
  }
270
345
  else if (e.type === "device_code") {
271
346
  io.note(`Go to ${e.verificationUri} and enter the code: ${e.userCode}`);
272
- io.openUrl(e.verificationUri);
347
+ io.openUrl(e.verificationUri, e.userCode);
273
348
  }
274
349
  else {
275
350
  io.note(e.message);
@@ -277,55 +352,88 @@ function terminalInteraction(io, signal) {
277
352
  },
278
353
  };
279
354
  }
280
- async function selectMethod(io) {
281
- const v = await io.select("Authentication method", [
282
- { value: "oauth", label: "Use a subscription (OAuth)" },
283
- { value: "api_key", label: "Use an API key" },
284
- ]);
285
- if (v !== "oauth" && v !== "api_key")
286
- throw new LoginCancelled("no authentication method selected");
287
- return v;
288
- }
289
355
  /**
290
- * `fastagent login`: ask only what was not given (method, then provider, from {@link loginOptions} with each one's
291
- * stored credential), then {@link login}.
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 login}.
292
358
  */
293
359
  export async function loginFlow(io, options) {
294
- const offered = await loginOptionsOver(options.authPath, options.providers);
295
- let provider = options.provider;
296
- let method = options.method;
297
- if (provider) {
298
- const methods = offered.filter((o) => o.provider === provider).map((o) => o.method);
299
- if (methods.length === 0) {
300
- const known = loginProviders(options.providers).some((p) => p.id === provider);
301
- throw new Error(known
302
- ? `provider "${provider}" has no interactive login — set its API key via the provider's env var`
303
- : `unknown provider "${provider}"`);
304
- }
305
- method ??= methods.length > 1 ? await selectMethod(io) : methods[0];
306
- }
307
- else {
308
- const chosenMethod = method ?? (await selectMethod(io));
309
- method = chosenMethod;
310
- const candidates = offered.filter((o) => o.method === chosenMethod);
311
- if (candidates.length === 0)
312
- throw new Error(`no provider supports ${chosenMethod} login`);
313
- provider = await io.select("Select a provider", candidates.map((o) => ({
314
- value: o.provider,
315
- label: o.label,
316
- ...(o.stored ? { hint: `configured (${o.stored})` } : {}),
317
- })));
318
- if (!candidates.some((o) => o.provider === provider))
319
- throw new LoginCancelled("no provider selected");
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`, []);
320
364
  }
321
365
  return loginOver({
322
- provider: provider,
323
- method: method,
366
+ provider: choice.provider,
367
+ method: choice.method,
324
368
  authPath: options.authPath,
325
369
  interaction: terminalInteraction(io, options.signal),
326
370
  }, {
327
371
  ...(options.providers ? { providers: options.providers } : {}),
328
372
  ...(options.verifyWith ? { verifyWith: options.verifyWith } : {}),
329
- ...(options.agentDir ? { agentDir: options.agentDir } : {}),
330
373
  });
331
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 LoginCancelled("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 LoginCancelled("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
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * pi-ai's model collection as FastAgent composes it: the built-in providers over a credential store, kept current by
3
+ * the catalogs (catalog.ts), and the definition's own providers; and the reads a report, a login or a picker make of
4
+ * it. A custom model is a provider in code (`defineAgent({ providers })`), never a models file or a per-account list
5
+ * (architecture.md §3).
6
+ */
7
+ import { type Api, type CredentialStore, type Model, type Models, type Provider } from "@earendil-works/pi-ai";
8
+ import type { Catalogs } from "./catalog.ts";
9
+ /** The provider of a `"provider/modelId"` spec. */
10
+ export declare function providerOf(spec: string): string;
11
+ /** Every chat model `models` offers, as `"provider/modelId"`, sorted. */
12
+ export declare function listModels(models: Models): string[];
13
+ /**
14
+ * The built-ins plus `providers` (a same id replaces a built-in), over any credential store. With `catalogs`, the lists
15
+ * its refresh brings up to date; without, the ones this pi-ai was built with.
16
+ */
17
+ export declare function modelsOver(credentials: CredentialStore, providers?: readonly Provider[], catalogs?: Catalogs): Models;
18
+ /**
19
+ * Whether `env` alone satisfies a provider, by pi's own check, and with what when the provider says (its API-key
20
+ * variable, or an ambient source such as an AWS profile); undefined when it does not. A provider in code may
21
+ * authenticate without naming a source. Passed explicitly rather than read from `process.env`: only the environment a
22
+ * SHELL hands down is shared by every agent on the machine, and a process that has loaded an agent's `.secrets/.env`
23
+ * holds that agent's variables too. What the shell shares outranks the global credentials file (environment.ts), so a
24
+ * global login for such a provider is not used.
25
+ *
26
+ * `fileExists` answers for the files a source points at (an AWS profile, Google ADC): this machine's by default, which
27
+ * is right wherever the answer is about THIS machine. A deployment passes one that finds nothing, because no file of
28
+ * the builder's travels with it.
29
+ */
30
+ export declare function environmentAuth(providerId: string, env: NodeJS.ProcessEnv, fileExists?: (path: string) => Promise<boolean>, providers?: readonly Provider[]): Promise<{
31
+ source?: string;
32
+ } | undefined>;
33
+ /**
34
+ * The environment variables a provider reads its key from, in the order it reads them: what a person sets for it to
35
+ * authenticate with no sign-in. pi-ai keeps the names inside each provider's auth, so they are learnt by asking the
36
+ * provider to authenticate over an environment that records every name asked for and holds none. Its side-effect-free
37
+ * `check` when it has one, since its `resolve` may then run commands.
38
+ */
39
+ export declare function keyVariables(provider: Provider): Promise<string[]>;
40
+ /**
41
+ * What a provider can offer as an interactive login: an OAuth flow, an API-key ENTRY prompt, or nothing ("none" — the
42
+ * key must come from the provider's env var).
43
+ */
44
+ export type InteractiveLoginKind = "oauth" | "api_key" | "none";
45
+ export declare function interactiveLoginKind(p: Provider): InteractiveLoginKind;
46
+ /** THE rule for "can this method be signed in to interactively": the auth to run for it, or undefined. */
47
+ export declare function interactiveAuth(provider: Provider, method: Exclude<InteractiveLoginKind, "none">): import("@earendil-works/pi-ai").ApiKeyAuth | import("@earendil-works/pi-ai").OAuthAuth | undefined;
48
+ /** The providers a sign-in can target: pi's built-ins plus `extra`, composed exactly as {@link modelsOver} does. */
49
+ export declare function loginProviders(extra?: readonly Provider[]): readonly Provider[];
50
+ /** Per-provider auth status for the first-run model picker. */
51
+ export type ProviderAuthStatus = {
52
+ state: "ready";
53
+ source?: string;
54
+ } | {
55
+ state: "unconfigured";
56
+ login: InteractiveLoginKind;
57
+ } | {
58
+ state: "broken";
59
+ message: string;
60
+ login: InteractiveLoginKind;
61
+ };
62
+ /** Probe every provider's auth once (auth is provider-scoped, so any of its models works as the probe). */
63
+ export declare function providerAuthStatuses(models: Models): Promise<Map<string, ProviderAuthStatus>>;
64
+ /** The source of a provider that authenticates without naming one: a definition's provider resolving its own key. */
65
+ export declare const UNNAMED_AUTH_SOURCE = "the provider's own auth";
66
+ /**
67
+ * Which source currently satisfies auth for `spec`, or undefined when none does (or the spec is not in `models`).
68
+ * Rejects when resolving fails — a corrupt entry, a refresh the provider refused — which is a different answer from
69
+ * "nothing configured" and must reach whoever reports it.
70
+ */
71
+ export declare function probeAuthSource(models: Models, spec: string): Promise<string | undefined>;
72
+ /**
73
+ * Verdict of {@link probeApiKey}: `rejected` is DEFINITIVE (the provider answered HTTP 401 — the key is wrong);
74
+ * everything else non-ok is `unknown`.
75
+ */
76
+ export type KeyProbe = {
77
+ state: "ok";
78
+ } | {
79
+ state: "rejected" | "unknown";
80
+ message: string;
81
+ };
82
+ /** Quick-fail probe for a just-stored API key. */
83
+ export declare function probeApiKey(models: Models, model: Model<Api>, signal?: AbortSignal): Promise<KeyProbe>;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * pi-ai's model collection as FastAgent composes it: the built-in providers over a credential store, kept current by
3
+ * the catalogs (catalog.ts), and the definition's own providers; and the reads a report, a login or a picker make of
4
+ * it. A custom model is a provider in code (`defineAgent({ providers })`), never a models file or a per-account list
5
+ * (architecture.md §3).
6
+ */
7
+ import { InMemoryCredentialStore, createModels, defaultProviderAuthContext, } from "@earendil-works/pi-ai";
8
+ import { builtinModels, builtinProviders } from "@earendil-works/pi-ai/providers/all";
9
+ /** The provider of a `"provider/modelId"` spec. */
10
+ export function providerOf(spec) {
11
+ const slash = spec.indexOf("/");
12
+ return slash > 0 ? spec.slice(0, slash) : spec;
13
+ }
14
+ /** Every chat model `models` offers, as `"provider/modelId"`, sorted. */
15
+ export function listModels(models) {
16
+ const specs = [];
17
+ for (const provider of models.getProviders()) {
18
+ for (const model of provider.getModels())
19
+ specs.push(`${provider.id}/${model.id}`);
20
+ }
21
+ return specs.sort();
22
+ }
23
+ /**
24
+ * The built-ins plus `providers` (a same id replaces a built-in), over any credential store. With `catalogs`, the lists
25
+ * its refresh brings up to date; without, the ones this pi-ai was built with.
26
+ */
27
+ export function modelsOver(credentials, providers = [], catalogs) {
28
+ const models = createModels({
29
+ credentials,
30
+ authContext: defaultProviderAuthContext(),
31
+ ...(catalogs ? { modelsStore: catalogs.fetched } : {}),
32
+ });
33
+ for (const builtIn of builtinProviders())
34
+ models.setProvider(catalogs ? catalogs.over(builtIn) : builtIn);
35
+ for (const provider of providers)
36
+ models.setProvider(provider);
37
+ return models;
38
+ }
39
+ /**
40
+ * Whether `env` alone satisfies a provider, by pi's own check, and with what when the provider says (its API-key
41
+ * variable, or an ambient source such as an AWS profile); undefined when it does not. A provider in code may
42
+ * authenticate without naming a source. Passed explicitly rather than read from `process.env`: only the environment a
43
+ * SHELL hands down is shared by every agent on the machine, and a process that has loaded an agent's `.secrets/.env`
44
+ * holds that agent's variables too. What the shell shares outranks the global credentials file (environment.ts), so a
45
+ * global login for such a provider is not used.
46
+ *
47
+ * `fileExists` answers for the files a source points at (an AWS profile, Google ADC): this machine's by default, which
48
+ * is right wherever the answer is about THIS machine. A deployment passes one that finds nothing, because no file of
49
+ * the builder's travels with it.
50
+ */
51
+ export async function environmentAuth(providerId, env, fileExists = defaultProviderAuthContext().fileExists, providers = []) {
52
+ const models = builtinModels({
53
+ credentials: new InMemoryCredentialStore(),
54
+ authContext: {
55
+ // pi's own reading of a variable (a blank one is unset), over the given environment.
56
+ env: async (name) => (env[name]?.trim() ? env[name] : undefined),
57
+ fileExists,
58
+ },
59
+ });
60
+ for (const provider of providers)
61
+ models.setProvider(provider);
62
+ return models.checkAuth(providerId);
63
+ }
64
+ /**
65
+ * The environment variables a provider reads its key from, in the order it reads them: what a person sets for it to
66
+ * authenticate with no sign-in. pi-ai keeps the names inside each provider's auth, so they are learnt by asking the
67
+ * provider to authenticate over an environment that records every name asked for and holds none. Its side-effect-free
68
+ * `check` when it has one, since its `resolve` may then run commands.
69
+ */
70
+ export async function keyVariables(provider) {
71
+ const apiKey = provider.auth.apiKey;
72
+ if (apiKey === undefined)
73
+ return [];
74
+ const asked = new Set();
75
+ const ctx = {
76
+ env: async (name) => void asked.add(name),
77
+ fileExists: async () => false,
78
+ };
79
+ const signal = new AbortController().signal;
80
+ await (apiKey.check ? apiKey.check({ ctx, signal }) : apiKey.resolve({ ctx, signal }));
81
+ return [...asked];
82
+ }
83
+ export function interactiveLoginKind(p) {
84
+ if (interactiveAuth(p, "oauth"))
85
+ return "oauth";
86
+ return interactiveAuth(p, "api_key") ? "api_key" : "none";
87
+ }
88
+ /** THE rule for "can this method be signed in to interactively": the auth to run for it, or undefined. */
89
+ export function interactiveAuth(provider, method) {
90
+ return method === "oauth" ? provider.auth.oauth : provider.auth.apiKey?.login ? provider.auth.apiKey : undefined;
91
+ }
92
+ /** The providers a sign-in can target: pi's built-ins plus `extra`, composed exactly as {@link modelsOver} does. */
93
+ export function loginProviders(extra) {
94
+ return modelsOver(new InMemoryCredentialStore(), extra).getProviders();
95
+ }
96
+ /** Probe every provider's auth once (auth is provider-scoped, so any of its models works as the probe). */
97
+ export async function providerAuthStatuses(models) {
98
+ const statuses = new Map();
99
+ for (const provider of models.getProviders()) {
100
+ const [probe] = provider.getModels();
101
+ if (!probe)
102
+ continue;
103
+ const login = interactiveLoginKind(provider);
104
+ try {
105
+ const auth = await models.getAuth(probe);
106
+ statuses.set(provider.id, auth ? { state: "ready", source: auth.source } : { state: "unconfigured", login });
107
+ }
108
+ catch (error) {
109
+ statuses.set(provider.id, { state: "broken", message: error.message, login });
110
+ }
111
+ }
112
+ return statuses;
113
+ }
114
+ /** The source of a provider that authenticates without naming one: a definition's provider resolving its own key. */
115
+ export const UNNAMED_AUTH_SOURCE = "the provider's own auth";
116
+ /**
117
+ * Which source currently satisfies auth for `spec`, or undefined when none does (or the spec is not in `models`).
118
+ * Rejects when resolving fails — a corrupt entry, a refresh the provider refused — which is a different answer from
119
+ * "nothing configured" and must reach whoever reports it.
120
+ */
121
+ export async function probeAuthSource(models, spec) {
122
+ const slash = spec.indexOf("/");
123
+ if (slash < 1)
124
+ return undefined;
125
+ const model = models.getModel(spec.slice(0, slash), spec.slice(slash + 1));
126
+ if (!model)
127
+ return undefined;
128
+ // A virtual selection authenticates the physical model after routing, not its catalog entry.
129
+ if (model.api === "pi-virtual")
130
+ return "virtual";
131
+ const auth = await models.getAuth(model);
132
+ return auth === undefined ? undefined : (auth.source ?? UNNAMED_AUTH_SOURCE);
133
+ }
134
+ /** Quick-fail probe for a just-stored API key. */
135
+ export async function probeApiKey(models, model, signal) {
136
+ let status;
137
+ let reply;
138
+ try {
139
+ reply = await models.complete(model, { messages: [{ role: "user", content: "ping", timestamp: Date.now() }] }, {
140
+ maxTokens: 16,
141
+ timeoutMs: 15_000,
142
+ maxRetries: 0,
143
+ ...(signal ? { signal } : {}),
144
+ onResponse: (r) => {
145
+ status = r.status;
146
+ },
147
+ });
148
+ }
149
+ catch (error) {
150
+ // Thrown = before/around the request (auth resolution, transport setup) — not a provider verdict.
151
+ return { state: "unknown", message: error.message };
152
+ }
153
+ if (reply.stopReason !== "error" && reply.stopReason !== "aborted")
154
+ return { state: "ok" };
155
+ const message = reply.errorMessage ?? `stopReason "${reply.stopReason}"`;
156
+ const unauthorized = status === 401 || (status === undefined && /(^|\D)401(\D|$)/.test(message));
157
+ return { state: unauthorized ? "rejected" : "unknown", message };
158
+ }
@@ -0,0 +1,42 @@
1
+ import type { Models, ModelThinkingLevel } from "@earendil-works/pi-ai";
2
+ import { type Conversation, Harness, type HarnessOptions, type HarnessSettings, type ModelRef, type RegistryReader, type Storage } from "@earendil-works/pi-durable";
3
+ import { type Agent } from "../../agent-api.ts";
4
+ /** What a Caller may offer as `/<name>`: a prompt template or a skill, expanded for the model (SPEC.md §5.1). */
5
+ export interface AgentCommand {
6
+ name: string;
7
+ description?: string;
8
+ expand(args: string): string;
9
+ }
10
+ export interface DurableAgentOptions {
11
+ storage: Storage;
12
+ models: Models;
13
+ registry: RegistryReader;
14
+ settings?: HarnessSettings;
15
+ env?: HarnessOptions["env"];
16
+ name: string;
17
+ description?: string;
18
+ /** Read for every `info()` and send, so a command the definition gains is offered without a restart. */
19
+ commands?: () => readonly AgentCommand[];
20
+ /** What a conversation answers with when its own settings name none (defaults.ts). */
21
+ model?: ModelRef;
22
+ thinkingLevel?: ModelThinkingLevel;
23
+ inFlightPerProcess?: number;
24
+ /** What becomes of the messages an earlier process accepted and did not end ({@link Unfinished}). */
25
+ unfinished?: Unfinished;
26
+ }
27
+ /**
28
+ * What a process does with the messages an earlier one accepted and did not end (it was stopped, or killed). `"resume"`
29
+ * (the default, a serve's): they run on in this one, as its own work. `"abort"`: they end aborted before anything runs,
30
+ * for a Caller whose messages do not outlive it (`fastagent invoke`), where running one again would run what its user
31
+ * stopped. `"refuse"`: the open fails naming them, for a process that runs one thing and would cut the rest off
32
+ * (`fastagent schedules run`).
33
+ */
34
+ export type Unfinished = "resume" | "abort" | "refuse";
35
+ export interface DurableAgent extends Agent {
36
+ /** pi-durable's own API, for operating the agent. */
37
+ readonly harness: Harness;
38
+ /** The conversation a session id names, to operate it through `harness`. */
39
+ conversation(sessionId: string): Promise<Conversation | undefined>;
40
+ close(): Promise<void>;
41
+ }
42
+ export declare function openDurableAgent(options: DurableAgentOptions): Promise<DurableAgent>;