@fastagent-sh/fastagent 0.24.4 → 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 -535
  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 -92
  455. package/dist/engines/pi/turn-kit.js +0 -288
  456. package/dist/engines/pi/wake-tool.d.ts +0 -9
  457. package/dist/engines/pi/wake-tool.js +0 -92
  458. package/dist/loader.d.ts +0 -41
  459. package/dist/loader.js +0 -116
  460. package/dist/node.d.ts +0 -3
  461. package/dist/node.js +0 -4
  462. package/dist/observe.d.ts +0 -6
  463. package/dist/observe.js +0 -47
  464. package/dist/pi.d.ts +0 -25
  465. package/dist/pi.js +0 -15
  466. package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
  467. package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
  468. package/dist/schedule/discover.d.ts +0 -17
  469. package/dist/schedule/discover.js +0 -61
  470. package/dist/schedule/routine.d.ts +0 -43
  471. package/dist/schedule/routine.js +0 -32
  472. package/dist/schedule/run.d.ts +0 -55
  473. package/dist/schedule/run.js +0 -146
  474. package/dist/secrets-gate.d.ts +0 -32
  475. package/dist/secrets-gate.js +0 -33
  476. package/dist/session-remote.d.ts +0 -47
  477. package/dist/session-remote.js +0 -460
  478. package/dist/session.d.ts +0 -466
  479. package/dist/session.js +0 -57
  480. /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
@@ -1,533 +0,0 @@
1
- /**
2
- * Building pi's model registry: the built-ins, an agent's `models.json` and model catalog layered over the machine's,
3
- * the catalog refresh, and the reads of those files a report or a deploy check needs. Which credential store an agent
4
- * runs it over is agent-models.ts's; registry and auth are one collection, so the model's provider auth is in scope.
5
- */
6
- import { createHash } from "node:crypto";
7
- import { existsSync } from "node:fs";
8
- import { readFile } from "node:fs/promises";
9
- import { join } from "node:path";
10
- import { InMemoryCredentialStore, InMemoryModelsStore, defaultProviderAuthContext, } from "@earendil-works/pi-ai";
11
- import { builtinModels, builtinProviders, getBuiltinModelDataGeneratedAt } from "@earendil-works/pi-ai/providers/all";
12
- import { ModelRuntime } from "@earendil-works/pi-coding-agent";
13
- import { AGENT_MODEL_CATALOG_FILE, AGENT_MODELS_FILE, globalHome, resolveOverridePath } from "../../paths.js";
14
- import { writeFileAtomic } from "../../atomic-write.js";
15
- import { withLockedFile } from "./locked-file.js";
16
- import { OPENAI_PROVIDER, registerAccountModels, withAccountModels } from "./openai-account-models.js";
17
- /** The built-ins plus `providers` (a same id replaces a built-in), over any credential store. */
18
- export function piModelsOver(credentials, providers = []) {
19
- const models = builtinModels({ credentials, authContext: defaultProviderAuthContext() });
20
- const openai = models.getProvider(OPENAI_PROVIDER);
21
- if (openai)
22
- models.setProvider(withAccountModels(openai));
23
- for (const provider of providers)
24
- models.setProvider(provider);
25
- return models;
26
- }
27
- /** The serving default for reasoning effort, pinned to what pi's TUI defaults to (its own DEFAULT_THINKING_LEVEL). */
28
- export const DEFAULT_THINKING_LEVEL = "medium";
29
- /**
30
- * The MACHINE's custom-endpoint file, in the same schema as an agent's own: endpoints a person set up for this machine
31
- * (a local Ollama, a company gateway), which every agent here inherits the way it inherits the machine's skills.
32
- * `FASTAGENT_MODELS_PATH` moves it. It never ships: a deployed agent has only its definition's file.
33
- */
34
- function machineModelsPath(env = process.env) {
35
- return resolveOverridePath(env.FASTAGENT_MODELS_PATH) ?? join(globalHome(), AGENT_MODELS_FILE);
36
- }
37
- /** A custom-endpoint file's providers by id, or undefined when there is no file. Strict JSON: see {@link modelLayers}. */
38
- async function readProviders(path) {
39
- let raw;
40
- try {
41
- raw = await readFile(path, "utf8");
42
- }
43
- catch (error) {
44
- if (error.code === "ENOENT")
45
- return undefined;
46
- throw new Error(`could not read ${path}: ${error.message}`);
47
- }
48
- let parsed;
49
- try {
50
- parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
51
- }
52
- catch (error) {
53
- throw new Error(`${path} is not valid JSON: ${error.message}`);
54
- }
55
- const providers = parsed?.providers;
56
- if (typeof providers !== "object" || providers === null || Array.isArray(providers)) {
57
- throw new Error(`${path} must hold { "providers": { ... } }`);
58
- }
59
- return providers;
60
- }
61
- /**
62
- * The two custom-endpoint layers of an agent, read ONCE for every reader: the file pi loads and the report of where a
63
- * provider came from both come from this, so what runs and what `info`/`deploy` say cannot disagree. Undefined when
64
- * the machine has no file: then the agent's own file goes to pi untouched.
65
- *
66
- * Read here rather than by pi, because pi loads exactly one path and its comment stripping is not reachable: while a
67
- * machine file exists, both files must be plain JSON.
68
- */
69
- async function modelLayers(agentDir) {
70
- const machinePath = machineModelsPath();
71
- const machine = await readProviders(machinePath);
72
- if (!machine)
73
- return undefined;
74
- return { machinePath, machine, own: (await readProviders(join(agentDir, AGENT_MODELS_FILE))) ?? {} };
75
- }
76
- /**
77
- * The models.json pi loads for an agent: its own file, layered over the machine's when `machine` is on. The agent's
78
- * file wins a provider id outright, as a definition's skill wins a name: an agent that pins an endpoint keeps it.
79
- *
80
- * Always a SNAPSHOT named by its content, in fastagent's own home: pi reloads `modelsPath` on every refresh, and every
81
- * session builds its own runtime, so a live file would let turns run on an edit the control plane never read. Not in
82
- * the agent (so `info` writes nothing there), and not in a shared temp dir, which the OS clears while a long-running
83
- * process still re-reads it. Content addressing is what lets every process share the directory: a snapshot is never
84
- * rewritten, so no process can change what another reads. A running process keeps the content it started with; an
85
- * edit to either file takes effect on the next start.
86
- *
87
- * ponytail: snapshots are never pruned (one per distinct content, a few KB each) because a running process may still
88
- * read an old one; delete the directory while no fastagent process runs if it ever matters.
89
- */
90
- async function modelsFileFor(agentDir, machine) {
91
- const definition = join(agentDir, AGENT_MODELS_FILE);
92
- const layers = machine ? await modelLayers(agentDir) : undefined;
93
- if (layers) {
94
- const content = JSON.stringify({ providers: { ...layers.machine, ...layers.own } }, null, 2);
95
- return { path: snapshotFile(content), merged: { machine: layers.machinePath, definition } };
96
- }
97
- let own;
98
- try {
99
- own = await readFile(definition, "utf8");
100
- }
101
- catch (error) {
102
- if (error.code === "ENOENT")
103
- return { path: null };
104
- throw new Error(`could not read ${definition}: ${error.message}`);
105
- }
106
- // Verbatim, so pi parses it exactly as it would the file itself.
107
- return { path: snapshotFile(own), snapshotOf: definition };
108
- }
109
- function snapshotFile(content) {
110
- const hash = createHash("sha256").update(content).digest("hex").slice(0, 32);
111
- const path = join(globalHome(), ".cache", "models", `${hash}.json`);
112
- // 0600: it may carry literal keys. Several processes may create the same snapshot at once.
113
- if (!existsSync(path))
114
- writeFileAtomic(path, content, 0o600, true);
115
- return path;
116
- }
117
- /**
118
- * Where an agent's custom endpoints come from, for a report: the machine file, the providers the agent inherits from
119
- * it, and the ones its own file overrides. Undefined when the machine has no file.
120
- */
121
- export async function machineModels(agentDir) {
122
- const layers = await modelLayers(agentDir);
123
- if (!layers)
124
- return undefined;
125
- const ids = Object.keys(layers.machine).sort();
126
- return {
127
- path: layers.machinePath,
128
- inherited: ids.filter((id) => !(id in layers.own)),
129
- overridden: ids.filter((id) => id in layers.own),
130
- };
131
- }
132
- /** Whether pi ships this provider id itself, so an agent resolves it without any models.json entry. */
133
- export function isBuiltinProvider(providerId) {
134
- return builtinProviders().some((provider) => provider.id === providerId);
135
- }
136
- /**
137
- * The MACHINE's model catalog: the models pi.dev lists that are newer than the catalog bundled with pi, fetched by
138
- * `fastagent models --refresh -g`. Every agent here reads it under its own; like `~/.fastagent/models.json`, it
139
- * never ships.
140
- */
141
- export function globalCatalogPath() {
142
- return join(globalHome(), AGENT_MODEL_CATALOG_FILE);
143
- }
144
- /** A catalog file as pi writes it (entries by provider), or `{}` when there is none. Read without a lock: its one
145
- * writer ({@link catalogFileStore}) replaces the file whole, so a read sees the old catalog or the new one. */
146
- async function readCatalog(path) {
147
- if (!existsSync(path))
148
- return {};
149
- return parseCatalog(await readFile(path, "utf8"), path);
150
- }
151
- /**
152
- * The one reading of a catalog file's content, for reads and writes alike. Empty is an empty catalog, as pi reads it:
153
- * the writer creates the file exclusively an instant before its first content, and pi's own store, which wrote these
154
- * files before, left one behind whenever a refresh it began wrote no entry. That is safe only because nothing writes
155
- * these files in place any more: a rewrite in place is read mostly as empty while it runs. Anything else that does not
156
- * parse is a corrupt file, and a WRITE refuses it too: serializing over it would drop every other provider's entries.
157
- */
158
- function parseCatalog(content, path) {
159
- if (content === "")
160
- return {};
161
- try {
162
- return JSON.parse(content);
163
- }
164
- catch (error) {
165
- throw new Error(`model catalog ${path} is not valid JSON (${error.message}): refresh or delete it`);
166
- }
167
- }
168
- /**
169
- * The catalog file a refresh writes, as pi's store. It replaces pi's own file store, which rewrites the file in place
170
- * under its lock: a reader that takes no lock (every catalog read here, and anything else reading the file) could open
171
- * it cut short mid-write and blame a healthy file. Same lock as pi's, so concurrent refreshes still queue; reading
172
- * creates nothing, so a refresh that is refused writes no file.
173
- */
174
- export function catalogFileStore(path) {
175
- const change = (update, signal) => withLockedFile(path, async (current) => {
176
- const catalog = parseCatalog(current ?? "", path);
177
- update(catalog);
178
- return { result: undefined, next: JSON.stringify(catalog, null, 2) };
179
- }, signal ? { signal } : {});
180
- return {
181
- async read(providerId, options) {
182
- options?.signal?.throwIfAborted();
183
- return (await readCatalog(path))[providerId];
184
- },
185
- async write(providerId, entry, options) {
186
- options?.signal?.throwIfAborted();
187
- await change((catalog) => {
188
- catalog[providerId] = structuredClone(entry);
189
- }, options?.signal);
190
- },
191
- async delete(providerId, options) {
192
- options?.signal?.throwIfAborted();
193
- await change((catalog) => {
194
- delete catalog[providerId];
195
- }, options?.signal);
196
- },
197
- };
198
- }
199
- /**
200
- * The catalog files layered into one read-only store, later files winning a (type, model id) pair. An entry pi would ignore (no
201
- * newer than the catalog bundled with it) is dropped before the merge, so a stale layer cannot ride a newer one's
202
- * date past pi's rule.
203
- *
204
- * Read here rather than handed to pi as a file: pi takes one store, and its file store creates the file (and a lock)
205
- * on read, which would drop an empty catalog into every agent dir that was never refreshed.
206
- */
207
- async function layeredCatalog(paths) {
208
- return catalogStore(await readLayeredCatalog(paths));
209
- }
210
- /** A fresh store over a catalog read once: pi may write into the store it is given, so runtimes never share one. */
211
- async function catalogStore(entries) {
212
- const store = new InMemoryModelsStore();
213
- for (const [provider, entry] of entries)
214
- await store.write(provider, structuredClone(entry));
215
- return store;
216
- }
217
- async function readLayeredCatalog(paths) {
218
- const bundledAt = getBuiltinModelDataGeneratedAt();
219
- const merged = new Map();
220
- for (const path of paths) {
221
- for (const [provider, entry] of Object.entries(await readCatalog(path))) {
222
- if (bundledAt !== undefined && (entry.lastModified === undefined || entry.lastModified <= bundledAt))
223
- continue;
224
- const held = merged.get(provider);
225
- const key = (model) => `${model.type ?? "chat"}\u0000${model.id}`;
226
- const ids = new Set(entry.models.map(key));
227
- merged.set(provider, held
228
- ? {
229
- models: [...held.models.filter((model) => !ids.has(key(model))), ...entry.models],
230
- lastModified: Math.max(held.lastModified ?? 0, entry.lastModified ?? 0),
231
- }
232
- : entry);
233
- }
234
- }
235
- return merged;
236
- }
237
- /**
238
- * Whether the machine's model catalog supplies this model, by the same rule every registry reads it with
239
- * ({@link layeredCatalog}): an entry pi would ignore supplies nothing. The one layer a deployed agent does not have.
240
- */
241
- export async function inGlobalCatalog(provider, id) {
242
- const entry = await (await layeredCatalog([globalCatalogPath()])).read(provider);
243
- return entry?.models.some((model) => (model.type ?? "chat") === "chat" && model.id === id) ?? false;
244
- }
245
- /** The models.json a runtime for these options loads, and which model catalog it reads. */
246
- export async function modelRuntimeFiles(options) {
247
- const { agentDir } = options;
248
- const machine = options.machineLayer !== false;
249
- const models = agentDir ? await modelsFileFor(agentDir, machine) : undefined;
250
- const catalogs = !agentDir
251
- ? []
252
- : [...(machine ? [globalCatalogPath()] : []), join(agentDir, AGENT_MODEL_CATALOG_FILE)];
253
- const catalog = !options.catalogFile && agentDir ? await readLayeredCatalog(catalogs) : undefined;
254
- return {
255
- ...(models ? { models } : {}),
256
- create: async () => ({
257
- modelsPath: models?.path ?? null,
258
- // Always a store of our own whenever modelsPath is set: pi's default is a file at
259
- // `<dirname(modelsPath)>/models-store.json`. Without a directory, pi keeps an empty one in memory.
260
- ...(options.catalogFile
261
- ? { modelsStore: catalogFileStore(options.catalogFile) }
262
- : catalog
263
- ? { modelsStore: await catalogStore(catalog) }
264
- : {}),
265
- // Never fetched while a runtime is built: serving stays offline and reproducible. A refresh is asked for.
266
- allowModelNetwork: false,
267
- ...(options.catalogBaseUrl ? { catalogBaseUrl: options.catalogBaseUrl } : {}),
268
- }),
269
- };
270
- }
271
- /**
272
- * This machine's registry, without an agent: pi's built-ins, the machine's models.json and its model catalog. What
273
- * `fastagent models` lists outside an agent; {@link catalogFile} is the refresh's (it writes the file).
274
- */
275
- export async function machineModelRuntime(options = {}) {
276
- const runtime = await ModelRuntime.create({
277
- credentials: options.credentials ?? new InMemoryCredentialStore(),
278
- modelsPath: machineModelsPath(),
279
- modelsStore: options.catalogFile
280
- ? catalogFileStore(options.catalogFile)
281
- : await layeredCatalog([globalCatalogPath()]),
282
- allowModelNetwork: false,
283
- ...(options.catalogBaseUrl ? { catalogBaseUrl: options.catalogBaseUrl } : {}),
284
- });
285
- const error = runtime.getError();
286
- if (error)
287
- throw new Error(error);
288
- // A machine catalog refresh may refresh an OAuth token, which must carry the account's catalog over.
289
- await withModelRegistration(runtime, async () => registerAccountModels(runtime));
290
- return runtime;
291
- }
292
- /** How long a catalog refresh may take, as `pi update --models` allows. */
293
- const CATALOG_REFRESH_TIMEOUT_MS = 15_000;
294
- /**
295
- * Fetch the model catalog of every provider the runtime can authenticate into `catalogFile` ({@link
296
- * catalogFileStore}): the refresh `pi update --models` runs. `build` makes the runtime over that file, and runs only
297
- * once the refresh is known to go ahead. A refusal writes nothing: the store creates the file only when pi writes an
298
- * entry. pi asks pi.dev only for a provider with a usable credential, and may refresh an expired OAuth token of the
299
- * runtime's store to get one. Rejects, naming each provider that failed, when any part fails, when it outlasts 15
300
- * seconds, when `PI_OFFLINE` is set, when the catalog file is corrupt, and when no provider has a usable credential (the
301
- * refresh would ask for nothing).
302
- */
303
- export async function refreshCatalog(catalogFile, build, options = {}) {
304
- // pi skips its own background refresh under PI_OFFLINE, but an explicit `allowNetwork: true` overrides that.
305
- if (process.env.PI_OFFLINE !== undefined)
306
- throw new Error("PI_OFFLINE is set, so the model catalog is not refreshed");
307
- // A corrupt file fails here, once, by its own message: past this point every provider's refresh reads the store and
308
- // would repeat it, worded as that provider's failure.
309
- await readCatalog(catalogFile);
310
- const runtime = await build(catalogFile);
311
- // pi skips a provider it cannot authenticate without recording anything, so with no usable credential at all the
312
- // refresh would "succeed" having asked for nothing.
313
- const refreshable = runtime.getProviders().filter((provider) => provider.refreshModels !== undefined);
314
- const usable = await Promise.all(refreshable.map(async (provider) => (await runtime.checkAuth(provider.id)) !== undefined));
315
- if (!usable.includes(true)) {
316
- throw new Error("no provider has a usable credential here, so there is no model catalog to fetch — log in " +
317
- "(`fastagent login`) or set a provider's API key first");
318
- }
319
- const timeout = AbortSignal.timeout(CATALOG_REFRESH_TIMEOUT_MS);
320
- const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
321
- const result = await runtime.refresh({ allowNetwork: true, force: true, signal });
322
- if (result.aborted) {
323
- throw new Error(options.signal?.aborted
324
- ? "model catalog refresh cancelled"
325
- : `model catalog refresh did not finish within ${CATALOG_REFRESH_TIMEOUT_MS / 1000}s`);
326
- }
327
- if (result.errors.size > 0) {
328
- const details = [...result.errors].map(([provider, error]) => `${provider}: ${error.message}`).join("; ");
329
- throw new Error(`could not refresh the model catalog: ${details}`);
330
- }
331
- }
332
- /**
333
- * What satisfies a provider from `env` alone (its API-key variable, or an ambient source such as an AWS profile), by
334
- * pi's own check, or undefined. Passed explicitly rather than read from `process.env`: only the environment a SHELL
335
- * hands down is shared by every agent on the machine, and a process that has loaded an agent's `.secrets/.env` holds
336
- * that agent's variables too. What the shell shares outranks the global credentials file (agent-models.ts),
337
- * so a global login for such a provider is not used.
338
- *
339
- * `fileExists` answers for the files a source points at (an AWS profile, Google ADC): this machine's by default, which
340
- * is right wherever the answer is about THIS machine. A deployment passes one that finds nothing, because no file of
341
- * the builder's travels with it.
342
- */
343
- export async function environmentAuthSource(providerId, env, fileExists = defaultProviderAuthContext().fileExists) {
344
- const models = builtinModels({
345
- credentials: new InMemoryCredentialStore(),
346
- authContext: {
347
- // pi's own reading of a variable (a blank one is unset), over the given environment.
348
- env: async (name) => (env[name]?.trim() ? env[name] : undefined),
349
- fileExists,
350
- },
351
- });
352
- return (await models.checkAuth(providerId))?.source;
353
- }
354
- /** Pi registration starts background refreshes; wait for all of them before publishing this fresh runtime. */
355
- export async function withModelRegistration(runtime, register) {
356
- const refresh = runtime.refresh;
357
- const refreshes = [];
358
- runtime.refresh = (...args) => {
359
- const pending = refresh.apply(runtime, args);
360
- refreshes.push(pending);
361
- return pending;
362
- };
363
- try {
364
- const result = await register();
365
- await Promise.all(refreshes);
366
- return result;
367
- }
368
- finally {
369
- runtime.refresh = refresh;
370
- }
371
- }
372
- /**
373
- * A registry over the given credential store. What an agent runs on is built through `agentModels`
374
- * (agent-models.ts); this is for a registry over some OTHER store: none at all (`models`, a deploy check of what
375
- * ships), a trial key under test (login), or the catalog file a refresh writes.
376
- */
377
- export async function createPiModelRuntime(options) {
378
- const { models, create } = options.files ?? (await modelRuntimeFiles(options));
379
- const runtime = await ModelRuntime.create({ credentials: options.credentials, ...(await create()) });
380
- // A malformed models.json does NOT throw upstream — `create` resolves with the built-ins and parks the reason in
381
- // getError().
382
- const error = runtime.getError();
383
- if (error) {
384
- const origin = models?.merged
385
- ? `\n\nThat file merges ${models.merged.machine} with ${models.merged.definition} (the agent's own wins a provider id).`
386
- : models?.snapshotOf
387
- ? `\n\nThat file is ${models.snapshotOf} as read at startup.`
388
- : "";
389
- throw new Error(`${error}${origin}`);
390
- }
391
- await withModelRegistration(runtime, async () => {
392
- registerAccountModels(runtime);
393
- for (const provider of options.providers ?? [])
394
- runtime.registerNativeProvider(provider);
395
- });
396
- return runtime;
397
- }
398
- /**
399
- * EVERY provider whose `models.json` entry writes its key as a LITERAL rather than a `"$NAME"` reference or a
400
- * `"!cmd"`. The file ships inside the image, so a literal there is a credential in a readable layer.
401
- *
402
- * Read from the FILE, not from `getProviderAuthStatus`: that answers "what satisfies this provider right now" and
403
- * returns `stored` first, so a provider that has both an `auth.json` entry and a literal in the file would report
404
- * `stored` and the literal would go unreported. The question is what the definition DECLARES, and only the file
405
- * answers it. The three-way split mirrors pi's `configuredRequestAuthStatus`, which is not exported.
406
- *
407
- * The caller WARNS on this; it does not refuse. Whether a given string is a credential is the author's knowledge,
408
- * not the framework's — pi's own docs prescribe `"apiKey": "ollama"` for a keyless local server, and no static rule
409
- * separates that from a leaked key (an endpoint's reachability is not decidable from its URL either). FastAgent
410
- * gates what IT causes; what the author wrote into their own committed file, it reports.
411
- */
412
- export async function literalKeyProviders(agentDir) {
413
- return Object.entries(await definitionApiKeys(agentDir))
414
- .filter(([, apiKey]) => isLiteralKey(apiKey))
415
- .map(([id]) => id);
416
- }
417
- /**
418
- * How the agent's OWN models.json supplies `providerId`'s key, read from the file for the reason
419
- * {@link literalKeyProviders} gives: `reference` is the variable a `"$NAME"` reads (the value file must hold it),
420
- * `inFile` a key that travels with the definition itself (a literal, or a `"!command"` run on the box), and undefined
421
- * no apiKey there at all.
422
- */
423
- export async function definitionKeyOf(agentDir, providerId) {
424
- const apiKey = (await definitionApiKeys(agentDir))[providerId];
425
- if (typeof apiKey !== "string" || apiKey === "")
426
- return undefined;
427
- if (apiKey.startsWith("!") || isLiteralKey(apiKey))
428
- return { inFile: true };
429
- // Not a literal, so isLiteralKey's own pattern matched: there is a name to read.
430
- return { reference: apiKey.replaceAll("$$", "").match(/\$\{?([A-Za-z_][A-Za-z0-9_]*)/)?.[1] };
431
- }
432
- /** Each provider's `apiKey` as the agent's own models.json writes it. */
433
- async function definitionApiKeys(agentDir) {
434
- const file = join(agentDir, AGENT_MODELS_FILE);
435
- let raw;
436
- try {
437
- raw = await readFile(file, "utf8");
438
- }
439
- catch (error) {
440
- // No custom endpoints is the normal case; anything else (unreadable, a directory) is the caller's problem.
441
- if (error.code === "ENOENT")
442
- return {};
443
- throw error;
444
- }
445
- // Malformed JSON already threw out of createPiModelRuntime before this runs, so a parse failure here would be a
446
- // genuine surprise and must not be swallowed.
447
- const providers = JSON.parse(raw).providers ?? {};
448
- return Object.fromEntries(Object.entries(providers).map(([id, provider]) => [id, provider?.apiKey]));
449
- }
450
- /**
451
- * `!cmd` runs on the box; `$NAME` / `${NAME}` reads the environment; anything else is the key itself.
452
- *
453
- * `$$` is pi's escape for a literal `$`, so it is removed BEFORE looking for a reference — otherwise `"sk$$abc"`
454
- * (which resolves to the literal `sk$abc`) would read as a reference and go unmentioned.
455
- */
456
- function isLiteralKey(apiKey) {
457
- if (typeof apiKey !== "string" || apiKey === "" || apiKey.startsWith("!"))
458
- return false;
459
- return !/\$\{?[A-Za-z_]/.test(apiKey.replaceAll("$$", ""));
460
- }
461
- export function interactiveLoginKind(p) {
462
- if (interactiveAuth(p, "oauth"))
463
- return "oauth";
464
- return interactiveAuth(p, "api_key") ? "api_key" : "none";
465
- }
466
- /** THE rule for "can this method be signed in to interactively": the auth to run for it, or undefined. */
467
- export function interactiveAuth(provider, method) {
468
- return method === "oauth" ? provider.auth.oauth : provider.auth.apiKey?.login ? provider.auth.apiKey : undefined;
469
- }
470
- /** The providers a sign-in can target: pi's built-ins plus `extra`, composed exactly as {@link piModelsOver} does. */
471
- export function loginProviders(extra) {
472
- return piModelsOver(new InMemoryCredentialStore(), extra).getProviders();
473
- }
474
- /** Probe every provider's auth once (auth is provider-scoped, so any of its models works as the probe). */
475
- export async function providerAuthStatuses(models) {
476
- const statuses = new Map();
477
- for (const provider of models.getProviders()) {
478
- const [probe] = provider.getModels();
479
- if (!probe)
480
- continue;
481
- const login = interactiveLoginKind(provider);
482
- try {
483
- const auth = await models.getAuth(probe);
484
- statuses.set(provider.id, auth ? { state: "ready", source: auth.source } : { state: "unconfigured", login });
485
- }
486
- catch (error) {
487
- statuses.set(provider.id, { state: "broken", message: error.message, login });
488
- }
489
- }
490
- return statuses;
491
- }
492
- /**
493
- * Which source currently satisfies auth for `spec`, or undefined when none does (or the spec is not in `models`).
494
- * Rejects when resolving fails — a corrupt entry, a refresh the provider refused — which is a different answer from
495
- * "nothing configured" and must reach whoever reports it.
496
- */
497
- export async function probeAuthSource(models, spec) {
498
- const slash = spec.indexOf("/");
499
- if (slash < 1)
500
- return undefined;
501
- const model = models.getModel(spec.slice(0, slash), spec.slice(slash + 1));
502
- if (!model)
503
- return undefined;
504
- // A virtual selection authenticates the physical model after routing, not its catalog entry.
505
- if (model.api === "pi-virtual")
506
- return "virtual";
507
- return (await models.getAuth(model))?.source;
508
- }
509
- /** Quick-fail probe for a just-stored API key. */
510
- export async function probeApiKey(models, model, signal) {
511
- let status;
512
- let reply;
513
- try {
514
- reply = await models.complete(model, { messages: [{ role: "user", content: "ping", timestamp: Date.now() }] }, {
515
- maxTokens: 16,
516
- timeoutMs: 15_000,
517
- maxRetries: 0,
518
- ...(signal ? { signal } : {}),
519
- onResponse: (r) => {
520
- status = r.status;
521
- },
522
- });
523
- }
524
- catch (error) {
525
- // Thrown = before/around the request (auth resolution, transport setup) — not a provider verdict.
526
- return { state: "unknown", message: error.message };
527
- }
528
- if (reply.stopReason !== "error" && reply.stopReason !== "aborted")
529
- return { state: "ok" };
530
- const message = reply.errorMessage ?? `stopReason "${reply.stopReason}"`;
531
- const unauthorized = status === 401 || (status === undefined && /(^|\D)401(\D|$)/.test(message));
532
- return { state: unauthorized ? "rejected" : "unknown", message };
533
- }