@fastagent-sh/fastagent 0.29.0 → 0.30.1

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 (312) hide show
  1. package/README.md +9 -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 +2 -1
  15. package/dist/channels/feishu/feishu.d.ts +0 -3
  16. package/dist/channels/feishu/feishu.js +10 -10
  17. package/dist/channels/feishu/register-app.js +3 -5
  18. package/dist/channels/feishu/scaffold/channel.ts +32 -34
  19. package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
  20. package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
  21. package/dist/channels/feishu/setup-mode.d.ts +2 -12
  22. package/dist/channels/feishu/setup-mode.js +2 -22
  23. package/dist/channels/feishu/setup.d.ts +12 -0
  24. package/dist/channels/feishu/setup.js +197 -0
  25. package/dist/channels/kit/abortable.d.ts +8 -0
  26. package/dist/channels/kit/abortable.js +22 -0
  27. package/dist/channels/lark/lark.d.ts +0 -3
  28. package/dist/channels/lark/lark.js +0 -5
  29. package/dist/channels/lark/scaffold/channel.ts +33 -35
  30. package/dist/channels/lark/scaffold/lark-send.ts +5 -5
  31. package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
  32. package/dist/channels/lark/setup.d.ts +9 -0
  33. package/dist/channels/lark/setup.js +57 -0
  34. package/dist/channels/setup.d.ts +112 -0
  35. package/dist/channels/setup.js +31 -0
  36. package/dist/channels/slack/history.js +1 -1
  37. package/dist/channels/slack/manifest.d.ts +17 -7
  38. package/dist/channels/slack/manifest.js +12 -16
  39. package/dist/channels/slack/scaffold/channel.ts +44 -30
  40. package/dist/channels/slack/scaffold/slack-send.ts +8 -8
  41. package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
  42. package/dist/channels/slack/setup.d.ts +7 -0
  43. package/dist/channels/slack/setup.js +119 -0
  44. package/dist/channels/slack/slack-api.d.ts +2 -0
  45. package/dist/channels/slack/slack-api.js +4 -0
  46. package/dist/channels/slack/slack.d.ts +21 -2
  47. package/dist/channels/slack/slack.js +398 -338
  48. package/dist/channels/slack/socket.d.ts +57 -0
  49. package/dist/channels/slack/socket.js +305 -0
  50. package/dist/channels/telegram/polling.d.ts +27 -0
  51. package/dist/channels/telegram/polling.js +105 -0
  52. package/dist/channels/telegram/scaffold/channel.ts +52 -32
  53. package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
  54. package/dist/channels/telegram/setup.d.ts +2 -0
  55. package/dist/channels/telegram/setup.js +42 -0
  56. package/dist/channels/telegram/telegram-api.d.ts +27 -1
  57. package/dist/channels/telegram/telegram-api.js +7 -6
  58. package/dist/channels/telegram/telegram.d.ts +11 -3
  59. package/dist/channels/telegram/telegram.js +184 -157
  60. package/dist/channels/webhook-evidence.d.ts +11 -0
  61. package/dist/channels/webhook-evidence.js +71 -0
  62. package/dist/channels/webhooks.d.ts +37 -0
  63. package/dist/channels/webhooks.js +70 -0
  64. package/dist/child-env.d.ts +16 -0
  65. package/dist/child-env.js +30 -0
  66. package/dist/cli/box-login.js +4 -1
  67. package/dist/cli/channel-app.d.ts +3 -0
  68. package/dist/cli/channel-app.js +35 -0
  69. package/dist/cli/channel-setup.d.ts +38 -0
  70. package/dist/cli/channel-setup.js +195 -0
  71. package/dist/cli/commands/add.d.ts +6 -14
  72. package/dist/cli/commands/add.js +45 -303
  73. package/dist/cli/commands/auth.js +8 -2
  74. package/dist/cli/commands/build.d.ts +5 -0
  75. package/dist/cli/commands/build.js +16 -0
  76. package/dist/cli/commands/channels.d.ts +19 -0
  77. package/dist/cli/commands/channels.js +230 -0
  78. package/dist/cli/commands/content.d.ts +7 -3
  79. package/dist/cli/commands/content.js +16 -10
  80. package/dist/cli/commands/deploy/agentcore.js +55 -23
  81. package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
  82. package/dist/cli/commands/deploy/channel-apps.js +30 -0
  83. package/dist/cli/commands/deploy/docker.js +32 -65
  84. package/dist/cli/commands/deploy/fly.js +28 -12
  85. package/dist/cli/commands/deploy/railway.js +38 -13
  86. package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
  87. package/dist/cli/commands/deploy/secrets-step.js +66 -0
  88. package/dist/cli/commands/deploy/shared.d.ts +16 -5
  89. package/dist/cli/commands/deploy/shared.js +25 -8
  90. package/dist/cli/commands/deploy.js +53 -60
  91. package/dist/cli/commands/dev.d.ts +2 -0
  92. package/dist/cli/commands/dev.js +15 -3
  93. package/dist/cli/commands/env.js +1 -1
  94. package/dist/cli/commands/info.d.ts +2 -0
  95. package/dist/cli/commands/info.js +60 -25
  96. package/dist/cli/commands/init.js +3 -1
  97. package/dist/cli/commands/invoke.d.ts +2 -0
  98. package/dist/cli/commands/invoke.js +20 -7
  99. package/dist/cli/commands/login.d.ts +0 -7
  100. package/dist/cli/commands/login.js +22 -10
  101. package/dist/cli/commands/logout.js +1 -1
  102. package/dist/cli/commands/mcp.js +14 -6
  103. package/dist/cli/commands/models.js +3 -2
  104. package/dist/cli/commands/schedules.d.ts +4 -2
  105. package/dist/cli/commands/schedules.js +14 -10
  106. package/dist/cli/commands/secrets.d.ts +14 -0
  107. package/dist/cli/commands/secrets.js +213 -0
  108. package/dist/cli/commands/start.d.ts +14 -14
  109. package/dist/cli/commands/start.js +46 -60
  110. package/dist/cli/content-view.d.ts +4 -6
  111. package/dist/cli/content-view.js +5 -11
  112. package/dist/cli/first-run-channels.d.ts +3 -0
  113. package/dist/cli/first-run-channels.js +63 -0
  114. package/dist/cli/instance.d.ts +41 -0
  115. package/dist/cli/instance.js +155 -0
  116. package/dist/cli/invoke-stream.d.ts +5 -2
  117. package/dist/cli/invoke-stream.js +7 -2
  118. package/dist/cli/kernel.js +3 -0
  119. package/dist/cli/program.js +282 -110
  120. package/dist/cli/serve.d.ts +4 -5
  121. package/dist/cli/serve.js +4 -7
  122. package/dist/cli/shared.d.ts +11 -2
  123. package/dist/cli/shared.js +32 -19
  124. package/dist/cli/ticket.d.ts +7 -0
  125. package/dist/cli/ticket.js +106 -30
  126. package/dist/content/declare.d.ts +31 -37
  127. package/dist/content/declare.js +59 -60
  128. package/dist/content/file.d.ts +23 -12
  129. package/dist/content/file.js +48 -35
  130. package/dist/content/git.d.ts +0 -10
  131. package/dist/content/git.js +5 -39
  132. package/dist/content/resolve.d.ts +18 -22
  133. package/dist/content/resolve.js +32 -66
  134. package/dist/content/source.d.ts +7 -8
  135. package/dist/content/source.js +10 -28
  136. package/dist/credentials-page.d.ts +26 -0
  137. package/dist/credentials-page.js +185 -0
  138. package/dist/declared-secrets.d.ts +10 -3
  139. package/dist/declared-secrets.js +8 -4
  140. package/dist/deploy/agentcore/destroy.d.ts +4 -1
  141. package/dist/deploy/agentcore/destroy.js +25 -1
  142. package/dist/deploy/agentcore/forwarder.js +1 -1
  143. package/dist/deploy/agentcore/plan.d.ts +7 -21
  144. package/dist/deploy/agentcore/plan.js +47 -49
  145. package/dist/deploy/agentcore/run.d.ts +18 -10
  146. package/dist/deploy/agentcore/run.js +93 -70
  147. package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
  148. package/dist/deploy/agentcore/runtime-secrets.js +108 -0
  149. package/dist/deploy/agentcore/shell.js +12 -19
  150. package/dist/deploy/agentcore/sigv4.d.ts +7 -0
  151. package/dist/deploy/agentcore/sigv4.js +15 -0
  152. package/dist/deploy/agentcore/slr.d.ts +18 -0
  153. package/dist/deploy/agentcore/slr.js +27 -0
  154. package/dist/deploy/agentcore/store.d.ts +7 -0
  155. package/dist/deploy/agentcore/store.js +115 -0
  156. package/dist/deploy/api.d.ts +14 -14
  157. package/dist/deploy/api.js +26 -24
  158. package/dist/deploy/box-shell.d.ts +3 -1
  159. package/dist/deploy/build-context.d.ts +4 -4
  160. package/dist/deploy/build-context.js +11 -15
  161. package/dist/deploy/channel-ingress.d.ts +35 -9
  162. package/dist/deploy/channel-ingress.js +119 -22
  163. package/dist/deploy/container.d.ts +35 -10
  164. package/dist/deploy/container.js +79 -37
  165. package/dist/deploy/delivered-text.d.ts +13 -0
  166. package/dist/deploy/delivered-text.js +86 -0
  167. package/dist/deploy/docker/plan.d.ts +7 -8
  168. package/dist/deploy/docker/plan.js +31 -52
  169. package/dist/deploy/docker/run.d.ts +5 -43
  170. package/dist/deploy/docker/run.js +9 -84
  171. package/dist/deploy/file-store.d.ts +2 -0
  172. package/dist/deploy/file-store.js +17 -0
  173. package/dist/deploy/fly/plan.d.ts +3 -2
  174. package/dist/deploy/fly/plan.js +20 -9
  175. package/dist/deploy/fly/run.d.ts +18 -8
  176. package/dist/deploy/fly/run.js +24 -42
  177. package/dist/deploy/fly/store.d.ts +12 -0
  178. package/dist/deploy/fly/store.js +83 -0
  179. package/dist/deploy/hosts.d.ts +1 -2
  180. package/dist/deploy/hosts.js +1 -2
  181. package/dist/deploy/preflight.d.ts +18 -5
  182. package/dist/deploy/preflight.js +135 -63
  183. package/dist/deploy/railway/ignore.d.ts +8 -0
  184. package/dist/deploy/railway/ignore.js +57 -0
  185. package/dist/deploy/railway/plan.d.ts +1 -2
  186. package/dist/deploy/railway/plan.js +8 -6
  187. package/dist/deploy/railway/run.d.ts +21 -15
  188. package/dist/deploy/railway/run.js +32 -78
  189. package/dist/deploy/railway/store.d.ts +29 -0
  190. package/dist/deploy/railway/store.js +143 -0
  191. package/dist/deploy/runner.d.ts +5 -2
  192. package/dist/deploy/runner.js +4 -3
  193. package/dist/deploy/secret-store.d.ts +63 -0
  194. package/dist/deploy/secret-store.js +98 -0
  195. package/dist/deploy/secrets.d.ts +29 -46
  196. package/dist/deploy/secrets.js +49 -122
  197. package/dist/deploy/workspace.d.ts +6 -12
  198. package/dist/deploy/workspace.js +18 -94
  199. package/dist/dev-supervisor.js +6 -8
  200. package/dist/environment/declare.js +3 -2
  201. package/dist/environment/mise.d.ts +3 -1
  202. package/dist/environment/mise.js +4 -2
  203. package/dist/feishu.d.ts +1 -1
  204. package/dist/feishu.js +1 -1
  205. package/dist/harnesses/models/catalog.d.ts +1 -0
  206. package/dist/harnesses/models/catalog.js +1 -2
  207. package/dist/harnesses/models/environment.d.ts +7 -2
  208. package/dist/harnesses/models/environment.js +9 -5
  209. package/dist/harnesses/models/host.d.ts +0 -7
  210. package/dist/harnesses/models/host.js +10 -1
  211. package/dist/harnesses/models/login.d.ts +0 -8
  212. package/dist/harnesses/models/login.js +5 -4
  213. package/dist/harnesses/models/models.d.ts +7 -2
  214. package/dist/harnesses/models/models.js +28 -4
  215. package/dist/harnesses/pi-durable/agent.js +59 -38
  216. package/dist/harnesses/pi-durable/build.d.ts +12 -0
  217. package/dist/harnesses/pi-durable/build.js +270 -0
  218. package/dist/harnesses/pi-durable/built.d.ts +24 -0
  219. package/dist/harnesses/pi-durable/built.js +136 -0
  220. package/dist/harnesses/pi-durable/config.d.ts +81 -0
  221. package/dist/harnesses/pi-durable/config.js +266 -0
  222. package/dist/harnesses/pi-durable/define.d.ts +63 -13
  223. package/dist/harnesses/pi-durable/define.js +23 -2
  224. package/dist/harnesses/pi-durable/definition.d.ts +72 -0
  225. package/dist/harnesses/pi-durable/definition.js +143 -0
  226. package/dist/harnesses/pi-durable/events.d.ts +4 -3
  227. package/dist/harnesses/pi-durable/events.js +179 -136
  228. package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
  229. package/dist/harnesses/pi-durable/execution-env.js +11 -0
  230. package/dist/harnesses/pi-durable/mcp.js +1 -3
  231. package/dist/harnesses/pi-durable/open.d.ts +35 -37
  232. package/dist/harnesses/pi-durable/open.js +91 -153
  233. package/dist/harnesses/pi-durable/reads.d.ts +0 -1
  234. package/dist/harnesses/pi-durable/reads.js +1 -3
  235. package/dist/harnesses/pi-durable/registry.d.ts +4 -6
  236. package/dist/harnesses/pi-durable/registry.js +29 -26
  237. package/dist/harnesses/pi-durable/release.d.ts +2 -2
  238. package/dist/harnesses/pi-durable/release.js +10 -7
  239. package/dist/harnesses/pi-durable/serve.d.ts +91 -15
  240. package/dist/harnesses/pi-durable/serve.js +109 -47
  241. package/dist/harnesses/pi-durable/sessions.js +15 -12
  242. package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
  243. package/dist/harnesses/pi-durable/text-sources.js +55 -0
  244. package/dist/harnesses/pi-durable/text.d.ts +16 -4
  245. package/dist/harnesses/pi-durable/text.js +115 -59
  246. package/dist/harnesses/pi-durable/wake.js +8 -8
  247. package/dist/http/client.js +9 -3
  248. package/dist/http/contract.d.ts +3 -3
  249. package/dist/http/server.d.ts +1 -1
  250. package/dist/http/server.js +1 -1
  251. package/dist/index.d.ts +8 -5
  252. package/dist/index.js +4 -1
  253. package/dist/instance-name.d.ts +7 -0
  254. package/dist/instance-name.js +26 -0
  255. package/dist/lark.d.ts +1 -1
  256. package/dist/lark.js +1 -1
  257. package/dist/mcp/client.js +11 -2
  258. package/dist/mcp/config.d.ts +70 -18
  259. package/dist/mcp/config.js +136 -53
  260. package/dist/mcp/host.d.ts +5 -2
  261. package/dist/mcp/host.js +14 -6
  262. package/dist/mcp/sign-in.js +2 -2
  263. package/dist/mcp-api.d.ts +14 -0
  264. package/dist/paths.d.ts +22 -8
  265. package/dist/paths.js +44 -11
  266. package/dist/runtime-websocket.d.ts +14 -0
  267. package/dist/runtime-websocket.js +39 -0
  268. package/dist/scaffold/add-channel.d.ts +9 -16
  269. package/dist/scaffold/add-channel.js +13 -83
  270. package/dist/scaffold/templates/env.example +4 -5
  271. package/dist/scaffold/templates/fastagent.config.ts +20 -14
  272. package/dist/scaffold/templates/gitignore +9 -6
  273. package/dist/scaffold/templates.d.ts +6 -2
  274. package/dist/scaffold/templates.js +7 -2
  275. package/dist/scaffold/vendor-skill.js +4 -2
  276. package/dist/service.d.ts +44 -6
  277. package/dist/service.js +110 -10
  278. package/dist/serving-command.d.ts +10 -0
  279. package/dist/serving-command.js +1 -0
  280. package/dist/slack.d.ts +1 -1
  281. package/dist/slack.js +1 -1
  282. package/dist/strip-edges.d.ts +10 -0
  283. package/dist/strip-edges.js +22 -0
  284. package/dist/telegram.d.ts +1 -1
  285. package/dist/telegram.js +1 -1
  286. package/dist/tunnel.d.ts +0 -1
  287. package/dist/tunnel.js +2 -8
  288. package/dist/value-file.d.ts +11 -6
  289. package/dist/value-file.js +21 -9
  290. package/dist/version.d.ts +7 -1
  291. package/dist/version.js +9 -3
  292. package/package.json +6 -5
  293. package/dist/channels/lark/credentials-page.d.ts +0 -25
  294. package/dist/channels/lark/credentials-page.js +0 -206
  295. package/dist/channels/lark/onboard.d.ts +0 -52
  296. package/dist/channels/lark/onboard.js +0 -86
  297. package/dist/channels/slack/config-api.d.ts +0 -69
  298. package/dist/channels/slack/config-api.js +0 -162
  299. package/dist/channels/slack/onboard.d.ts +0 -40
  300. package/dist/channels/slack/onboard.js +0 -136
  301. package/dist/channels/slack/onboarding-state.d.ts +0 -31
  302. package/dist/channels/slack/onboarding-state.js +0 -64
  303. package/dist/channels/slack/register-webhook.d.ts +0 -12
  304. package/dist/channels/slack/register-webhook.js +0 -67
  305. package/dist/channels/slack/setup-server.d.ts +0 -32
  306. package/dist/channels/slack/setup-server.js +0 -246
  307. package/dist/cli/add-feishu.d.ts +0 -41
  308. package/dist/cli/add-feishu.js +0 -391
  309. package/dist/cli/add-slack.d.ts +0 -18
  310. package/dist/cli/add-slack.js +0 -216
  311. package/dist/harnesses/pi-durable/parked.d.ts +0 -18
  312. package/dist/harnesses/pi-durable/parked.js +0 -27
@@ -7,36 +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
- import { mcpSecrets } from "../../mcp/config.js";
23
19
  import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
24
- import { declaredVariables } from "../../environment/declare.js";
25
- import { channelModuleProblem } from "../../channels/discover.js";
20
+ import { channelIngress, channelModuleProblem } from "../../channels/discover.js";
26
21
  import { AGENT_CONFIG_FILE as CONFIG_FILE } from "../../paths.js";
27
22
  import { readPackageJson } from "../../runtime.js";
28
- 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";
29
26
  import { modelRefOf } from "../../model-ref.js";
30
27
  import { MAX_SCHEDULES, scheduleConfigProblem } from "../../schedule/schedule.js";
31
28
  import { openDurableAgent } from "./agent.js";
32
- import { isNeeds } from "./define.js";
33
- 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";
34
33
  import { definitionSections, readCommands } from "./text.js";
34
+ import { programText } from "./text-sources.js";
35
35
  import { WAKE_EXTENSION, WAKE_TOOLS, wakeExtension } from "./wake.js";
36
- const SECRETS_MANIFEST_FILE = "secrets.json";
37
- const CONFIG_KEYS = ["name", "description", "model", "thinkingLevel", "extensions", "providers", "http", "deploy"];
38
36
  const DEFINITION_EXTENSION = "fastagent.definition";
39
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
40
37
  async function importDefault(path, what) {
41
38
  try {
42
39
  return (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href))).default;
@@ -45,137 +42,43 @@ async function importDefault(path, what) {
45
42
  throw new Error(`${what}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
46
43
  }
47
44
  }
48
- /** An object whose keys are among `keys`, or the refusal naming the one that is not and the ones there are. */
49
- function checkKeys(value, keys, where) {
50
- if (typeof value !== "object" || value === null || Array.isArray(value))
51
- throw new Error(`${where} must be an object`);
52
- for (const key of Object.keys(value)) {
53
- if (!keys.includes(key))
54
- throw new Error(`${where}: unknown key "${key}" (keys: ${keys.join(", ")})`);
55
- }
56
- }
57
- /** Refuse what a type check would have: Node strips types without checking them, so a typo reaches here. */
58
- function checkConfig(config, file) {
59
- if (config === undefined)
60
- return {};
61
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
62
- throw new Error(`${file} must default-export defineAgent({ ... })`);
63
- }
64
- checkKeys(config, CONFIG_KEYS, file);
65
- const { model, thinkingLevel, extensions, providers, http, deploy } = config;
66
- if (http !== undefined) {
67
- checkKeys(http, ["port", "api"], `${file}: "http"`);
68
- const port = http.port;
69
- if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
70
- throw new Error(`${file}: "http.port" must be an integer 0-65535`);
71
- }
72
- if (http.api !== undefined && typeof http.api !== "boolean") {
73
- throw new Error(`${file}: "http.api" must be true or false`);
74
- }
75
- }
76
- if (deploy !== undefined) {
77
- checkKeys(deploy, ["agentcore"], `${file}: "deploy"`);
78
- if (deploy.agentcore !== undefined) {
79
- checkKeys(deploy.agentcore, ["idleTimeoutSeconds"], `${file}: "deploy.agentcore"`);
80
- // AWS's own bounds for idleRuntimeSessionTimeout: a value outside them is refused by CloudFormation minutes
81
- // into `deploy agentcore --run`, after the image build.
82
- const idle = deploy.agentcore.idleTimeoutSeconds;
83
- if (idle !== undefined && (!Number.isInteger(idle) || idle < 60 || idle > 1209600)) {
84
- throw new Error(`${file}: "deploy.agentcore.idleTimeoutSeconds" must be an integer 60-1209600 (seconds)`);
85
- }
86
- }
87
- }
88
- if (model !== undefined)
89
- modelRefOf(model, `${file}: "model"`);
90
- if (thinkingLevel !== undefined && !THINKING_LEVELS.includes(thinkingLevel)) {
91
- throw new Error(`${file}: "thinkingLevel" must be one of ${THINKING_LEVELS.join(", ")}`);
92
- }
93
- for (const [key, list] of [
94
- ["extensions", extensions],
95
- ["providers", providers],
96
- ]) {
97
- if (list !== undefined && !Array.isArray(list))
98
- throw new Error(`${file}: "${key}" must be an array`);
99
- }
100
- return config;
101
- }
102
45
  /** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
103
- export async function loadConfig(dir) {
46
+ async function loadConfig(dir) {
104
47
  const configPath = join(dir, CONFIG_FILE);
105
- 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);
106
49
  }
107
50
  /** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
108
51
  export function modelSpecFor(config, flag, env = process.env) {
109
52
  return flag ?? (env.FASTAGENT_MODEL || config.model);
110
53
  }
111
- /** Read the agent package in `dir`: `fastagent.config.ts` (optional) and `.fastagent/registry.ts`, generated first. */
112
- 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) {
113
62
  // Absolute, since the prompt names the definition's files by it and the agent's tools resolve a relative path against
114
63
  // the agent's own working directory, which is this one.
115
64
  const dir = resolve(directory);
116
65
  const config = await loadConfig(dir);
117
- const registryPath = join(dir, GENERATED_DIR, REGISTRY_FILE);
118
- if (!existsSync(registryPath)) {
119
- throw new Error(`${GENERATED_DIR}/${REGISTRY_FILE} is missing: generate it from src/ before opening the agent`);
120
- }
121
- let registry;
122
- try {
123
- registry = (await import(__rewriteRelativeImportExtension(pathToFileURL(registryPath).href)));
124
- }
125
- catch (error) {
126
- // The registry imports every module, so a module that fails to load (or has no default export) fails here.
127
- throw new Error(`loading src/ through ${GENERATED_DIR}/${REGISTRY_FILE}: ${error.message}`, {
128
- cause: error,
129
- });
130
- }
131
- const packageName = (await readPackageJson(dir));
132
- const name = config.name ?? (typeof packageName.name === "string" ? packageName.name : basename(dir));
133
- return { dir, name, config, registry };
134
- }
135
- /** Every value the definition can hold `Needs` in, with where it is: modules by file, configuration by key. */
136
- function declarers(definition) {
137
- return [
138
- ...REGISTRY_KINDS.flatMap((kind) => definition.registry[kind].map(({ file, value }) => ({ source: file, value }))),
139
- ...(definition.config.extensions ?? []).map((value, i) => ({ source: `${CONFIG_FILE} extensions[${i}]`, value })),
140
- ...(definition.config.providers ?? []).map((value, i) => ({ source: `${CONFIG_FILE} providers[${i}]`, value })),
141
- ];
142
- }
143
- /** The secrets the definition declares, wherever: its code's `needs`, and `context.json`'s command variables. */
144
- function codeSecrets(definition) {
145
- return [
146
- ...declarers(definition).flatMap(({ source, value }) => isNeeds(value) ? value.secrets.map((secret) => ({ ...secret, source })) : []),
147
- ...declaredVariables(loadEnvironment(definition.dir)),
148
- ];
149
- }
150
- export function definitionSecrets(definition) {
151
- return [...codeSecrets(definition), ...mcpSecrets(definition.dir)];
152
- }
153
- /**
154
- * The list a person deploying or running the agent reads instead of its code (`.fastagent/secrets.json`): each name
155
- * once, what it is for, whether the agent runs without it, and where it is declared.
156
- */
157
- export function secretsManifest(definition) {
158
- const byName = new Map();
159
- for (const secret of definitionSecrets(definition)) {
160
- const entry = byName.get(secret.name) ?? { name: secret.name, optional: true, declaredIn: [] };
161
- entry.optional &&= secret.optional === true;
162
- entry.declaredIn.push(secret.source);
163
- if (entry.description === undefined && secret.description !== undefined)
164
- entry.description = secret.description;
165
- byName.set(secret.name, entry);
166
- }
167
- return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
168
- }
169
- /** Generate the registry, read the definition, and write its secrets manifest beside the registry. */
170
- export async function prepareDefinition(dir) {
171
- await generateRegistry(dir);
172
- const definition = await loadDefinition(dir);
173
- writeFileAtomic(join(dir, GENERATED_DIR, SECRETS_MANIFEST_FILE), `${JSON.stringify(secretsManifest(definition), null, 2)}\n`, undefined, true);
174
- 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
+ });
175
78
  }
176
79
  /** What one value is once its secrets are supplied, or why it is not what its place holds. */
177
- function built(source, value, values, accept) {
178
- 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;
179
82
  const problem = accept(resolved);
180
83
  if (problem !== undefined)
181
84
  throw new Error(`${source}: ${problem}`);
@@ -198,12 +101,38 @@ const channelProblem = channelModuleProblem;
198
101
  const scheduleProblem = (value) => value?.kind !== "fastagent.schedule"
199
102
  ? "must default-export defineSchedule({ ... }), or needs(...) around one"
200
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
+ }
201
128
  /**
202
129
  * Refuse to run while a required secret has no value, naming where it is declared; otherwise build every module with
203
- * 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.
204
132
  */
205
- export function buildDefinition(definition, env = process.env) {
206
- const missing = missingSecrets(codeSecrets(definition), env);
133
+ export function buildDefinition(definition, env, instance = { settings: {} }) {
134
+ const { settings } = instance;
135
+ const missing = missingSecrets(codeSecrets(definition, instance), env);
207
136
  if (missing.length > 0) {
208
137
  throw new Error(`missing required secrets: ${describeSecrets(missing)} (set them in the environment)`);
209
138
  }
@@ -211,27 +140,27 @@ export function buildDefinition(definition, env = process.env) {
211
140
  const { registry, config } = definition;
212
141
  const tools = registry.tools.map(({ file, value }) => ({
213
142
  source: file,
214
- ...named(built(file, value, values(value), toolProblem)),
143
+ ...named(built(file, value, values(value), settings, toolProblem)),
215
144
  }));
216
145
  const extensions = [
217
146
  ...registry.extensions.map(({ file, value }) => ({
218
147
  source: file,
219
- ...named(built(file, value, values(value), extensionProblem)),
148
+ ...named(built(file, value, values(value), settings, extensionProblem)),
220
149
  })),
221
150
  ...(config.extensions ?? []).map((value, i) => {
222
151
  const source = `${CONFIG_FILE} extensions[${i}]`;
223
- return { source, ...named(built(source, value, values(value), extensionProblem)) };
152
+ return { source, ...named(built(source, value, values(value), settings, extensionProblem)) };
224
153
  }),
225
154
  ];
226
155
  const channels = registry.channels.map(({ file, name, value }) => ({
227
156
  source: file,
228
157
  name,
229
- value: built(file, value, values(value), channelProblem),
158
+ value: chosenChannel(file, name, value, instance, (way, source) => built(source, way, values(way), settings, channelProblem)),
230
159
  }));
231
160
  const schedules = registry.schedules.map(({ file, name, value }) => ({
232
161
  source: file,
233
162
  name,
234
- value: built(file, value, values(value), scheduleProblem),
163
+ value: built(file, value, values(value), settings, scheduleProblem),
235
164
  }));
236
165
  refuseTwins("tool", tools);
237
166
  // pi-durable replaces an extension installed under a name already taken, its tools and sections with it.
@@ -249,7 +178,7 @@ export function buildDefinition(definition, env = process.env) {
249
178
  return {
250
179
  tools: tools.map(({ value }) => value),
251
180
  extensions: extensions.map(({ value }) => value),
252
- 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)),
253
182
  channels: new Map(channels.map(({ name, value }) => [name, value])),
254
183
  schedules: schedules.map(({ name, value: { kind: _kind, ...config } }) => ({ name, ...config })),
255
184
  };
@@ -259,12 +188,15 @@ export function buildDefinition(definition, env = process.env) {
259
188
  * providers): a declared secret `env` holds no value for has a stand-in, since the shape must not depend on whether
260
189
  * this machine holds the values. Whatever a build refuses, this refuses too.
261
190
  */
262
- export function buildDefinitionShape(definition, env) {
263
- 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)"]));
264
196
  for (const [name, value] of Object.entries(env))
265
197
  if (value)
266
198
  values[name] = value;
267
- return buildDefinition(definition, values);
199
+ return buildDefinition(definition, values, config);
268
200
  }
269
201
  /**
270
202
  * The providers of the definition in `agentDir`, for a command that reads models without serving (`models`, `login`,
@@ -272,8 +204,8 @@ export function buildDefinitionShape(definition, env) {
272
204
  * it needs no secret the serve would. Without these a provider in code, or one replacing a built-in's id, would be
273
205
  * unknown there, or verified against the built-in's endpoint.
274
206
  */
275
- export async function definitionProviders(agentDir, env = process.env) {
276
- return buildDefinitionShape(await prepareDefinition(agentDir), env).providers;
207
+ export async function definitionProviders(agentDir, env) {
208
+ return buildDefinitionShape(await loadSourceDefinition(agentDir), env).providers;
277
209
  }
278
210
  const named = (value) => ({ name: value.name, value });
279
211
  /** Refuse two of a kind under one name: whichever is installed or kept last would silently stand in for the other. */
@@ -288,8 +220,9 @@ function refuseTwins(kind, entries) {
288
220
  }
289
221
  /** Open the definition as an Agent over `models`: the coding tools, then its own. */
290
222
  export async function assembleAgent(definition, options) {
291
- const { dir, config } = definition;
292
- const { code, models } = options;
223
+ const { dir } = definition;
224
+ const { code, models, config } = options;
225
+ const text = options.text ?? programText(dir);
293
226
  const spec = options.model ?? config.model;
294
227
  const model = spec === undefined ? undefined : modelRefOf(spec, "the default model");
295
228
  if (model !== undefined && models.getModel(model.provider, model.modelId) === undefined) {
@@ -307,7 +240,12 @@ export async function assembleAgent(definition, options) {
307
240
  // After the coding tools, so a tool of the definition's own with one of their names replaces it.
308
241
  registry.install(defineExtension({
309
242
  name: DEFINITION_EXTENSION,
310
- sections: definitionSections(dir, options.content ?? [], { wake: ownWake || options.stateRoot !== undefined }),
243
+ sections: definitionSections(dir, options.content ?? [], {
244
+ wake: ownWake || options.stateRoot !== undefined,
245
+ runsAs: options.runsAs,
246
+ workdir: options.workdir ?? dir,
247
+ text,
248
+ }),
311
249
  tools: [...code.tools, ...(options.mcpTools ?? [])],
312
250
  }));
313
251
  for (const extension of code.extensions)
@@ -320,14 +258,14 @@ export async function assembleAgent(definition, options) {
320
258
  storage: options.storage,
321
259
  models,
322
260
  registry,
323
- name: definition.name,
324
- ...(config.description === undefined ? {} : { description: config.description }),
261
+ name: options.name ?? instanceName(definition.name),
262
+ ...(definition.config.description === undefined ? {} : { description: definition.config.description }),
325
263
  ...(model === undefined ? {} : { model }),
326
264
  ...(thinkingLevel === undefined ? {} : { thinkingLevel }),
327
265
  ...(options.settings === undefined ? {} : { settings: options.settings }),
328
266
  ...(options.unfinished === undefined ? {} : { unfinished: options.unfinished }),
329
267
  // What the agent runs is not `dev`'s own serving, even when `dev` is what runs the agent.
330
- env: ({ cwd }) => new NodeExecutionEnv({ cwd: cwd ?? dir, shellEnv: { ...options.environment, [DEV_SERVE_ENV]: undefined } }),
331
- commands: () => readCommands(dir),
268
+ env: ({ cwd }) => new InstanceExecutionEnv({ cwd: cwd ?? options.workdir ?? dir, processEnv: options.processEnv }),
269
+ commands: () => readCommands(text),
332
270
  });
333
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) {