@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
@@ -7,35 +7,33 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  /**
10
- * A definition assembled onto pi-durable (architecture.md §3): its configuration and generated registry read, its
11
- * secrets checked as one list, its code built with them, and an Agent opened over pi-ai's built-in models with the
10
+ * A definition assembled onto pi-durable (architecture.md §3): a source project read as a definition, its secrets
11
+ * checked as one list, its code built with them, and an Agent opened over pi-ai's built-in models with the
12
12
  * text, tools and extensions installed. `dev`, `start` and an embedder call the same function.
13
13
  */
14
14
  import { existsSync } from "node:fs";
15
- import { writeFileAtomic } from "../../atomic-write.js";
16
15
  import { basename, join, resolve } from "node:path";
17
16
  import { pathToFileURL } from "node:url";
18
17
  import { createRegistry, defineExtension, } from "@earendil-works/pi-durable";
19
- import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node";
20
18
  import { CodingTools } from "@earendil-works/pi-durable/tools";
21
- import { loadEnvironment } from "../../content/file.js";
22
19
  import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
23
- import { declaredVariables } from "../../environment/declare.js";
24
- import { channelModuleProblem } from "../../channels/discover.js";
20
+ import { channelIngress, channelModuleProblem } from "../../channels/discover.js";
25
21
  import { AGENT_CONFIG_FILE as CONFIG_FILE } from "../../paths.js";
26
22
  import { readPackageJson } from "../../runtime.js";
27
- import { DEV_SERVE_ENV } from "../../serving-command.js";
23
+ import { fastagentVersion } from "../../version.js";
24
+ import { InstanceExecutionEnv } from "./execution-env.js";
25
+ import { instanceName } from "../../instance-name.js";
28
26
  import { modelRefOf } from "../../model-ref.js";
29
27
  import { MAX_SCHEDULES, scheduleConfigProblem } from "../../schedule/schedule.js";
30
28
  import { openDurableAgent } from "./agent.js";
31
- import { isNeeds } from "./define.js";
32
- import { GENERATED_DIR, REGISTRY_FILE, REGISTRY_KINDS, generateRegistry } from "./registry.js";
29
+ import { isByIngress, isNeeds } from "./define.js";
30
+ import { checkProgramConfig, devInstanceConfig } from "./config.js";
31
+ import { DEFINITION_FORMAT, chosenIngress, codeSecrets, defineDefinition, } from "./definition.js";
32
+ import { importRegistry } from "./registry.js";
33
33
  import { definitionSections, readCommands } from "./text.js";
34
+ import { programText } from "./text-sources.js";
34
35
  import { WAKE_EXTENSION, WAKE_TOOLS, wakeExtension } from "./wake.js";
35
- const SECRETS_MANIFEST_FILE = "secrets.json";
36
- const CONFIG_KEYS = ["name", "description", "model", "thinkingLevel", "extensions", "providers", "http", "deploy"];
37
36
  const DEFINITION_EXTENSION = "fastagent.definition";
38
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
39
37
  async function importDefault(path, what) {
40
38
  try {
41
39
  return (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href))).default;
@@ -44,134 +42,43 @@ async function importDefault(path, what) {
44
42
  throw new Error(`${what}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
45
43
  }
46
44
  }
47
- /** An object whose keys are among `keys`, or the refusal naming the one that is not and the ones there are. */
48
- function checkKeys(value, keys, where) {
49
- if (typeof value !== "object" || value === null || Array.isArray(value))
50
- throw new Error(`${where} must be an object`);
51
- for (const key of Object.keys(value)) {
52
- if (!keys.includes(key))
53
- throw new Error(`${where}: unknown key "${key}" (keys: ${keys.join(", ")})`);
54
- }
55
- }
56
- /** Refuse what a type check would have: Node strips types without checking them, so a typo reaches here. */
57
- function checkConfig(config, file) {
58
- if (config === undefined)
59
- return {};
60
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
61
- throw new Error(`${file} must default-export defineAgent({ ... })`);
62
- }
63
- checkKeys(config, CONFIG_KEYS, file);
64
- const { model, thinkingLevel, extensions, providers, http, deploy } = config;
65
- if (http !== undefined) {
66
- checkKeys(http, ["port", "api"], `${file}: "http"`);
67
- const port = http.port;
68
- if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
69
- throw new Error(`${file}: "http.port" must be an integer 0-65535`);
70
- }
71
- if (http.api !== undefined && typeof http.api !== "boolean") {
72
- throw new Error(`${file}: "http.api" must be true or false`);
73
- }
74
- }
75
- if (deploy !== undefined) {
76
- checkKeys(deploy, ["agentcore"], `${file}: "deploy"`);
77
- if (deploy.agentcore !== undefined) {
78
- checkKeys(deploy.agentcore, ["idleTimeoutSeconds"], `${file}: "deploy.agentcore"`);
79
- // AWS's own bounds for idleRuntimeSessionTimeout: a value outside them is refused by CloudFormation minutes
80
- // into `deploy agentcore --run`, after the image build.
81
- const idle = deploy.agentcore.idleTimeoutSeconds;
82
- if (idle !== undefined && (!Number.isInteger(idle) || idle < 60 || idle > 1209600)) {
83
- throw new Error(`${file}: "deploy.agentcore.idleTimeoutSeconds" must be an integer 60-1209600 (seconds)`);
84
- }
85
- }
86
- }
87
- if (model !== undefined)
88
- modelRefOf(model, `${file}: "model"`);
89
- if (thinkingLevel !== undefined && !THINKING_LEVELS.includes(thinkingLevel)) {
90
- throw new Error(`${file}: "thinkingLevel" must be one of ${THINKING_LEVELS.join(", ")}`);
91
- }
92
- for (const [key, list] of [
93
- ["extensions", extensions],
94
- ["providers", providers],
95
- ]) {
96
- if (list !== undefined && !Array.isArray(list))
97
- throw new Error(`${file}: "${key}" must be an array`);
98
- }
99
- return config;
100
- }
101
45
  /** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
102
- export async function loadConfig(dir) {
46
+ async function loadConfig(dir) {
103
47
  const configPath = join(dir, CONFIG_FILE);
104
- return checkConfig(existsSync(configPath) ? await importDefault(configPath, CONFIG_FILE) : undefined, CONFIG_FILE);
48
+ return checkProgramConfig(existsSync(configPath) ? await importDefault(configPath, CONFIG_FILE) : undefined, CONFIG_FILE);
105
49
  }
106
50
  /** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
107
51
  export function modelSpecFor(config, flag, env = process.env) {
108
52
  return flag ?? (env.FASTAGENT_MODEL || config.model);
109
53
  }
110
- /** Read the agent package in `dir`: `fastagent.config.ts` (optional) and `.fastagent/registry.ts`, generated first. */
111
- export async function loadDefinition(directory) {
54
+ /** The development project in `dir`, as `dev` opens it: its source and the `fastagent.json` at its root, if any. */
55
+ export async function loadDevProject(dir) {
56
+ const definition = await loadSourceDefinition(dir);
57
+ const instance = await devInstanceConfig(dir);
58
+ return instance === undefined ? { definition } : { definition, instance };
59
+ }
60
+ /** The source project in `dir` as a definition: `fastagent.config.ts` (optional) and the modules of `src/`. */
61
+ export async function loadSourceDefinition(directory) {
112
62
  // Absolute, since the prompt names the definition's files by it and the agent's tools resolve a relative path against
113
63
  // the agent's own working directory, which is this one.
114
64
  const dir = resolve(directory);
115
65
  const config = await loadConfig(dir);
116
- const registryPath = join(dir, GENERATED_DIR, REGISTRY_FILE);
117
- if (!existsSync(registryPath)) {
118
- throw new Error(`${GENERATED_DIR}/${REGISTRY_FILE} is missing: generate it from src/ before opening the agent`);
119
- }
120
- let registry;
121
- try {
122
- registry = (await import(__rewriteRelativeImportExtension(pathToFileURL(registryPath).href)));
123
- }
124
- catch (error) {
125
- // The registry imports every module, so a module that fails to load (or has no default export) fails here.
126
- throw new Error(`loading src/ through ${GENERATED_DIR}/${REGISTRY_FILE}: ${error.message}`, {
127
- cause: error,
128
- });
129
- }
130
- const packageName = (await readPackageJson(dir));
131
- const name = config.name ?? (typeof packageName.name === "string" ? packageName.name : basename(dir));
132
- return { dir, name, config, registry };
133
- }
134
- /** Every value the definition can hold `Needs` in, with where it is: modules by file, configuration by key. */
135
- function declarers(definition) {
136
- return [
137
- ...REGISTRY_KINDS.flatMap((kind) => definition.registry[kind].map(({ file, value }) => ({ source: file, value }))),
138
- ...(definition.config.extensions ?? []).map((value, i) => ({ source: `${CONFIG_FILE} extensions[${i}]`, value })),
139
- ...(definition.config.providers ?? []).map((value, i) => ({ source: `${CONFIG_FILE} providers[${i}]`, value })),
140
- ];
141
- }
142
- /** The secrets the definition declares, wherever: its code's `needs`, and `context.json`'s command variables. */
143
- export function definitionSecrets(definition) {
144
- return [
145
- ...declarers(definition).flatMap(({ source, value }) => isNeeds(value) ? value.secrets.map((secret) => ({ ...secret, source })) : []),
146
- ...declaredVariables(loadEnvironment(definition.dir)),
147
- ];
148
- }
149
- /**
150
- * The list a person deploying or running the agent reads instead of its code (`.fastagent/secrets.json`): each name
151
- * once, what it is for, whether the agent runs without it, and where it is declared.
152
- */
153
- export function secretsManifest(definition) {
154
- const byName = new Map();
155
- for (const secret of definitionSecrets(definition)) {
156
- const entry = byName.get(secret.name) ?? { name: secret.name, optional: true, declaredIn: [] };
157
- entry.optional &&= secret.optional === true;
158
- entry.declaredIn.push(secret.source);
159
- if (entry.description === undefined && secret.description !== undefined)
160
- entry.description = secret.description;
161
- byName.set(secret.name, entry);
162
- }
163
- return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
164
- }
165
- /** Generate the registry, read the definition, and write its secrets manifest beside the registry. */
166
- export async function prepareDefinition(dir) {
167
- await generateRegistry(dir);
168
- const definition = await loadDefinition(dir);
169
- writeFileAtomic(join(dir, GENERATED_DIR, SECRETS_MANIFEST_FILE), `${JSON.stringify(secretsManifest(definition), null, 2)}\n`, undefined, true);
170
- return definition;
66
+ const registry = await importRegistry(dir);
67
+ const pkg = (await readPackageJson(dir));
68
+ return defineDefinition({
69
+ format: DEFINITION_FORMAT,
70
+ builtWith: await fastagentVersion(),
71
+ name: config.name ?? (typeof pkg.name === "string" ? pkg.name : basename(dir)),
72
+ // A source project need not be published, so it need not have a version.
73
+ version: typeof pkg.version === "string" ? pkg.version : "0.0.0",
74
+ dir,
75
+ config,
76
+ registry,
77
+ });
171
78
  }
172
79
  /** What one value is once its secrets are supplied, or why it is not what its place holds. */
173
- function built(source, value, values, accept) {
174
- const resolved = isNeeds(value) ? value.build(values) : value;
80
+ function built(source, value, values, settings, accept) {
81
+ const resolved = isNeeds(value) ? value.build(values, settings) : value;
175
82
  const problem = accept(resolved);
176
83
  if (problem !== undefined)
177
84
  throw new Error(`${source}: ${problem}`);
@@ -194,12 +101,38 @@ const channelProblem = channelModuleProblem;
194
101
  const scheduleProblem = (value) => value?.kind !== "fastagent.schedule"
195
102
  ? "must default-export defineSchedule({ ... }), or needs(...) around one"
196
103
  : scheduleConfigProblem(value);
104
+ /**
105
+ * The channel `name` as `config` has it: the way it chooses of one that receives either way, which must build the
106
+ * module that way receives with; or the one module there is, which must receive the way `config` names, when it names
107
+ * one.
108
+ */
109
+ function chosenChannel(file, name, value, config, build) {
110
+ const shapeOf = (module) => channelIngress(module) === "webhook" ? "webhook" : "connection";
111
+ const by = (way) => (way === "webhook" ? "webhook" : "a connection it opens");
112
+ if (isByIngress(value)) {
113
+ const way = chosenIngress(config, name);
114
+ const module = build(value[way], `${file} (byIngress ${way})`);
115
+ if (shapeOf(module) !== way) {
116
+ throw new Error(`${file}: its ${way} way builds a channel that receives by ${by(shapeOf(module))}`);
117
+ }
118
+ return module;
119
+ }
120
+ const module = build(value, file);
121
+ const named = config.channels?.[name]?.ingress;
122
+ if (named !== undefined && shapeOf(module) !== named) {
123
+ throw new Error(`channels.${name}.ingress is "${named}", and ${file} receives only by ${by(shapeOf(module))}: default-export ` +
124
+ `byIngress({ webhook, connection }) from it to let an instance choose`);
125
+ }
126
+ return module;
127
+ }
197
128
  /**
198
129
  * Refuse to run while a required secret has no value, naming where it is declared; otherwise build every module with
199
- * the values it declared, and refuse one that is not what its directory holds.
130
+ * the values it declared, and refuse one that is not what its directory holds. A channel that receives either way is
131
+ * built the way `config` chooses, and only that way's secrets are asked for.
200
132
  */
201
- export function buildDefinition(definition, env = process.env) {
202
- const missing = missingSecrets(definitionSecrets(definition), env);
133
+ export function buildDefinition(definition, env, instance = { settings: {} }) {
134
+ const { settings } = instance;
135
+ const missing = missingSecrets(codeSecrets(definition, instance), env);
203
136
  if (missing.length > 0) {
204
137
  throw new Error(`missing required secrets: ${describeSecrets(missing)} (set them in the environment)`);
205
138
  }
@@ -207,27 +140,27 @@ export function buildDefinition(definition, env = process.env) {
207
140
  const { registry, config } = definition;
208
141
  const tools = registry.tools.map(({ file, value }) => ({
209
142
  source: file,
210
- ...named(built(file, value, values(value), toolProblem)),
143
+ ...named(built(file, value, values(value), settings, toolProblem)),
211
144
  }));
212
145
  const extensions = [
213
146
  ...registry.extensions.map(({ file, value }) => ({
214
147
  source: file,
215
- ...named(built(file, value, values(value), extensionProblem)),
148
+ ...named(built(file, value, values(value), settings, extensionProblem)),
216
149
  })),
217
150
  ...(config.extensions ?? []).map((value, i) => {
218
151
  const source = `${CONFIG_FILE} extensions[${i}]`;
219
- return { source, ...named(built(source, value, values(value), extensionProblem)) };
152
+ return { source, ...named(built(source, value, values(value), settings, extensionProblem)) };
220
153
  }),
221
154
  ];
222
155
  const channels = registry.channels.map(({ file, name, value }) => ({
223
156
  source: file,
224
157
  name,
225
- value: built(file, value, values(value), channelProblem),
158
+ value: chosenChannel(file, name, value, instance, (way, source) => built(source, way, values(way), settings, channelProblem)),
226
159
  }));
227
160
  const schedules = registry.schedules.map(({ file, name, value }) => ({
228
161
  source: file,
229
162
  name,
230
- value: built(file, value, values(value), scheduleProblem),
163
+ value: built(file, value, values(value), settings, scheduleProblem),
231
164
  }));
232
165
  refuseTwins("tool", tools);
233
166
  // pi-durable replaces an extension installed under a name already taken, its tools and sections with it.
@@ -245,7 +178,7 @@ export function buildDefinition(definition, env = process.env) {
245
178
  return {
246
179
  tools: tools.map(({ value }) => value),
247
180
  extensions: extensions.map(({ value }) => value),
248
- providers: (config.providers ?? []).map((value, i) => built(`${CONFIG_FILE} providers[${i}]`, value, values(value), providerProblem)),
181
+ providers: (config.providers ?? []).map((value, i) => built(`${CONFIG_FILE} providers[${i}]`, value, values(value), settings, providerProblem)),
249
182
  channels: new Map(channels.map(({ name, value }) => [name, value])),
250
183
  schedules: schedules.map(({ name, value: { kind: _kind, ...config } }) => ({ name, ...config })),
251
184
  };
@@ -255,12 +188,15 @@ export function buildDefinition(definition, env = process.env) {
255
188
  * providers): a declared secret `env` holds no value for has a stand-in, since the shape must not depend on whether
256
189
  * this machine holds the values. Whatever a build refuses, this refuses too.
257
190
  */
258
- export function buildDefinitionShape(definition, env) {
259
- const values = Object.fromEntries(definitionSecrets(definition).map(({ name }) => [name, "(no value here)"]));
191
+ export function buildDefinitionShape(definition, env, config = {
192
+ settings: definition.config.defaults?.settings ?? {},
193
+ ...(definition.config.defaults?.channels === undefined ? {} : { channels: definition.config.defaults.channels }),
194
+ }) {
195
+ const values = Object.fromEntries(definition.inputs.secrets.map(({ name }) => [name, "(no value here)"]));
260
196
  for (const [name, value] of Object.entries(env))
261
197
  if (value)
262
198
  values[name] = value;
263
- return buildDefinition(definition, values);
199
+ return buildDefinition(definition, values, config);
264
200
  }
265
201
  /**
266
202
  * The providers of the definition in `agentDir`, for a command that reads models without serving (`models`, `login`,
@@ -268,8 +204,8 @@ export function buildDefinitionShape(definition, env) {
268
204
  * it needs no secret the serve would. Without these a provider in code, or one replacing a built-in's id, would be
269
205
  * unknown there, or verified against the built-in's endpoint.
270
206
  */
271
- export async function definitionProviders(agentDir, env = process.env) {
272
- return buildDefinitionShape(await prepareDefinition(agentDir), env).providers;
207
+ export async function definitionProviders(agentDir, env) {
208
+ return buildDefinitionShape(await loadSourceDefinition(agentDir), env).providers;
273
209
  }
274
210
  const named = (value) => ({ name: value.name, value });
275
211
  /** Refuse two of a kind under one name: whichever is installed or kept last would silently stand in for the other. */
@@ -284,8 +220,9 @@ function refuseTwins(kind, entries) {
284
220
  }
285
221
  /** Open the definition as an Agent over `models`: the coding tools, then its own. */
286
222
  export async function assembleAgent(definition, options) {
287
- const { dir, config } = definition;
288
- const { code, models } = options;
223
+ const { dir } = definition;
224
+ const { code, models, config } = options;
225
+ const text = options.text ?? programText(dir);
289
226
  const spec = options.model ?? config.model;
290
227
  const model = spec === undefined ? undefined : modelRefOf(spec, "the default model");
291
228
  if (model !== undefined && models.getModel(model.provider, model.modelId) === undefined) {
@@ -293,13 +230,23 @@ export async function assembleAgent(definition, options) {
293
230
  throw new Error(`unknown model "${spec}": no provider "${model.provider}" offers "${model.modelId}"${stale}`);
294
231
  }
295
232
  const ownWake = [...code.tools, ...code.extensions.flatMap((extension) => extension.tools ?? [])].some(({ name }) => WAKE_TOOLS.includes(name));
233
+ const authoredNames = new Set([...code.tools, ...code.extensions.flatMap((extension) => extension.tools ?? [])].map((tool) => tool.name));
234
+ for (const tool of options.mcpTools ?? []) {
235
+ if (authoredNames.has(tool.name))
236
+ throw new Error(`MCP tool ${tool.name} conflicts with an authored tool`);
237
+ }
296
238
  const registry = createRegistry();
297
239
  registry.install(CodingTools);
298
240
  // After the coding tools, so a tool of the definition's own with one of their names replaces it.
299
241
  registry.install(defineExtension({
300
242
  name: DEFINITION_EXTENSION,
301
- sections: definitionSections(dir, options.content ?? [], { wake: ownWake || options.stateRoot !== undefined }),
302
- tools: code.tools,
243
+ sections: definitionSections(dir, options.content ?? [], {
244
+ wake: ownWake || options.stateRoot !== undefined,
245
+ runsAs: options.runsAs,
246
+ workdir: options.workdir ?? dir,
247
+ text,
248
+ }),
249
+ tools: [...code.tools, ...(options.mcpTools ?? [])],
303
250
  }));
304
251
  for (const extension of code.extensions)
305
252
  registry.install(extension);
@@ -311,14 +258,14 @@ export async function assembleAgent(definition, options) {
311
258
  storage: options.storage,
312
259
  models,
313
260
  registry,
314
- name: definition.name,
315
- ...(config.description === undefined ? {} : { description: config.description }),
261
+ name: options.name ?? instanceName(definition.name),
262
+ ...(definition.config.description === undefined ? {} : { description: definition.config.description }),
316
263
  ...(model === undefined ? {} : { model }),
317
264
  ...(thinkingLevel === undefined ? {} : { thinkingLevel }),
318
265
  ...(options.settings === undefined ? {} : { settings: options.settings }),
319
266
  ...(options.unfinished === undefined ? {} : { unfinished: options.unfinished }),
320
267
  // What the agent runs is not `dev`'s own serving, even when `dev` is what runs the agent.
321
- env: ({ cwd }) => new NodeExecutionEnv({ cwd: cwd ?? dir, shellEnv: { ...options.environment, [DEV_SERVE_ENV]: undefined } }),
322
- commands: () => readCommands(dir),
268
+ env: ({ cwd }) => new InstanceExecutionEnv({ cwd: cwd ?? options.workdir ?? dir, processEnv: options.processEnv }),
269
+ commands: () => readCommands(text),
323
270
  });
324
271
  }
@@ -24,7 +24,6 @@ export declare function durableReads(harness: Harness, ids: Ids, sessions: Pick<
24
24
  message: (record: InputRecord, known?: readonly EntryRecord[]) => Effect.Effect<Message, PortFailure>;
25
25
  submission: (messageId: string) => Effect.Effect<InputRecord | undefined, PortFailure, never>;
26
26
  submissionByKey: (conversation: Conversation, requestId: string) => Effect.Effect<InputRecord | undefined, PortFailure, never>;
27
- inbox: (conversation: Conversation) => Effect.Effect<Readonly<import("@earendil-works/pi-durable").InboxState> | undefined, PortFailure, never>;
28
27
  session: (conversation: Pick<Conversation, "id">) => Effect.Effect<Session, PortFailure>;
29
28
  sessionsOf: (conversations: readonly ConversationId[]) => Effect.Effect<Session[], PortFailure>;
30
29
  };
@@ -1,4 +1,4 @@
1
- import { AgentDoc, InboxDoc, LiveDoc, UsageDoc, } from "@earendil-works/pi-durable";
1
+ import { AgentDoc, LiveDoc, UsageDoc, } from "@earendil-works/pi-durable";
2
2
  import * as Effect from "effect/Effect";
3
3
  import * as Option from "effect/Option";
4
4
  import * as Stream from "effect/Stream";
@@ -63,7 +63,6 @@ export function durableReads(harness, ids, sessions, context) {
63
63
  return port(() => harness.submission(id, context)).pipe(Effect.flatMap((handle) => (handle === undefined ? Effect.undefined : port(() => handle.status(context)))), Effect.map((record) => (isInput(record) ? record : undefined)));
64
64
  };
65
65
  const submissionByKey = (conversation, requestId) => port(() => harness.commit((tx) => tx.submissionByRequest(conversation.id, requestId), context)).pipe(Effect.map((record) => (isInput(record) ? record : undefined)));
66
- const inbox = (conversation) => port(() => harness.snapshot(InboxDoc, conversation.id, context));
67
66
  /**
68
67
  * The sessions as they are now, in one commit: each one's status, reply and settings of one moment, and a page of
69
68
  * them one turn on pi-durable's commit line, which every running session writes through too.
@@ -87,7 +86,6 @@ export function durableReads(harness, ids, sessions, context) {
87
86
  message,
88
87
  submission,
89
88
  submissionByKey,
90
- inbox,
91
89
  session,
92
90
  sessionsOf,
93
91
  };
@@ -9,10 +9,8 @@ interface RegisteredModule {
9
9
  value: unknown;
10
10
  }
11
11
  export type Registry = Record<RegistryKind, readonly RegisteredModule[]>;
12
- export declare const GENERATED_DIR = ".fastagent";
13
- export declare const REGISTRY_FILE = "registry.ts";
14
- /** The registry's source: one default import per module, grouped by directory. */
15
- export declare function registrySource(dir: string): Promise<string>;
16
- /** Write `.fastagent/registry.ts` for the agent in `dir`, and return its path. */
17
- export declare function generateRegistry(dir: string): Promise<string>;
12
+ /** The module files of the source project in `dir`, project-relative, in the order the registry imports them. */
13
+ export declare function registryFiles(dir: string): Promise<string[]>;
14
+ /** Import every module of the source project in `dir`, in the order the files sort, so each opens the same way. */
15
+ export declare function importRegistry(dir: string): Promise<Registry>;
18
16
  export {};
@@ -1,15 +1,20 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
1
9
  /**
2
- * The generated registry (architecture.md §3): `.fastagent/registry.ts`, plain imports of every module in `src/tools/`,
3
- * `src/extensions/`, `src/channels/` and `src/schedules/`, so nothing is imported by hand, types flow and a bundler can
4
- * follow it. Each module default-exports one thing; a file directly in the directory is a module, and anything else a
5
- * module imports (a helper) lives outside these directories. Generated from the files, never edited.
10
+ * The registry (architecture.md §3): every module in `src/tools/`, `src/extensions/`, `src/channels/` and
11
+ * `src/schedules/` with what it default-exports, so nothing is imported by hand. A file directly in the directory is a
12
+ * module, and anything else a module imports (a helper) lives outside these directories.
6
13
  */
7
14
  import { readdir } from "node:fs/promises";
8
- import { writeFileAtomic } from "../../atomic-write.js";
9
15
  import { join } from "node:path";
16
+ import { pathToFileURL } from "node:url";
10
17
  export const REGISTRY_KINDS = ["tools", "extensions", "channels", "schedules"];
11
- export const GENERATED_DIR = ".fastagent";
12
- export const REGISTRY_FILE = "registry.ts";
13
18
  const MODULE = /^([^.][^/]*)\.(ts|mts|js|mjs)$/;
14
19
  const NOT_A_MODULE = /\.(d|test|spec)\.[a-z]+$/;
15
20
  /** The module files of one directory of `src/`, sorted so the registry is the same wherever it is generated. */
@@ -30,27 +35,25 @@ async function modulesIn(dir, kind) {
30
35
  : [{ file: `src/${kind}/${entry}`, name: match[1] }];
31
36
  });
32
37
  }
33
- /** The registry's source: one default import per module, grouped by directory. */
34
- export async function registrySource(dir) {
35
- const lines = ["// Generated by fastagent from src/: do not edit.", ""];
36
- const groups = [];
37
- let index = 0;
38
+ /** The module files of the source project in `dir`, project-relative, in the order the registry imports them. */
39
+ export async function registryFiles(dir) {
40
+ return (await Promise.all(REGISTRY_KINDS.map((kind) => modulesIn(dir, kind)))).flatMap((files) => files.map(({ file }) => file));
41
+ }
42
+ /** Import every module of the source project in `dir`, in the order the files sort, so each opens the same way. */
43
+ export async function importRegistry(dir) {
44
+ const registry = {};
38
45
  for (const kind of REGISTRY_KINDS) {
39
- const entries = [];
46
+ registry[kind] = [];
40
47
  for (const { file, name } of await modulesIn(dir, kind)) {
41
- const local = `m${index++}`;
42
- lines.push(`import ${local} from ${JSON.stringify(`../${file}`)};`);
43
- entries.push(` { file: ${JSON.stringify(file)}, name: ${JSON.stringify(name)}, value: ${local} },`);
48
+ let value;
49
+ try {
50
+ value = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(dir, file)).href))).default;
51
+ }
52
+ catch (error) {
53
+ throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
54
+ }
55
+ registry[kind].push({ file, name, value });
44
56
  }
45
- groups.push(`export const ${kind} = [\n${entries.join("\n")}${entries.length > 0 ? "\n" : ""}];`);
46
57
  }
47
- return `${lines.join("\n")}${index > 0 ? "\n" : ""}\n${groups.join("\n\n")}\n`;
48
- }
49
- /** Write `.fastagent/registry.ts` for the agent in `dir`, and return its path. */
50
- export async function generateRegistry(dir) {
51
- const path = join(dir, GENERATED_DIR, REGISTRY_FILE);
52
- // Every command that reads the definition writes it, `dev`'s worker importing it meanwhile: a reader must see a whole
53
- // file, and two writers write the same bytes.
54
- writeFileAtomic(path, await registrySource(dir), undefined, true);
55
- return path;
58
+ return registry;
56
59
  }
@@ -2,8 +2,8 @@
2
2
  * Temporary: pi-durable leaves a conversation's queued messages waiting after a run that did not answer (a model
3
3
  * failure, an abort, a message sent while an aborted run was still ending) until another submission places them, while
4
4
  * the Agent API takes them whatever the message before them ended with (SPEC.md §5.3). A write of FastAgent's own
5
- * kind places them. pi-durable 1.4.0's `settings.afterFailedRun` covers a model failure (`model_error`, `no_model`)
6
- * and not an abort: remove this once it does.
5
+ * kind places them. pi-durable 1.5.0's `settings.afterFailedRun` covers a failed run (a model failure, a crash) and
6
+ * not an abort: remove this once it does.
7
7
  */
8
8
  import type { Context } from "@earendil-works/chord/context";
9
9
  import { type Harness } from "@earendil-works/pi-durable";
@@ -8,15 +8,18 @@ import { queuedInputs } from "./messages.js";
8
8
  /** Release every conversation that waits now, and each one a run that did not answer leaves waiting, until the scope closes. */
9
9
  export const releaseWaitingQueues = (harness, context) => Effect.gen(function* () {
10
10
  const waiting = yield* Queue.unbounded();
11
- const unsubscribe = harness.subscribeCommits(({ changes }) => {
12
- for (const change of changes) {
13
- const record = change.type === "submission" ? change.value : undefined;
14
- if (record?.type === "input" && record.status === "unanswered") {
15
- Queue.offerUnsafe(waiting, record.conversationId);
11
+ const observation = harness.observeCommits({
12
+ advance: ({ changes }) => {
13
+ for (const change of changes) {
14
+ const record = change.type === "submission" ? change.value : undefined;
15
+ if (record?.type === "input" && record.status === "unanswered") {
16
+ Queue.offerUnsafe(waiting, record.conversationId);
17
+ }
16
18
  }
17
- }
19
+ },
20
+ end: () => { },
18
21
  });
19
- yield* Effect.addFinalizer(() => Effect.sync(unsubscribe));
22
+ yield* Effect.addFinalizer(() => Effect.sync(() => observation.detach()));
20
23
  yield* Queue.take(waiting).pipe(Effect.flatMap((conversationId) => release(harness, conversationId, context).pipe(Effect.catchCause((cause) => Effect.sync(() => log.error(`[fastagent] could not take the messages waiting in conversation ${conversationId}: ${String(portError(cause))}`))))), Effect.forever, Effect.forkScoped);
21
24
  const inspection = yield* port(() => harness.inspect(context));
22
25
  for (const record of inspection.submissions) {