@fastagent-sh/fastagent 0.28.0 → 0.30.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 (325) hide show
  1. package/README.md +10 -4
  2. package/dist/atomic-write.d.ts +4 -5
  3. package/dist/atomic-write.js +4 -5
  4. package/dist/authoring.d.ts +32 -15
  5. package/dist/authoring.js +147 -127
  6. package/dist/channels/agentcore-protocol.d.ts +8 -3
  7. package/dist/channels/agentcore-protocol.js +3 -1
  8. package/dist/channels/agentcore-service.d.ts +1 -0
  9. package/dist/channels/agentcore-service.js +14 -4
  10. package/dist/channels/agentcore.d.ts +9 -0
  11. package/dist/channels/agentcore.js +29 -4
  12. package/dist/channels/context.d.ts +2 -0
  13. package/dist/channels/context.js +5 -1
  14. package/dist/channels/feishu/feishu-api.d.ts +3 -1
  15. package/dist/channels/feishu/feishu-api.js +4 -0
  16. package/dist/channels/feishu/feishu.d.ts +0 -3
  17. package/dist/channels/feishu/feishu.js +10 -10
  18. package/dist/channels/feishu/register-app.d.ts +2 -0
  19. package/dist/channels/feishu/register-app.js +4 -6
  20. package/dist/channels/feishu/scaffold/channel.ts +32 -34
  21. package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
  22. package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
  23. package/dist/channels/feishu/setup-mode.d.ts +2 -12
  24. package/dist/channels/feishu/setup-mode.js +2 -22
  25. package/dist/channels/feishu/setup.d.ts +12 -0
  26. package/dist/channels/feishu/setup.js +197 -0
  27. package/dist/channels/kit/abortable.d.ts +8 -0
  28. package/dist/channels/kit/abortable.js +22 -0
  29. package/dist/channels/lark/lark.d.ts +0 -3
  30. package/dist/channels/lark/lark.js +0 -5
  31. package/dist/channels/lark/scaffold/channel.ts +33 -35
  32. package/dist/channels/lark/scaffold/lark-send.ts +5 -5
  33. package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
  34. package/dist/channels/lark/setup.d.ts +9 -0
  35. package/dist/channels/lark/setup.js +57 -0
  36. package/dist/channels/setup.d.ts +112 -0
  37. package/dist/channels/setup.js +31 -0
  38. package/dist/channels/slack/history.js +1 -1
  39. package/dist/channels/slack/manifest.d.ts +17 -7
  40. package/dist/channels/slack/manifest.js +12 -16
  41. package/dist/channels/slack/scaffold/channel.ts +44 -30
  42. package/dist/channels/slack/scaffold/slack-send.ts +8 -8
  43. package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
  44. package/dist/channels/slack/setup.d.ts +7 -0
  45. package/dist/channels/slack/setup.js +119 -0
  46. package/dist/channels/slack/slack-api.d.ts +2 -0
  47. package/dist/channels/slack/slack-api.js +4 -0
  48. package/dist/channels/slack/slack.d.ts +21 -2
  49. package/dist/channels/slack/slack.js +398 -338
  50. package/dist/channels/slack/socket.d.ts +57 -0
  51. package/dist/channels/slack/socket.js +305 -0
  52. package/dist/channels/telegram/polling.d.ts +27 -0
  53. package/dist/channels/telegram/polling.js +105 -0
  54. package/dist/channels/telegram/scaffold/channel.ts +52 -32
  55. package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
  56. package/dist/channels/telegram/setup.d.ts +2 -0
  57. package/dist/channels/telegram/setup.js +42 -0
  58. package/dist/channels/telegram/telegram-api.d.ts +27 -1
  59. package/dist/channels/telegram/telegram-api.js +7 -6
  60. package/dist/channels/telegram/telegram.d.ts +11 -3
  61. package/dist/channels/telegram/telegram.js +184 -157
  62. package/dist/channels/wait-health.js +1 -1
  63. package/dist/channels/webhook-evidence.d.ts +11 -0
  64. package/dist/channels/webhook-evidence.js +71 -0
  65. package/dist/channels/webhooks.d.ts +37 -0
  66. package/dist/channels/webhooks.js +70 -0
  67. package/dist/child-env.d.ts +16 -0
  68. package/dist/child-env.js +30 -0
  69. package/dist/cli/box-login.js +4 -1
  70. package/dist/cli/channel-app.d.ts +3 -0
  71. package/dist/cli/channel-app.js +35 -0
  72. package/dist/cli/channel-setup.d.ts +38 -0
  73. package/dist/cli/channel-setup.js +195 -0
  74. package/dist/cli/commands/add.d.ts +6 -14
  75. package/dist/cli/commands/add.js +45 -310
  76. package/dist/cli/commands/auth.js +8 -2
  77. package/dist/cli/commands/build.d.ts +5 -0
  78. package/dist/cli/commands/build.js +16 -0
  79. package/dist/cli/commands/channels.d.ts +19 -0
  80. package/dist/cli/commands/channels.js +230 -0
  81. package/dist/cli/commands/content.d.ts +7 -3
  82. package/dist/cli/commands/content.js +16 -10
  83. package/dist/cli/commands/deploy/agentcore.js +55 -23
  84. package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
  85. package/dist/cli/commands/deploy/channel-apps.js +30 -0
  86. package/dist/cli/commands/deploy/docker.js +32 -65
  87. package/dist/cli/commands/deploy/fly.js +28 -12
  88. package/dist/cli/commands/deploy/railway.js +38 -13
  89. package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
  90. package/dist/cli/commands/deploy/secrets-step.js +66 -0
  91. package/dist/cli/commands/deploy/shared.d.ts +16 -5
  92. package/dist/cli/commands/deploy/shared.js +25 -8
  93. package/dist/cli/commands/deploy.js +53 -67
  94. package/dist/cli/commands/dev.d.ts +2 -0
  95. package/dist/cli/commands/dev.js +15 -3
  96. package/dist/cli/commands/env.js +1 -1
  97. package/dist/cli/commands/info.d.ts +2 -0
  98. package/dist/cli/commands/info.js +63 -24
  99. package/dist/cli/commands/init.js +3 -1
  100. package/dist/cli/commands/invoke.d.ts +2 -0
  101. package/dist/cli/commands/invoke.js +20 -7
  102. package/dist/cli/commands/login.d.ts +0 -7
  103. package/dist/cli/commands/login.js +22 -10
  104. package/dist/cli/commands/logout.js +1 -1
  105. package/dist/cli/commands/mcp.d.ts +12 -0
  106. package/dist/cli/commands/mcp.js +164 -0
  107. package/dist/cli/commands/models.js +3 -2
  108. package/dist/cli/commands/schedules.d.ts +4 -2
  109. package/dist/cli/commands/schedules.js +14 -10
  110. package/dist/cli/commands/secrets.d.ts +14 -0
  111. package/dist/cli/commands/secrets.js +213 -0
  112. package/dist/cli/commands/start.d.ts +14 -14
  113. package/dist/cli/commands/start.js +46 -60
  114. package/dist/cli/content-view.d.ts +4 -6
  115. package/dist/cli/content-view.js +5 -11
  116. package/dist/cli/first-run-channels.d.ts +3 -0
  117. package/dist/cli/first-run-channels.js +63 -0
  118. package/dist/cli/instance.d.ts +41 -0
  119. package/dist/cli/instance.js +155 -0
  120. package/dist/cli/invoke-stream.d.ts +5 -2
  121. package/dist/cli/invoke-stream.js +7 -2
  122. package/dist/cli/program.js +339 -108
  123. package/dist/cli/serve.d.ts +4 -5
  124. package/dist/cli/serve.js +4 -7
  125. package/dist/cli/shared.d.ts +11 -2
  126. package/dist/cli/shared.js +32 -19
  127. package/dist/cli/ticket.js +50 -6
  128. package/dist/content/declare.d.ts +31 -37
  129. package/dist/content/declare.js +59 -60
  130. package/dist/content/file.d.ts +23 -12
  131. package/dist/content/file.js +48 -35
  132. package/dist/content/git.d.ts +0 -10
  133. package/dist/content/git.js +5 -39
  134. package/dist/content/resolve.d.ts +18 -22
  135. package/dist/content/resolve.js +32 -66
  136. package/dist/content/source.d.ts +7 -8
  137. package/dist/content/source.js +10 -28
  138. package/dist/credentials-page.d.ts +26 -0
  139. package/dist/credentials-page.js +185 -0
  140. package/dist/declared-secrets.d.ts +10 -3
  141. package/dist/declared-secrets.js +8 -4
  142. package/dist/deploy/agentcore/destroy.d.ts +4 -1
  143. package/dist/deploy/agentcore/destroy.js +26 -2
  144. package/dist/deploy/agentcore/forwarder.js +1 -1
  145. package/dist/deploy/agentcore/plan.d.ts +7 -21
  146. package/dist/deploy/agentcore/plan.js +47 -49
  147. package/dist/deploy/agentcore/run.d.ts +18 -10
  148. package/dist/deploy/agentcore/run.js +93 -70
  149. package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
  150. package/dist/deploy/agentcore/runtime-secrets.js +108 -0
  151. package/dist/deploy/agentcore/shell.js +12 -19
  152. package/dist/deploy/agentcore/sigv4.d.ts +7 -0
  153. package/dist/deploy/agentcore/sigv4.js +15 -0
  154. package/dist/deploy/agentcore/slr.d.ts +18 -0
  155. package/dist/deploy/agentcore/slr.js +27 -0
  156. package/dist/deploy/agentcore/store.d.ts +7 -0
  157. package/dist/deploy/agentcore/store.js +115 -0
  158. package/dist/deploy/api.d.ts +14 -14
  159. package/dist/deploy/api.js +26 -24
  160. package/dist/deploy/box-shell.d.ts +3 -1
  161. package/dist/deploy/build-context.d.ts +4 -4
  162. package/dist/deploy/build-context.js +11 -11
  163. package/dist/deploy/channel-ingress.d.ts +35 -9
  164. package/dist/deploy/channel-ingress.js +119 -22
  165. package/dist/deploy/container.d.ts +35 -10
  166. package/dist/deploy/container.js +80 -39
  167. package/dist/deploy/delivered-text.d.ts +13 -0
  168. package/dist/deploy/delivered-text.js +86 -0
  169. package/dist/deploy/docker/plan.d.ts +7 -8
  170. package/dist/deploy/docker/plan.js +31 -52
  171. package/dist/deploy/docker/run.d.ts +18 -43
  172. package/dist/deploy/docker/run.js +57 -79
  173. package/dist/deploy/file-store.d.ts +2 -0
  174. package/dist/deploy/file-store.js +17 -0
  175. package/dist/deploy/fly/plan.d.ts +3 -2
  176. package/dist/deploy/fly/plan.js +20 -9
  177. package/dist/deploy/fly/run.d.ts +18 -22
  178. package/dist/deploy/fly/run.js +25 -44
  179. package/dist/deploy/fly/store.d.ts +12 -0
  180. package/dist/deploy/fly/store.js +83 -0
  181. package/dist/deploy/hosts.d.ts +1 -2
  182. package/dist/deploy/hosts.js +1 -2
  183. package/dist/deploy/preflight.d.ts +18 -5
  184. package/dist/deploy/preflight.js +135 -63
  185. package/dist/deploy/railway/ignore.d.ts +8 -0
  186. package/dist/deploy/railway/ignore.js +57 -0
  187. package/dist/deploy/railway/plan.d.ts +1 -2
  188. package/dist/deploy/railway/plan.js +8 -6
  189. package/dist/deploy/railway/run.d.ts +21 -15
  190. package/dist/deploy/railway/run.js +32 -78
  191. package/dist/deploy/railway/store.d.ts +29 -0
  192. package/dist/deploy/railway/store.js +143 -0
  193. package/dist/deploy/runner.d.ts +5 -2
  194. package/dist/deploy/runner.js +4 -3
  195. package/dist/deploy/secret-store.d.ts +63 -0
  196. package/dist/deploy/secret-store.js +98 -0
  197. package/dist/deploy/secrets.d.ts +29 -46
  198. package/dist/deploy/secrets.js +49 -122
  199. package/dist/deploy/workspace.d.ts +6 -12
  200. package/dist/deploy/workspace.js +18 -94
  201. package/dist/dev-supervisor.js +6 -7
  202. package/dist/environment/declare.js +3 -2
  203. package/dist/environment/mise.d.ts +3 -1
  204. package/dist/environment/mise.js +4 -2
  205. package/dist/feishu.d.ts +1 -1
  206. package/dist/feishu.js +1 -1
  207. package/dist/harnesses/models/catalog.d.ts +1 -0
  208. package/dist/harnesses/models/catalog.js +1 -2
  209. package/dist/harnesses/models/environment.d.ts +7 -2
  210. package/dist/harnesses/models/environment.js +9 -5
  211. package/dist/harnesses/models/host.d.ts +0 -7
  212. package/dist/harnesses/models/host.js +10 -1
  213. package/dist/harnesses/models/login.d.ts +0 -8
  214. package/dist/harnesses/models/login.js +5 -4
  215. package/dist/harnesses/models/models.d.ts +7 -2
  216. package/dist/harnesses/models/models.js +28 -4
  217. package/dist/harnesses/pi-durable/agent.js +59 -38
  218. package/dist/harnesses/pi-durable/build.d.ts +12 -0
  219. package/dist/harnesses/pi-durable/build.js +270 -0
  220. package/dist/harnesses/pi-durable/built.d.ts +24 -0
  221. package/dist/harnesses/pi-durable/built.js +136 -0
  222. package/dist/harnesses/pi-durable/config.d.ts +81 -0
  223. package/dist/harnesses/pi-durable/config.js +266 -0
  224. package/dist/harnesses/pi-durable/define.d.ts +63 -13
  225. package/dist/harnesses/pi-durable/define.js +23 -2
  226. package/dist/harnesses/pi-durable/definition.d.ts +72 -0
  227. package/dist/harnesses/pi-durable/definition.js +143 -0
  228. package/dist/harnesses/pi-durable/events.d.ts +4 -3
  229. package/dist/harnesses/pi-durable/events.js +179 -136
  230. package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
  231. package/dist/harnesses/pi-durable/execution-env.js +11 -0
  232. package/dist/harnesses/pi-durable/mcp.d.ts +3 -0
  233. package/dist/harnesses/pi-durable/mcp.js +32 -0
  234. package/dist/harnesses/pi-durable/open.d.ts +36 -38
  235. package/dist/harnesses/pi-durable/open.js +97 -150
  236. package/dist/harnesses/pi-durable/reads.d.ts +0 -1
  237. package/dist/harnesses/pi-durable/reads.js +1 -3
  238. package/dist/harnesses/pi-durable/registry.d.ts +4 -6
  239. package/dist/harnesses/pi-durable/registry.js +29 -26
  240. package/dist/harnesses/pi-durable/release.d.ts +2 -2
  241. package/dist/harnesses/pi-durable/release.js +10 -7
  242. package/dist/harnesses/pi-durable/serve.d.ts +95 -15
  243. package/dist/harnesses/pi-durable/serve.js +151 -59
  244. package/dist/harnesses/pi-durable/sessions.js +15 -12
  245. package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
  246. package/dist/harnesses/pi-durable/text-sources.js +55 -0
  247. package/dist/harnesses/pi-durable/text.d.ts +16 -4
  248. package/dist/harnesses/pi-durable/text.js +115 -59
  249. package/dist/harnesses/pi-durable/wake.js +8 -8
  250. package/dist/http/client.js +9 -3
  251. package/dist/http/server.d.ts +1 -1
  252. package/dist/http/server.js +1 -1
  253. package/dist/index.d.ts +11 -4
  254. package/dist/index.js +8 -1
  255. package/dist/instance-name.d.ts +7 -0
  256. package/dist/instance-name.js +26 -0
  257. package/dist/lark.d.ts +1 -1
  258. package/dist/lark.js +1 -1
  259. package/dist/locked-file.d.ts +2 -2
  260. package/dist/locked-file.js +8 -3
  261. package/dist/mcp/auth.d.ts +22 -0
  262. package/dist/mcp/auth.js +180 -0
  263. package/dist/mcp/client.d.ts +15 -0
  264. package/dist/mcp/client.js +179 -0
  265. package/dist/mcp/config.d.ts +86 -0
  266. package/dist/mcp/config.js +225 -0
  267. package/dist/mcp/grants.d.ts +4 -0
  268. package/dist/mcp/grants.js +132 -0
  269. package/dist/mcp/host.d.ts +19 -0
  270. package/dist/mcp/host.js +128 -0
  271. package/dist/mcp/network.d.ts +12 -0
  272. package/dist/mcp/network.js +150 -0
  273. package/dist/mcp/sign-in.d.ts +2 -0
  274. package/dist/mcp/sign-in.js +204 -0
  275. package/dist/mcp-api.d.ts +107 -0
  276. package/dist/mcp-api.js +10 -0
  277. package/dist/paths.d.ts +22 -8
  278. package/dist/paths.js +44 -11
  279. package/dist/runtime-websocket.d.ts +14 -0
  280. package/dist/runtime-websocket.js +39 -0
  281. package/dist/scaffold/add-channel.d.ts +9 -16
  282. package/dist/scaffold/add-channel.js +13 -83
  283. package/dist/scaffold/templates/env.example +4 -5
  284. package/dist/scaffold/templates/fastagent.config.ts +20 -14
  285. package/dist/scaffold/templates/gitignore +9 -6
  286. package/dist/scaffold/templates.d.ts +6 -2
  287. package/dist/scaffold/templates.js +7 -2
  288. package/dist/scaffold/vendor-skill.js +4 -2
  289. package/dist/service.d.ts +44 -6
  290. package/dist/service.js +110 -10
  291. package/dist/serving-command.d.ts +10 -0
  292. package/dist/serving-command.js +1 -0
  293. package/dist/setup-page.d.ts +51 -0
  294. package/dist/setup-page.js +149 -0
  295. package/dist/slack.d.ts +1 -1
  296. package/dist/slack.js +1 -1
  297. package/dist/strip-edges.d.ts +10 -0
  298. package/dist/strip-edges.js +22 -0
  299. package/dist/telegram.d.ts +1 -1
  300. package/dist/telegram.js +1 -1
  301. package/dist/tunnel.d.ts +0 -1
  302. package/dist/tunnel.js +2 -8
  303. package/dist/value-file.d.ts +12 -6
  304. package/dist/value-file.js +36 -25
  305. package/dist/version.d.ts +7 -1
  306. package/dist/version.js +9 -3
  307. package/package.json +8 -6
  308. package/dist/channels/lark/onboard.d.ts +0 -36
  309. package/dist/channels/lark/onboard.js +0 -56
  310. package/dist/channels/slack/config-api.d.ts +0 -69
  311. package/dist/channels/slack/config-api.js +0 -162
  312. package/dist/channels/slack/onboard.d.ts +0 -40
  313. package/dist/channels/slack/onboard.js +0 -136
  314. package/dist/channels/slack/onboarding-state.d.ts +0 -31
  315. package/dist/channels/slack/onboarding-state.js +0 -64
  316. package/dist/channels/slack/register-webhook.d.ts +0 -12
  317. package/dist/channels/slack/register-webhook.js +0 -67
  318. package/dist/channels/slack/setup-server.d.ts +0 -32
  319. package/dist/channels/slack/setup-server.js +0 -201
  320. package/dist/cli/add-feishu.d.ts +0 -50
  321. package/dist/cli/add-feishu.js +0 -367
  322. package/dist/cli/add-slack.d.ts +0 -18
  323. package/dist/cli/add-slack.js +0 -216
  324. package/dist/harnesses/pi-durable/parked.d.ts +0 -18
  325. package/dist/harnesses/pi-durable/parked.js +0 -27
@@ -5,7 +5,7 @@
5
5
  import { spawn } from "node:child_process";
6
6
  import { relative, sep } from "node:path";
7
7
  import { watch as watchTree } from "chokidar";
8
- import { AGENT_CONFIG_FILE, CONTEXT_FILE, LOCAL_CONTEXT_FILE, resolveStateRoot, isUnderDir } from "./paths.js";
8
+ import { AGENT_CONFIG_FILE, CONTEXT_FILE, INSTANCE_CONFIG_FILE, isUnderDir } from "./paths.js";
9
9
  import { dotEnvPath } from "./value-file.js";
10
10
  import { log } from "./log.js";
11
11
  import { openExternalUrl } from "./open-url.js";
@@ -14,7 +14,7 @@ import { activeWork } from "./channels/busy.js";
14
14
  import { announceWebhooks, startCloudflareTunnel } from "./tunnel.js";
15
15
  /** The agent's code (its tools, extensions, channels and schedules), loaded ONCE per worker: a restart is its only re-read. */
16
16
  const CODE_DIR = "src";
17
- const WATCHED_HINT = `${CODE_DIR}/, package.json, fastagent.config.ts, context.json, context.local.json, .secrets/.env`;
17
+ const WATCHED_HINT = `${CODE_DIR}/, package.json, fastagent.config.ts, context.json, fastagent.json, .secrets/.env`;
18
18
  /** chokidar `ignored` matcher for the narrow watch scope (true = ignore), rooted at the AGENT DIR. */
19
19
  export function devWatchIgnored(root, envFile) {
20
20
  // The `.env` is allow-listed by its RESOLVED path, not by the `.secrets` name.
@@ -25,9 +25,9 @@ export function devWatchIgnored(root, envFile) {
25
25
  const rel = relative(root, path);
26
26
  // Code inputs at the agent dir root: config, package.json, and the code loaded once per worker (a restart is
27
27
  // their only re-read).
28
- if (rel === AGENT_CONFIG_FILE || rel === CONTEXT_FILE || rel === LOCAL_CONTEXT_FILE)
28
+ if (rel === AGENT_CONFIG_FILE || rel === CONTEXT_FILE)
29
29
  return false;
30
- if (rel === "package.json")
30
+ if (rel === "package.json" || rel === INSTANCE_CONFIG_FILE)
31
31
  return false;
32
32
  const segments = rel.split(sep);
33
33
  if (segments[0] === CODE_DIR)
@@ -95,7 +95,6 @@ export async function runDevSupervisor(agentDir, options = {}) {
95
95
  tunnel = t;
96
96
  void announceWebhooks(agentDir, t.url, declaredChannels(m.routeChannels ?? []), {
97
97
  openUrl: openExternalUrl,
98
- stateRoot: resolveStateRoot(agentDir),
99
98
  });
100
99
  }
101
100
  });
@@ -145,9 +144,9 @@ export async function runDevSupervisor(agentDir, options = {}) {
145
144
  });
146
145
  watcher.on("error", (error) => log.warn(`[fastagent] file watching error (${error.message}); some edits may need a manual restart`));
147
146
  log.info(`[fastagent] watching ${WATCHED_HINT} — code edits restart the dev worker (--no-watch to disable); SYSTEM.md, AGENTS.md, skills/ and prompts/ go live without a restart`);
148
- // FASTAGENT_SECRETS_DIR can move the `.env` OUT of the agent dir entirely.
147
+ // FASTAGENT_SECRETS_DIR or --env-file can move the `.env` OUT of the agent dir entirely.
149
148
  if (!isUnderDir(dotEnvPath(agentDir), agentDir)) {
150
- log.warn(`[fastagent] .env lives outside the agent dir (FASTAGENT_SECRETS_DIR → ${dotEnvPath(agentDir)}) — it is NOT watched; restart dev after editing it`);
149
+ log.warn(`[fastagent] .env lives outside the agent dir (FASTAGENT_SECRETS_DIR or --env-file → ${dotEnvPath(agentDir)}) — it is NOT watched; restart dev after editing it`);
151
150
  }
152
151
  const shutdown = () => {
153
152
  worker?.kill("SIGTERM");
@@ -61,7 +61,7 @@ function declareVariables(raw) {
61
61
  if (!isEnvName(name))
62
62
  throw new Error(`${at} is not an environment variable name`);
63
63
  if (isDeployOwned(name)) {
64
- throw new Error(`${at} is set by the deployment itself, so it cannot be declared: a value file's copy never travels`);
64
+ throw new Error(`${at} is set by the deployment itself, so it cannot be declared`);
65
65
  }
66
66
  if (!variable || typeof variable !== "object" || Array.isArray(variable)) {
67
67
  throw new Error(`${at} must be { "description": "what it is for" }: its value goes in the stage's value file ` +
@@ -87,8 +87,9 @@ function declareVariables(raw) {
87
87
  export function declaredVariables(environment) {
88
88
  return Object.entries(environment?.variables ?? {}).map(([name, { description }]) => ({
89
89
  name,
90
- source: CONTEXT_FILE,
90
+ declaredIn: CONTEXT_FILE,
91
91
  description,
92
+ optional: false,
92
93
  }));
93
94
  }
94
95
  function declarePackages(raw) {
@@ -48,4 +48,6 @@ export declare function installPackages(agentDir: string, bin: string, packages:
48
48
  * mise itself is not put on the PATH. An agent without packages borrows the machine's commands (no variables). Apt
49
49
  * packages are the image's to install; one missing here is said, with how.
50
50
  */
51
- export declare function prepareEnvironment(agentDir: string, env: NodeJS.ProcessEnv): Promise<Record<string, string>>;
51
+ export declare function prepareEnvironment(agentDir: string, env: NodeJS.ProcessEnv,
52
+ /** What the packages' `PATH` is computed over: the agent's own, which is not the installer's. */
53
+ commandsEnv?: NodeJS.ProcessEnv): Promise<Record<string, string>>;
@@ -256,7 +256,9 @@ function missingMise(agentDir) {
256
256
  * mise itself is not put on the PATH. An agent without packages borrows the machine's commands (no variables). Apt
257
257
  * packages are the image's to install; one missing here is said, with how.
258
258
  */
259
- export async function prepareEnvironment(agentDir, env) {
259
+ export async function prepareEnvironment(agentDir, env,
260
+ /** What the packages' `PATH` is computed over: the agent's own, which is not the installer's. */
261
+ commandsEnv = env) {
260
262
  const declared = loadEnvironment(agentDir);
261
263
  const missingApt = missingAptPackages(declared?.aptPackages ?? []);
262
264
  if (missingApt && missingApt.length > 0)
@@ -268,5 +270,5 @@ export async function prepareEnvironment(agentDir, env) {
268
270
  if (!bin)
269
271
  throw missingMise(agentDir);
270
272
  await installPackages(agentDir, bin, packages, [], env);
271
- return packagesEnvironment(agentDir, bin, packageSpecs(packages), env);
273
+ return packagesEnvironment(agentDir, bin, packageSpecs(packages), commandsEnv);
272
274
  }
package/dist/feishu.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/feishu` — the canonical Feishu (open.feishu.cn) bot-channel surface. */
2
- export { feishuChannel, feishuIngress, feishuWebSocketChannel, defaultFeishuRoute, feishuEnvelope, type FeishuChannelOptions, type FeishuWebSocketChannelOptions, type FeishuMessageEvent, type FeishuMessage, type FeishuRoute, type FeishuFailure, } from "./channels/feishu/feishu.ts";
2
+ export { feishuChannel, feishuWebSocketChannel, defaultFeishuRoute, feishuEnvelope, type FeishuChannelOptions, type FeishuWebSocketChannelOptions, type FeishuMessageEvent, type FeishuMessage, type FeishuRoute, type FeishuFailure, } from "./channels/feishu/feishu.ts";
3
3
  export { feishuThreads, feishuTransport, type FeishuTransport } from "./channels/feishu/shared-api.ts";
package/dist/feishu.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/feishu` — the canonical Feishu (open.feishu.cn) bot-channel surface. */
2
- export { feishuChannel, feishuIngress, feishuWebSocketChannel, defaultFeishuRoute, feishuEnvelope, } from "./channels/feishu/feishu.js";
2
+ export { feishuChannel, feishuWebSocketChannel, defaultFeishuRoute, feishuEnvelope, } from "./channels/feishu/feishu.js";
3
3
  export { feishuThreads, feishuTransport } from "./channels/feishu/shared-api.js";
@@ -11,6 +11,7 @@ export interface Catalogs {
11
11
  */
12
12
  refresh(models: Models, options?: {
13
13
  force?: boolean;
14
+ offline?: boolean;
14
15
  }): Promise<string | undefined>;
15
16
  }
16
17
  /** The catalogs kept in `dir`, or, without one, for this process alone. `url` and `timeoutMs` are for tests. */
@@ -110,8 +110,7 @@ export function catalogsIn(dir, options = {}) {
110
110
  };
111
111
  },
112
112
  fetched,
113
- async refresh(models, { force = false } = {}) {
114
- const offline = process.env[OFFLINE_VARIABLE] === "1";
113
+ async refresh(models, { force = false, offline = process.env[OFFLINE_VARIABLE] === "1" } = {}) {
115
114
  const signal = AbortSignal.timeout(timeoutMs);
116
115
  const why = (error) => signal.aborted ? `no answer within ${timeoutMs / 1000}s` : messageOf(error);
117
116
  const [published, own] = await Promise.all([
@@ -35,8 +35,9 @@ export interface ModelEnvironment {
35
35
  * The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: the named or global
36
36
  * credentials file). A supplied `credentialStore` is used as given; otherwise the files: the directory's own layers
37
37
  * ({@link resolveAuthLayers}), or the named file, or the global one. `providers` are the definition's. `environment` is
38
- * what the agent's environment adds to this process's: its keys, the requests' settings, and where its files are
39
- * (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) are all read there first. The model lists are refreshed before it
38
+ * the one source of the values a provider authenticates with and of the requests' settings, which this process's
39
+ * environment never adds to; where its files are (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) is still read from
40
+ * this process's first. The model lists are refreshed before it
40
41
  * resolves, kept in the instance's state root (`stateRoot`, else the directory's); `force` asks the network even when
41
42
  * they are fresh.
42
43
  */
@@ -44,5 +45,9 @@ export declare function modelEnvironment(agentDir: string | undefined, source?:
44
45
  force?: boolean;
45
46
  stateRoot?: string;
46
47
  environment?: Readonly<Record<string, string>>;
48
+ /** The environment `FASTAGENT_AUTH_PATH` and `FASTAGENT_STATE_DIR` are read from. Default: this process's. */
49
+ env?: Readonly<Record<string, string | undefined>>;
50
+ /** Refresh the lists without the network. Default: `FASTAGENT_OFFLINE` is `1` in this process's environment. */
51
+ offline?: boolean;
47
52
  }): Promise<ModelEnvironment>;
48
53
  export {};
@@ -15,14 +15,15 @@ import { environmentAuth, modelsOver, probeAuthSource } from "./models.js";
15
15
  * The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: the named or global
16
16
  * credentials file). A supplied `credentialStore` is used as given; otherwise the files: the directory's own layers
17
17
  * ({@link resolveAuthLayers}), or the named file, or the global one. `providers` are the definition's. `environment` is
18
- * what the agent's environment adds to this process's: its keys, the requests' settings, and where its files are
19
- * (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) are all read there first. The model lists are refreshed before it
18
+ * the one source of the values a provider authenticates with and of the requests' settings, which this process's
19
+ * environment never adds to; where its files are (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) is still read from
20
+ * this process's first. The model lists are refreshed before it
20
21
  * resolves, kept in the instance's state root (`stateRoot`, else the directory's); `force` asks the network even when
21
22
  * they are fresh.
22
23
  */
23
24
  export async function modelEnvironment(agentDir, source = {}, providers = [], options = {}) {
24
25
  const environment = options.environment ?? {};
25
- const env = { ...process.env, ...environment };
26
+ const env = { ...(options.env ?? process.env), ...environment };
26
27
  assertOneCredentialSource(source);
27
28
  const files = source.credentialStore
28
29
  ? undefined
@@ -34,7 +35,10 @@ export async function modelEnvironment(agentDir, source = {}, providers = [], op
34
35
  const stateRoot = options.stateRoot ?? (agentDir === undefined ? undefined : resolveStateRoot(agentDir, env));
35
36
  const catalogs = catalogsIn(stateRoot === undefined ? undefined : join(stateRoot, "models"));
36
37
  const models = modelsOver(credentials, providers, catalogs, environment);
37
- const catalogProblem = await catalogs.refresh(models, { force: options.force === true });
38
+ const catalogProblem = await catalogs.refresh(models, {
39
+ force: options.force === true,
40
+ ...(options.offline === undefined ? {} : { offline: options.offline }),
41
+ });
38
42
  if (catalogProblem !== undefined)
39
43
  log.warn(`[fastagent] ${catalogProblem}`);
40
44
  return {
@@ -58,7 +62,7 @@ export async function modelEnvironment(agentDir, source = {}, providers = [], op
58
62
  }
59
63
  // `read` never refreshes: this is the kind on file, whatever became of it.
60
64
  const stored = (await credentials.read(provider))?.type;
61
- const fromEnvironment = stored === undefined ? undefined : (await environmentAuth(provider, env, undefined, providers))?.source;
65
+ const fromEnvironment = stored === undefined ? undefined : (await environmentAuth(provider, environment, undefined, providers))?.source;
62
66
  const shadowed = resolved.source !== undefined && fromEnvironment !== resolved.source ? fromEnvironment : undefined;
63
67
  return {
64
68
  ...(store ? { path: await store.layerOf(provider) } : {}),
@@ -1,10 +1,3 @@
1
- /**
2
- * What an embedding host calls for model credentials (sign-in-api.ts has the types): a store kept where the host
3
- * chooses, a sign-in in its own UI, and what authenticates a provider. Thin over what `fastagent login`, `logout` and
4
- * `auth check` run, so each rule keeps one implementation. The one translation between FastAgent's types and pi-ai's
5
- * is here, written without casts and with every function checked as a function, so it stops compiling when the two
6
- * drift apart.
7
- */
8
1
  import type { CredentialStore as PiCredentialStore } from "@earendil-works/pi-ai";
9
2
  import type { CredentialStore, ProviderAuth, SignInOption, SignInRequest, SignInResult } from "../../sign-in-api.ts";
10
3
  /**
@@ -1,3 +1,11 @@
1
+ /**
2
+ * What an embedding host calls for model credentials (sign-in-api.ts has the types): a store kept where the host
3
+ * chooses, a sign-in in its own UI, and what authenticates a provider. Thin over what `fastagent login`, `logout` and
4
+ * `auth check` run, so each rule keeps one implementation. The one translation between FastAgent's types and pi-ai's
5
+ * is here, written without casts and with every function checked as a function, so it stops compiling when the two
6
+ * drift apart.
7
+ */
8
+ import { definedValues } from "../../declared-secrets.js";
1
9
  import { resolveOverridePath } from "../../paths.js";
2
10
  import { fastagentCredentialStore } from "./credentials.js";
3
11
  import { loginOptionsOver, loginOver } from "./login.js";
@@ -74,7 +82,8 @@ export function signIn(request) {
74
82
  * does; rejects when resolving fails, such as a refresh the provider refused.
75
83
  */
76
84
  export async function providerAuth(credentials, provider) {
77
- const models = modelsOver(piCredentials(credentials));
85
+ // The host's own environment: the answer is what a request this process sends authenticates with.
86
+ const models = modelsOver(piCredentials(credentials), [], undefined, definedValues(process.env));
78
87
  if (models.getProvider(provider) === undefined)
79
88
  throw new Error(`unknown provider "${provider}"`);
80
89
  const checked = await models.checkAuth(provider);
@@ -1,11 +1,3 @@
1
- /**
2
- * Signing in to a MODEL PROVIDER, for any front end: {@link loginOver} runs one provider's flow over pi-ai's own
3
- * `AuthInteraction` and persists the credential through the same {@link fastagentCredentialStore} the runtime reads
4
- * (one writer, one lock/corruption semantics); {@link loginOptionsOver} is the list a picker offers. An embedding host
5
- * reaches both through host.ts (`signIn`, `signInOptions`); `fastagent login` is a front end of its own
6
- * ({@link loginFlow}: terminal menus, then {@link loginOver}); `login --deployment` drives the same flow on a deployed
7
- * box over a relay (cli/login-relay.ts).
8
- */
9
1
  import { type AuthInteraction, type CredentialStore, type Provider } from "@earendil-works/pi-ai";
10
2
  import { type SignInMethod, type SignInOption, type SignInResult } from "../../sign-in-api.ts";
11
3
  /** A sign-in that cannot start as asked: an unknown provider, or a choice a caller without menus did not make. */
@@ -6,6 +6,7 @@
6
6
  * ({@link loginFlow}: terminal menus, then {@link loginOver}); `login --deployment` drives the same flow on a deployed
7
7
  * box over a relay (cli/login-relay.ts).
8
8
  */
9
+ import { definedValues } from "../../declared-secrets.js";
9
10
  import { InMemoryCredentialStore, } from "@earendil-works/pi-ai";
10
11
  import { randomUUID } from "node:crypto";
11
12
  import { mkdir, readFile } from "node:fs/promises";
@@ -36,8 +37,8 @@ export async function loginOptionsOver(credentials, providers) {
36
37
  // ONE read of the store: `list` is metadata only, and reading per provider would repeat a corrupt file's warning for
37
38
  // every provider.
38
39
  const held = new Map((await storeOf(credentials).list()).map((info) => [info.providerId, info.type]));
39
- // The environment alone, over no store: what authenticates a provider nothing is stored for.
40
- const environment = modelsOver(new InMemoryCredentialStore(), providers);
40
+ // The environment alone, over no store: what authenticates a provider nothing is stored for, here the host's own.
41
+ const environment = modelsOver(new InMemoryCredentialStore(), providers, undefined, definedValues(process.env));
41
42
  const offered = [];
42
43
  for (const provider of loginProviders(providers)) {
43
44
  const methods = ["oauth", "api_key"].filter((method) => provider.auth[method === "oauth" ? "oauth" : "apiKey"]);
@@ -129,7 +130,7 @@ export async function loginOver(request, internals = {}) {
129
130
  // The key under test lives here, not in the file, until it passes.
130
131
  const trial = new InMemoryCredentialStore();
131
132
  // Only a key needs the registry: it is checked with one request before it is kept.
132
- const models = method === "api_key" ? modelsOver(trial, providers) : undefined;
133
+ const models = method === "api_key" ? modelsOver(trial, providers, undefined, definedValues(process.env)) : undefined;
133
134
  const store = request.credentialStore ?? fastagentCredentialStore(request.authPath);
134
135
  await store.modify(provider.id, async () => undefined);
135
136
  const signal = interaction.signal ?? new AbortController().signal;
@@ -235,7 +236,7 @@ export const PI_DEFAULT_MODELS = {
235
236
  "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
236
237
  "ant-ling": "Ring-2.6-1T",
237
238
  anthropic: "claude-opus-4-8",
238
- openai: "gpt-5.5",
239
+ openai: "gpt-6.1-sol",
239
240
  azure: "gpt-5.4",
240
241
  "openai-codex": "gpt-6.1-sol",
241
242
  radius: "balanced",
@@ -11,8 +11,13 @@ export declare function providerOf(spec: string): string;
11
11
  /** Every chat model `models` offers, as `"provider/modelId"`, sorted. */
12
12
  export declare function listModels(models: Models): string[];
13
13
  /**
14
- * The built-ins plus `providers` (a same id replaces a built-in), over any credential store, in `environment`: what
15
- * the agent's environment adds to this process's, read before it by a provider's auth and by each request. With
14
+ * What to say when `spec` names no model: the models of its provider that read most like it, or the providers there are
15
+ * when the provider is the unknown part, and the command that lists them.
16
+ */
17
+ export declare function modelHint(specs: readonly string[], spec: string): string;
18
+ /**
19
+ * The built-ins plus `providers` (a same id replaces a built-in), over any credential store, in `environment`: the
20
+ * values a provider's auth reads its keys from, and each request is given. With
16
21
  * `catalogs`, the lists its refresh brings up to date; without, the ones this pi-ai was built with.
17
22
  */
18
23
  export declare function modelsOver(credentials: CredentialStore, providers?: readonly Provider[], catalogs?: Catalogs, environment?: Readonly<Record<string, string>>): Models;
@@ -20,6 +20,30 @@ export function listModels(models) {
20
20
  }
21
21
  return specs.sort();
22
22
  }
23
+ const tokensOf = (text) => text
24
+ .toLowerCase()
25
+ .split(/[^a-z0-9]+/)
26
+ .filter(Boolean);
27
+ /**
28
+ * What to say when `spec` names no model: the models of its provider that read most like it, or the providers there are
29
+ * when the provider is the unknown part, and the command that lists them.
30
+ */
31
+ export function modelHint(specs, spec) {
32
+ const provider = providerOf(spec);
33
+ const own = specs.filter((candidate) => candidate.startsWith(`${provider}/`));
34
+ if (own.length === 0) {
35
+ const providers = [...new Set(specs.map(providerOf))];
36
+ return `there is no provider "${provider}"; the providers are ${providers.join(", ")} (\`fastagent models\` lists their models)`;
37
+ }
38
+ const wanted = new Set(tokensOf(spec.slice(provider.length + 1)));
39
+ const overlap = (candidate) => tokensOf(candidate.slice(provider.length + 1)).filter((t) => wanted.has(t)).length;
40
+ const closest = own
41
+ .map((candidate) => ({ candidate, score: overlap(candidate) }))
42
+ .sort((a, b) => b.score - a.score || a.candidate.localeCompare(b.candidate))
43
+ .slice(0, 5)
44
+ .map(({ candidate }) => candidate);
45
+ return `${provider}'s models include ${closest.join(", ")} (\`fastagent models ${provider}\` lists all ${own.length})`;
46
+ }
23
47
  /**
24
48
  * pi's own reading of an environment (a blank variable is unset) through `read` instead of this process's: an agent's
25
49
  * environment is its own when the process runs several. Files a source points at are this machine's unless
@@ -61,15 +85,15 @@ function inEnvironment(provider, environment) {
61
85
  };
62
86
  }
63
87
  /**
64
- * The built-ins plus `providers` (a same id replaces a built-in), over any credential store, in `environment`: what
65
- * the agent's environment adds to this process's, read before it by a provider's auth and by each request. With
88
+ * The built-ins plus `providers` (a same id replaces a built-in), over any credential store, in `environment`: the
89
+ * values a provider's auth reads its keys from, and each request is given. With
66
90
  * `catalogs`, the lists its refresh brings up to date; without, the ones this pi-ai was built with.
67
91
  */
68
92
  export function modelsOver(credentials, providers = [], catalogs, environment = {}) {
69
93
  const models = createModels({
70
94
  credentials,
71
- // As a request reads it (pi's getProviderEnvValue): the request's env first, then this process's.
72
- authContext: authContextIn((name) => environment[name] || process.env[name]),
95
+ // The values given, and none of this process's: an instance's keys are its own, whoever else runs here.
96
+ authContext: authContextIn((name) => environment[name]),
73
97
  ...(catalogs ? { modelsStore: catalogs.fetched } : {}),
74
98
  });
75
99
  for (const builtIn of builtinProviders()) {
@@ -23,7 +23,6 @@ import { admitSettings } from "./defaults.js";
23
23
  import { USER_ENTRY, imageAt, isPublished, parseImageRef } from "./entries.js";
24
24
  import { sessionEvents } from "./events.js";
25
25
  import { storageIds } from "./ids.js";
26
- import { giveUpOnParked } from "./parked.js";
27
26
  import { isInput, queuedInputs, requestIdFor, usageOfState } from "./messages.js";
28
27
  import { durableReads } from "./reads.js";
29
28
  import { releaseWaitingQueues } from "./release.js";
@@ -38,13 +37,12 @@ export async function openDurableAgent(options) {
38
37
  registry: options.registry,
39
38
  ...(options.settings === undefined ? {} : { settings: options.settings }),
40
39
  ...(options.env === undefined ? {} : { env: options.env }),
41
- onReport: (error) => log.warn(`[fastagent] pi-durable reported: ${String(error)}`),
40
+ onReport: (report) => log.warn(`[fastagent] pi-durable reported ${report.kind}: ${String(report.error)}`),
42
41
  }, context);
43
42
  const lifetime = Scope.makeUnsafe();
44
43
  try {
45
44
  const ids = await storageIds(harness, context);
46
45
  const sessions = await Effect.runPromise(Scope.provide(trackSessions(harness, options.storage, context), lifetime));
47
- await giveUpOnParked(harness, context);
48
46
  // Before `resume()`, which runs an earlier process's work. An abort enables the scheduler too, but commits first, so
49
47
  // the aborted work does not run (measured).
50
48
  const unsettled = (await harness.inspect(context)).submissions.filter(isInput);
@@ -215,58 +213,81 @@ function durableAgent(harness, ids, sessions, lifetime, options) {
215
213
  sessionId: ids.encode(conversation.id),
216
214
  ...(messageId === undefined ? {} : { messageId: ids.encode(messageId) }),
217
215
  });
218
- /** Admission: a key's lookup, the limits, the settings and the submission, one at a time per process. */
216
+ /**
217
+ * Admission, one at a time per process: one commit that looks the key up, then checks the process's limit and the
218
+ * queue, takes the defaults and submits, so it stores all of that or none of it.
219
+ */
219
220
  const admit = (request) => Semaphore.withPermits(admission, 1)(Effect.gen(function* () {
220
221
  const requestId = requestIdFor(request.idempotencyKey);
221
222
  // Before anything is written: reading or expanding a command reads the definition's files, and may throw.
222
223
  const { content, sent } = expandCommand(request.content, commands());
223
224
  const conversation = yield* conversationOf(request.sessionId);
224
- if (request.idempotencyKey !== undefined) {
225
- const seen = yield* reads.submissionByKey(conversation, requestId);
226
- if (seen !== undefined)
227
- return { message: yield* reads.message(seen), replayed: true };
228
- }
229
- if ((yield* inFlight) >= inFlightLimit) {
230
- return yield* agentError("busy", `This agent already holds ${inFlightLimit} messages`, {
231
- reason: "capacity",
232
- });
233
- }
234
- if (request.whenBusy !== "reject") {
235
- const inbox = yield* reads.inbox(conversation);
236
- if (queuedInputs(inbox).length >= LIMITS.queuedPerSession) {
237
- return yield* agentError("busy", `The session already has ${LIMITS.queuedPerSession} queued messages`, {
238
- reason: "queue_full",
239
- });
240
- }
241
- }
242
- const refused = yield* port(() => conversation.commit((tx) => admitSettings(tx, conversation.id, defaults, has), context));
243
- if (refused !== undefined)
244
- return yield* unanswerable(refused);
225
+ // Read before the commit, which cannot read the harness, and applied after the key: a key the session has
226
+ // seen answers with its message whatever the process holds (SPEC.md §5.2).
227
+ const atCapacity = (yield* inFlight) >= inFlightLimit;
245
228
  const data = { requestId, ...(sent === undefined ? {} : { sent }) };
246
- let messageId;
247
- return yield* Effect.gen(function* () {
248
- const submission = yield* port(() => conversation.submit({
229
+ let submitting = false;
230
+ const admitted = yield* port(() => conversation.commit(async (tx) => {
231
+ if (request.idempotencyKey !== undefined) {
232
+ const seen = await tx.submissionByRequest(conversation.id, requestId);
233
+ if (isInput(seen))
234
+ return { kind: "seen", seen };
235
+ }
236
+ if (atCapacity)
237
+ return { kind: "capacity" };
238
+ if (request.whenBusy !== "reject") {
239
+ const inbox = await tx.doc(InboxDoc, conversation.id);
240
+ if (queuedInputs(inbox).length >= LIMITS.queuedPerSession)
241
+ return { kind: "full" };
242
+ }
243
+ const refused = await admitSettings(tx, conversation.id, defaults, has);
244
+ if (refused !== undefined)
245
+ return { kind: "refused", refused };
246
+ submitting = true;
247
+ const id = await conversation.submit(tx, {
249
248
  type: "input",
250
249
  content,
251
250
  requestId,
252
251
  whenBusy: request.whenBusy === "steer" ? "steer" : request.whenBusy === "reject" ? "reject" : "followUp",
253
252
  entry: { kind: USER_ENTRY, data },
254
- }, context));
255
- messageId = submission.id;
253
+ });
254
+ return { kind: "admitted", id };
255
+ }, context)).pipe(
256
+ // Once the submission is in the commit, only pi-durable's explicit refusal proves nothing was recorded.
257
+ Effect.catchCause((cause) => {
258
+ const error = portError(cause);
259
+ if (error instanceof ConversationBusy) {
260
+ return Effect.fail(agentError("busy", "The session is answering another message", { reason: "running" }));
261
+ }
262
+ return submitting ? Effect.fail(unconfirmed(conversation, undefined, error)) : Effect.failCause(cause);
263
+ }));
264
+ if (admitted.kind === "seen")
265
+ return { message: yield* reads.message(admitted.seen), replayed: true };
266
+ if (admitted.kind === "capacity") {
267
+ return yield* agentError("busy", `This agent already holds ${inFlightLimit} messages`, {
268
+ reason: "capacity",
269
+ });
270
+ }
271
+ if (admitted.kind === "full") {
272
+ return yield* agentError("busy", `The session already has ${LIMITS.queuedPerSession} queued messages`, {
273
+ reason: "queue_full",
274
+ });
275
+ }
276
+ if (admitted.kind === "refused")
277
+ return yield* unanswerable(admitted.refused);
278
+ const messageId = admitted.id;
279
+ return yield* Effect.gen(function* () {
280
+ const submission = yield* port(() => harness.submission(messageId, context));
281
+ if (submission === undefined) {
282
+ throw new Error(`submission ${messageId} is not in the storage it was admitted to`);
283
+ }
256
284
  // Counted once it is read back, whether or not that read succeeds: the wait must not be what reads the
257
285
  // storage first, or a failing read would fail it instead of answering this call.
258
286
  const record = yield* port(() => submission.status(context)).pipe(Effect.ensuring(Effect.sync(() => countUntilSettled(submission))));
259
287
  if (!isInput(record))
260
288
  throw new Error(`submission ${record.id} was admitted as a write`);
261
289
  return { message: yield* reads.message(record), replayed: false };
262
- }).pipe(
263
- // Once submission starts, only pi-durable's explicit refusal proves nothing was recorded.
264
- Effect.catchCause((cause) => {
265
- const error = portError(cause);
266
- return Effect.fail(messageId === undefined && error instanceof ConversationBusy
267
- ? agentError("busy", "The session is answering another message", { reason: "running" })
268
- : unconfirmed(conversation, messageId, error));
269
- }));
290
+ }).pipe(Effect.catchCause((cause) => Effect.fail(unconfirmed(conversation, messageId, portError(cause)))));
270
291
  }));
271
292
  /** The message as it is now. */
272
293
  const current = (message) => reads.submission(message.id).pipe(Effect.flatMap((record) => {
@@ -0,0 +1,12 @@
1
+ import { type AgentDefinition } from "./definition.ts";
2
+ export interface BuildOptions {
3
+ /** Type-check the project (default). Without it, `tsc --noCheck` only emits. */
4
+ check?: boolean;
5
+ }
6
+ export interface BuildResult {
7
+ dist: string;
8
+ definition: AgentDefinition;
9
+ /** What the build would publish that the author should look at: a literal credential in `mcpServers`, say. */
10
+ warnings: string[];
11
+ }
12
+ export declare function buildProject(directory: string, options?: BuildOptions): Promise<BuildResult>;