@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
@@ -1,7 +1,8 @@
1
1
  /**
2
- * The definition's text (architecture.md §3), read for each request so an edit takes effect on the next answer:
3
- * `SYSTEM.md` and `AGENTS.md` as prompt sections, `skills/` as Agent Skills (agentskills.io) and `prompts/` as prompt
4
- * templates. A skill is loaded as pi does it: the prompt lists each one's name, description and location, the model
2
+ * The definition's text (architecture.md §3) and the instance's (agent-artifact.md §4.2), read for each request so an
3
+ * edit takes effect on the next answer: `SYSTEM.md` and `AGENTS.md` as prompt sections, `skills/` as Agent Skills
4
+ * (agentskills.io) and `prompts/` as prompt templates, each with the files and directories the instance adds
5
+ * (text-sources.ts). A skill is loaded as pi does it: the prompt lists each one's name, description and location, the model
5
6
  * reads its `SKILL.md` when a task matches, and `/skill:<name>` loads it into the message.
6
7
  */
7
8
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
@@ -9,7 +10,7 @@ import { basename, join } from "node:path";
9
10
  import { section } from "@earendil-works/pi-durable";
10
11
  import { parse as parseYaml } from "yaml";
11
12
  import { log } from "../../log.js";
12
- import { isAgentcoreRuntime, isDeployedWorkspace } from "../../paths.js";
13
+ import { NOUN, declareText, programText, whatIs } from "./text-sources.js";
13
14
  const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
14
15
  const SKILL_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
15
16
  /** A file's YAML frontmatter and its body. A file without frontmatter is all body. */
@@ -49,14 +50,37 @@ function directories(dir) {
49
50
  }
50
51
  });
51
52
  }
52
- /** The agent's skills: `skills/<name>/SKILL.md`. One that breaks the specification is reported and skipped. */
53
- export function readSkills(agentDir) {
54
- const root = join(agentDir, "skills");
53
+ /**
54
+ * Whether a source is there to read as `kind`: one an instance declared that has gone, or become what its kind is not,
55
+ * is reported, not read as empty.
56
+ */
57
+ function present({ path, declaredBy }, kind) {
58
+ const found = whatIs(path);
59
+ if (found === NOUN[kind] || (found !== undefined && declaredBy === undefined))
60
+ return true;
61
+ if (declaredBy !== undefined) {
62
+ report(`${path}: ${declaredBy} declares it, and ${found === undefined ? "it is gone" : `it is a ${found} now`}`);
63
+ }
64
+ return false;
65
+ }
66
+ const collisionOf = (kind, name, taken, file, byInstance) => `${kind} "${name}" is declared by ${taken} and ${file}` +
67
+ (byInstance ? `: an instance adds to the program's ${kind}s and does not replace them — name it otherwise` : "");
68
+ /** The skills in `sources`: `<dir>/<name>/SKILL.md`. One that breaks the specification is reported and skipped. */
69
+ function loadSkills(sources, collide) {
55
70
  const skills = [];
71
+ for (const source of sources) {
72
+ if (present(source, "skills"))
73
+ readSkillsOf(source, skills, collide);
74
+ }
75
+ return skills;
76
+ }
77
+ /** The skills of one directory, added to `skills`. */
78
+ function readSkillsOf(source, skills, collide) {
79
+ const root = source.path;
56
80
  for (const entry of directories(root)) {
57
81
  const baseDir = join(root, entry);
58
82
  const filePath = join(baseDir, "SKILL.md");
59
- const label = `skills/${entry}/SKILL.md`;
83
+ const label = filePath;
60
84
  if (!existsSync(filePath))
61
85
  continue;
62
86
  let fields;
@@ -77,8 +101,10 @@ export function readSkills(agentDir) {
77
101
  report(`${label}: "description" must be a text of at most 1024 characters`);
78
102
  continue;
79
103
  }
80
- if (skills.some((skill) => skill.name === name)) {
81
- report(`${label}: another skill is named "${name}"`);
104
+ const taken = skills.find((skill) => skill.name === name);
105
+ if (taken !== undefined) {
106
+ const byInstance = source.declaredBy !== undefined;
107
+ collide(collisionOf("skill", name, taken.filePath, filePath, byInstance), byInstance);
82
108
  continue;
83
109
  }
84
110
  skills.push({
@@ -89,29 +115,53 @@ export function readSkills(agentDir) {
89
115
  disableModelInvocation: fields["disable-model-invocation"] === true,
90
116
  });
91
117
  }
92
- return skills;
93
118
  }
94
- /** The agent's prompt templates: `prompts/<name>.md`, a `description` in the frontmatter and the body as the text. */
95
- function readTemplates(agentDir) {
96
- const root = join(agentDir, "prompts");
97
- if (!existsSync(root))
98
- return [];
99
- return readdirSync(root)
100
- .sort()
101
- .flatMap((entry) => {
102
- if (!entry.endsWith(".md"))
103
- return [];
104
- const label = `prompts/${entry}`;
105
- try {
106
- const { fields, body } = frontmatter(readFileSync(join(root, entry), "utf8"), label);
107
- const description = typeof fields.description === "string" ? fields.description : undefined;
108
- return [{ name: entry.slice(0, -3), ...(description === undefined ? {} : { description }), body }];
109
- }
110
- catch (error) {
111
- report(`${label}: ${error instanceof Error ? error.message : String(error)}`);
112
- return [];
119
+ /** The skills the agent offers, reported and skipped as above. */
120
+ export const readSkills = (sources) => loadSkills(sources, report);
121
+ /** The prompt templates in `sources`: `<dir>/<name>.md`, a `description` in the frontmatter and the body as the text. */
122
+ function loadTemplates(sources, collide) {
123
+ const templates = [];
124
+ const files = new Map();
125
+ for (const source of sources) {
126
+ if (!present(source, "prompts"))
127
+ continue;
128
+ for (const entry of readdirSync(source.path).sort()) {
129
+ if (!entry.endsWith(".md"))
130
+ continue;
131
+ const file = join(source.path, entry);
132
+ const name = entry.slice(0, -3);
133
+ const taken = files.get(name);
134
+ if (taken !== undefined) {
135
+ const byInstance = source.declaredBy !== undefined;
136
+ collide(collisionOf("prompt template", name, taken, file, byInstance), byInstance);
137
+ continue;
138
+ }
139
+ try {
140
+ const { fields, body } = frontmatter(readFileSync(file, "utf8"), file);
141
+ const description = typeof fields.description === "string" ? fields.description : undefined;
142
+ templates.push({ name, ...(description === undefined ? {} : { description }), body });
143
+ files.set(name, file);
144
+ }
145
+ catch (error) {
146
+ report(`${file}: ${error instanceof Error ? error.message : String(error)}`);
147
+ }
113
148
  }
114
- });
149
+ }
150
+ return templates;
151
+ }
152
+ /**
153
+ * The instance's text as it will be read: declared, and refused when an instance entry takes a name the program (or an
154
+ * earlier instance entry) has. Called at open; what goes wrong afterwards is reported as each message reads it.
155
+ */
156
+ export function openText(dir, instance) {
157
+ const text = declareText(dir, instance);
158
+ const refuse = (problem, byInstance) => {
159
+ if (byInstance)
160
+ throw new Error(`${instance?.label}: ${problem}`);
161
+ };
162
+ loadSkills(text.skills, refuse);
163
+ loadTemplates(text.prompts, refuse);
164
+ return text;
115
165
  }
116
166
  /** Arguments as a shell splits them: by whitespace, with `"…"` or `'…'` keeping one argument together. */
117
167
  export function parseArguments(text) {
@@ -170,14 +220,14 @@ function loadedSkill(skill, args) {
170
220
  return args.trim() === "" ? block : `${block}\n\n${args.trim()}`;
171
221
  }
172
222
  /** What a Caller may offer as `/<name>`: the prompt templates, then `skill:<name>` for each skill. */
173
- export function readCommands(agentDir) {
223
+ export function readCommands(text) {
174
224
  return [
175
- ...readTemplates(agentDir).map((template) => ({
225
+ ...loadTemplates(text.prompts, report).map((template) => ({
176
226
  name: template.name,
177
227
  ...(template.description === undefined ? {} : { description: template.description }),
178
228
  expand: (args) => expandTemplate(template.body, parseArguments(args)),
179
229
  })),
180
- ...readSkills(agentDir).map((skill) => ({
230
+ ...readSkills(text.skills).map((skill) => ({
181
231
  name: `skill:${skill.name}`,
182
232
  description: skill.description,
183
233
  expand: (args) => loadedSkill(skill, args),
@@ -217,39 +267,44 @@ const fileText = (path) => {
217
267
  const text = readFileSync(path, "utf8").trim();
218
268
  return text === "" ? undefined : text;
219
269
  };
270
+ /** The instance's files of instructions, in the order it declared them. */
271
+ function instructionsText(sources) {
272
+ const texts = sources.filter((source) => present(source, "instructions")).flatMap(({ path }) => fileText(path) ?? []);
273
+ return texts.length === 0 ? undefined : texts.join("\n\n");
274
+ }
220
275
  /**
221
276
  * The prompt the definition contributes: `SYSTEM.md` as written, `AGENTS.md` as project context, the skills, where it
222
- * works, and, deployed, what of a change it makes to itself lasts. `wake`: the agent has the wake tool.
277
+ * works, and, deployed, what of a change it makes to itself lasts. `wake`: the agent has the wake tool; `runsAs` says
278
+ * whether it is deployed, and where.
223
279
  */
224
280
  export function definitionSections(agentDir, content, options) {
281
+ const text = options.text ?? programText(agentDir);
225
282
  return [
226
283
  section("system", () => fileText(join(agentDir, "SYSTEM.md")), { tag: false }),
227
284
  section("project_context", () => fileText(join(agentDir, "AGENTS.md"))),
228
- section("skills", () => skillsPrompt(readSkills(agentDir))),
229
- section("content", () => contentSection({ agentDir, content })),
230
- section("self_change", () => (isDeployedWorkspace() ? selfChangeSection(options.wake) : undefined)),
285
+ section("instance_context", () => instructionsText(text.instructions)),
286
+ section("skills", () => skillsPrompt(readSkills(text.skills))),
287
+ section("content", () => contentSection({ agentDir, workdir: options.workdir ?? agentDir, content })),
288
+ section("self_change", () => options.runsAs.deployed ? selfChangeSection(options.wake, options.runsAs.agentcore) : undefined),
231
289
  ];
232
290
  }
233
291
  /**
234
292
  * What takes effect when, and the agent's own path to a new capability, are the same on every host; how long the
235
293
  * storage lives is the host's answer: AgentCore's managed mount is reset by every deploy, so telling that agent to keep
236
- * work "outside the definition" would name a location its next deploy erases. The skill it writes lives in the
237
- * definition directory, which the next deployment replaces (or, on AgentCore, erases with everything else), so the
238
- * sentence says so, and where a skill that should outlast it has to go.
294
+ * work in its directory would name a location its next deploy erases; elsewhere the directory is the host's storage,
295
+ * which every deployment keeps. Its definition is the image's either way, so the sentence says where a capability that
296
+ * should outlast a deployment has to go.
239
297
  */
240
- function selfChangeSection(wake) {
241
- const deployment = isAgentcoreRuntime()
242
- ? "Every deployment of a new version resets this host's storage entirely"
243
- : "Each deployment replaces your directory with the author's release";
244
- return (`Your directory survives restarts, including uncommitted work; /tmp does not. ${deployment}, so anything that ` +
245
- `must outlive a deployment belongs in an external system (a git remote, an issue tracker, a database). ` +
246
- `SYSTEM.md, AGENTS.md, skills/ and prompts/ are read for every message; changes to code in src/ (tools, ` +
247
- `extensions, channels, schedules) or to configuration (fastagent.config.ts, context.json, package.json) take ` +
248
- `effect when the service restarts. A tool you install yourself (apt-get, npm install -g) belongs to this machine ` +
249
- `and is gone when its container is replaced. To give yourself a new capability now, write a skill — a SKILL.md in ` +
250
- `your definition's skills/ directory, with any script it needs run through bash. It lasts until the next ` +
251
- `deployment replaces that directory, so a capability that should outlast it belongs in the author's release: ` +
252
- `propose it to them.` +
298
+ function selfChangeSection(wake, agentcore) {
299
+ const storage = agentcore
300
+ ? "Every deployment of a new version resets this host's storage entirely, your directory with it, so anything " +
301
+ "that must outlive a deployment belongs in an external system (a git remote, an issue tracker, a database)."
302
+ : "Your directory is this host's storage and survives deployments of a new version.";
303
+ return (`Your directory survives restarts, including uncommitted work; /tmp does not. ${storage} ` +
304
+ `Your definition (SYSTEM.md, AGENTS.md, skills/, prompts/, src/ and the configuration) is the author's release, ` +
305
+ `read as it was built from where it is installed: a copy of it you make in your directory is not read. A tool you install ` +
306
+ `yourself (apt-get, npm install -g) belongs to this machine and is gone when its container is replaced. A ` +
307
+ `capability that should outlast a deployment belongs in the author's release: propose it to them.` +
253
308
  // Named only when it has the tool: naming a tool the model does not have invites calls to it.
254
309
  (wake ? " To schedule your own follow-up work, use the wake tool." : ""));
255
310
  }
@@ -257,8 +312,6 @@ function selfChangeSection(wake) {
257
312
  function contentKind(c) {
258
313
  if (c.kind !== "github")
259
314
  return "a directory on this machine";
260
- if (!c.clone)
261
- return `a checkout of github ${c.repo} on this machine`;
262
315
  // Brought up to date in place at each start where git can do so without touching the agent's work; what the agent
263
316
  // changes stays, and reaches anyone else only when pushed. The declared ref is what it follows, not a claim about
264
317
  // where it is: a clone the agent moved elsewhere is left there.
@@ -270,9 +323,12 @@ function contentKind(c) {
270
323
  * with its location and where a change there goes. A result written where it does not last is how knowledge gets
271
324
  * lost, so the agent is told which is which.
272
325
  */
273
- function contentSection({ agentDir, content }) {
274
- const own = `Your working directory, ${agentDir}, is your own directory: it holds your definition, and files you create ` +
275
- `land there unless you put them elsewhere.`;
326
+ function contentSection({ agentDir, workdir, content, }) {
327
+ const own = workdir === agentDir
328
+ ? `Your working directory, ${agentDir}, is your own directory: it holds your definition, and files you create ` +
329
+ `land there unless you put them elsewhere.`
330
+ : `Your working directory, ${workdir}, is your own directory: files you create land there unless you put them ` +
331
+ `elsewhere. Your definition is in ${agentDir}, which you read and do not change.`;
276
332
  if (content.length === 0)
277
333
  return `${own} Nothing else is declared for you: you work only in your own directory.`;
278
334
  const line = (c) => `- ${c.name}: ${c.location} (${contentKind(c)})${c.description !== undefined ? ` ${c.description}` : ""}`;
@@ -3,7 +3,7 @@
3
3
  * the instance's state (schedule/wakeups.ts), which the scheduler fires. A pair over one store: a definition that
4
4
  * defines either name gets neither.
5
5
  */
6
- import { Type } from "@earendil-works/pi-ai";
6
+ import { num, obj, opt, str, union } from "@earendil-works/pi-schema";
7
7
  import { defineExtension, defineTool } from "@earendil-works/pi-durable";
8
8
  import { addWakeup, parseDelayMs, removeWakeup } from "../../schedule/wakeups.js";
9
9
  import { sessionIdOf } from "./ids.js";
@@ -32,13 +32,13 @@ export function wakeExtension(stateRoot, options = {}) {
32
32
  "same conversation with `prompt` as its instruction, tagged with the wake-up's id so you can tell it from a " +
33
33
  "user's, and you keep the full context of this conversation. Its plain reply is delivered to no one: to " +
34
34
  "reach a person it must call a delivery tool (a channel's send tool), as a scheduled job does.",
35
- parameters: Type.Object({
36
- in: Type.Optional(Type.Union([Type.String(), Type.Number()], {
35
+ parameters: obj({
36
+ in: opt(union([str(), num()], {
37
37
  description: 'one-shot delay: a duration string with a unit ("30m" / "2h" / "1d"), or a number of seconds',
38
38
  })),
39
- cron: Type.Optional(Type.String({ description: 'recurring: a 5-field cron expression (e.g. "0 9 * * *")' })),
40
- tz: Type.Optional(Type.String({ description: 'IANA timezone for `cron` (default "UTC")' })),
41
- prompt: Type.String({
39
+ cron: opt(str({ description: 'recurring: a 5-field cron expression (e.g. "0 9 * * *")' })),
40
+ tz: opt(str({ description: 'IANA timezone for `cron` (default "UTC")' })),
41
+ prompt: str({
42
42
  minLength: 1,
43
43
  description: "the instruction for the woken message (it arrives in this same conversation)",
44
44
  }),
@@ -73,8 +73,8 @@ export function wakeExtension(stateRoot, options = {}) {
73
73
  name: "unwake",
74
74
  description: "Cancel one of YOUR pending wake-ups (one-shot or recurring) by the id `wake` returned. Use it when a " +
75
75
  "scheduled follow-up is no longer needed, especially to stop a recurring wake once its job is done.",
76
- parameters: Type.Object({
77
- id: Type.String({ minLength: 1, description: "the wake-up id (returned by `wake`)" }),
76
+ parameters: obj({
77
+ id: str({ minLength: 1, description: "the wake-up id (returned by `wake`)" }),
78
78
  }),
79
79
  execute: async (args, api, context) => {
80
80
  const session = await sessionIdOf(api, context);
@@ -48,9 +48,15 @@ const reasonOf = (error) => error.reason;
48
48
  const unsent = (error) => ["EncodeError", "InvalidUrlError"].includes(reasonOf(error)?._tag ?? "");
49
49
  const said = (error) => (error instanceof Error ? error.message : String(error));
50
50
  /** What stopped a request that left, said for a Caller. */
51
- const whatFailed = (baseUrl, error) => reasonOf(error)?._tag === "TransportError"
52
- ? `could not reach the agent at ${baseUrl}: ${said(error)}`
53
- : `the agent at ${baseUrl} answered what this client cannot read: ${said(error)}`;
51
+ const whatFailed = (baseUrl, error) => {
52
+ if (reasonOf(error)?._tag === "TransportError")
53
+ return `could not reach the agent at ${baseUrl}: ${said(error)}`;
54
+ const answered = `the agent at ${baseUrl} answered what this client cannot read: ${said(error)}`;
55
+ // A serve mounts the API only while it holds a key (SPEC §9.6): a host that is up and has none answers 404 here.
56
+ return reasonOf(error)?.response?.status === 404
57
+ ? `${answered} (a deployment serves no Agent API without a FASTAGENT_API_KEY, and AgentCore serves none)`
58
+ : answered;
59
+ };
54
60
  /**
55
61
  * A read's failure, and a write's other than a send's: a refusal as the agent answered it; one that could not get
56
62
  * through or met a server's error may be sent again, since repeating any request but a send does no harm (§7);
@@ -464,6 +464,9 @@ declare const AgentHttpApi_base: HttpApi.HttpApi<"fastagent", HttpApiGroup.HttpA
464
464
  readonly event: Schema.String;
465
465
  readonly data: Schema.String;
466
466
  }>, never, never>, Schema.Never, {
467
+ readonly type: "message";
468
+ readonly message: Message;
469
+ } | {
467
470
  readonly type: "entry";
468
471
  readonly entry: Entry;
469
472
  } | {
@@ -499,9 +502,6 @@ declare const AgentHttpApi_base: HttpApi.HttpApi<"fastagent", HttpApiGroup.HttpA
499
502
  readonly retryable: boolean;
500
503
  readonly details?: Schema.Json | undefined;
501
504
  };
502
- } | {
503
- readonly type: "message";
504
- readonly message: Message;
505
505
  } | {
506
506
  readonly type: string;
507
507
  }> | Schema.toCodecJson<typeof Message>, Schema.toCodecJson<Schema.decodeTo<Schema.declare<AgentError, AgentError>, Schema.Struct<{
@@ -11,7 +11,7 @@ export declare function apiTurnedOff(config: {
11
11
  api?: boolean;
12
12
  };
13
13
  }): boolean;
14
- export declare const API_TURNED_OFF = "fastagent.config.ts says http.api: false";
14
+ export declare const API_TURNED_OFF = "the configuration says http.api: false";
15
15
  export declare function apiKeysFrom(value: string | undefined): string[];
16
16
  export interface ApiHandler {
17
17
  fetch: ChannelHandler;
@@ -26,7 +26,7 @@ export const API_KEY_VARIABLE = "FASTAGENT_API_KEY";
26
26
  export function apiTurnedOff(config) {
27
27
  return config.http?.api === false;
28
28
  }
29
- export const API_TURNED_OFF = "fastagent.config.ts says http.api: false";
29
+ export const API_TURNED_OFF = "the configuration says http.api: false";
30
30
  export function apiKeysFrom(value) {
31
31
  return (value ?? "")
32
32
  .split(",")
package/dist/index.d.ts CHANGED
@@ -5,21 +5,24 @@
5
5
  */
6
6
  export type { Agent, AgentErrorData, AgentInfo, CompactOptions, ContentPart, CreateSessionOptions, EntriesOptions, Entry, ErrorCode, Json, ListOptions, Message, MessageKey, MessageStatus, MessageStreamEvent, ModelRef, Page, SendRequest, Session, SessionChanges, SessionEvent, SessionSettings, SessionStatus, ThinkingLevel, Tokens, Usage, WaitOptions, } from "./agent-api.ts";
7
7
  export { AgentError, LIMITS, hasEnded } from "./agent-api.ts";
8
- export { openAgent, type LocalAgent, type OpenAgentOptions, type ServeAgentOptions, } from "./harnesses/pi-durable/serve.ts";
8
+ export { openAgent, type InstanceLocation, type LocalAgent, type OpenAgentOptions, type ServeAgentOptions, } from "./harnesses/pi-durable/serve.ts";
9
9
  export type { Service } from "./service.ts";
10
- export { ContentNameError, addContent, createAgent, listContent, removeContent, type ContentEdit, type CreatedAgent, } from "./authoring.ts";
10
+ export { ContentNameError, addContent, createAgent, listContent, removeContent, type ContentEdit, type ContentScope, type CreatedAgent, } from "./authoring.ts";
11
11
  export type { ContentAddition, SourceOptions } from "./content/source.ts";
12
12
  export { readContentSource } from "./content/source.ts";
13
- export type { BoundContent, ContentBinding, ContentEntry } from "./content/declare.ts";
13
+ export type { ContentEntry } from "./content/declare.ts";
14
14
  export type { ResolvedContent } from "./content/resolve.ts";
15
15
  export { SignInCancelled, type Credential, type CredentialStore, type ProviderAuth, type SignInEvent, type SignInInteraction, type SignInMethod, type SignInOption, type SignInPrompt, type SignInRequest, type SignInResult, } from "./sign-in-api.ts";
16
16
  export { credentialFile, providerAuth, signIn, signInOptions } from "./harnesses/models/host.ts";
17
- export { McpError, type McpErrorCode, type McpOptions, type McpAuthorization, type McpGrantRecord, type McpGrantSummary, type McpGrantStore, type McpGrantAccess, type McpServerSummary, type McpSignInRequest, type McpSignInInteraction, } from "./mcp-api.ts";
17
+ export { McpError, type McpErrorCode, type McpOptions, type McpInspectOptions, type McpConfigInput, type McpAuthorization, type McpGrantRecord, type McpGrantSummary, type McpGrantStore, type McpGrantAccess, type McpServerSummary, type McpSignInRequest, type McpSignInInteraction, } from "./mcp-api.ts";
18
+ export { baseEnv } from "./child-env.ts";
18
19
  export { inspectMcp } from "./mcp/host.ts";
19
20
  export { signInMcp } from "./mcp/sign-in.ts";
20
21
  export { mcpGrantFile } from "./mcp/grants.ts";
21
- export { defineAgent, defineSchedule, needs, type AgentConfig, type Needs, type SecretSpec, type SecretValues, } from "./harnesses/pi-durable/define.ts";
22
+ export { byIngress, defineAgent, defineSchedule, needs, type ByIngress, type Ingress, type AgentConfig, type AgentDefaults, type SettingsOf, type Needs, type SecretSpec, type SecretValues, } from "./harnesses/pi-durable/define.ts";
23
+ export type { InstanceConfig } from "./harnesses/pi-durable/config.ts";
22
24
  export type { ScheduleConfig } from "./schedule/schedule.ts";
25
+ export { defineDefinition, type AgentDefinition } from "./harnesses/pi-durable/definition.ts";
23
26
  export { defineChannel, type ChannelContext, type ChannelHandler, type ChannelModule, type ForkSource, type LongConnection, type LongConnectionChannelModule, type Routes, } from "./channel.ts";
24
27
  export { readBodyCapped } from "./channels/body.ts";
25
28
  export { text } from "./channels/respond.ts";
package/dist/index.js CHANGED
@@ -5,10 +5,13 @@ export { readContentSource } from "./content/source.js";
5
5
  export { SignInCancelled, } from "./sign-in-api.js";
6
6
  export { credentialFile, providerAuth, signIn, signInOptions } from "./harnesses/models/host.js";
7
7
  export { McpError, } from "./mcp-api.js";
8
+ export { baseEnv } from "./child-env.js";
8
9
  export { inspectMcp } from "./mcp/host.js";
9
10
  export { signInMcp } from "./mcp/sign-in.js";
10
11
  export { mcpGrantFile } from "./mcp/grants.js";
11
- export { defineAgent, defineSchedule, needs, } from "./harnesses/pi-durable/define.js";
12
+ export { byIngress, defineAgent, defineSchedule, needs, } from "./harnesses/pi-durable/define.js";
13
+ // What `fastagent build` writes into a package's generated entry, never written by hand.
14
+ export { defineDefinition } from "./harnesses/pi-durable/definition.js";
12
15
  export { defineChannel, } from "./channel.js";
13
16
  // What a channel reads a request with, answers with, and compares its platform's secret with (constant time).
14
17
  export { readBodyCapped } from "./channels/body.js";
@@ -0,0 +1,7 @@
1
+ export declare const INSTANCE_NAME_ENV = "FASTAGENT_NAME";
2
+ /** `name`, when it can name an instance: one path segment of letters, digits, `-` and `_`; refused otherwise. */
3
+ export declare function checkInstanceName(name: string): string;
4
+ /** The name `FASTAGENT_NAME` gives: unset and empty are the same, as for the path variables. */
5
+ export declare function enteredInstanceName(env?: NodeJS.ProcessEnv): string | undefined;
6
+ /** The instance's name: `given` when there is one (refused when it is not a segment), else the definition's, as one. */
7
+ export declare function instanceName(definitionName: string, given?: string): string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * An instance's name (agent-artifact.md §4.5): the one thing `info().name` and a host's report call it. An explicit name
3
+ * is one path segment, so it is safe to name a directory after, and is refused otherwise; the
4
+ * default is the definition's name made into one (`@scope/pkg` is not one), so two instances of one definition on one
5
+ * machine take distinct names only by being given them.
6
+ */
7
+ import { stripEdges } from "./strip-edges.js";
8
+ export const INSTANCE_NAME_ENV = "FASTAGENT_NAME";
9
+ const SEGMENT = /^[A-Za-z0-9_-]+$/;
10
+ /** `name`, when it can name an instance: one path segment of letters, digits, `-` and `_`; refused otherwise. */
11
+ export function checkInstanceName(name) {
12
+ if (!SEGMENT.test(name)) {
13
+ throw new Error(`the instance name ${JSON.stringify(name)} must be one path segment of letters, digits, "-" and "_"`);
14
+ }
15
+ return name;
16
+ }
17
+ /** The name `FASTAGENT_NAME` gives: unset and empty are the same, as for the path variables. */
18
+ export function enteredInstanceName(env = process.env) {
19
+ return env[INSTANCE_NAME_ENV] || undefined;
20
+ }
21
+ /** The instance's name: `given` when there is one (refused when it is not a segment), else the definition's, as one. */
22
+ export function instanceName(definitionName, given) {
23
+ if (given !== undefined)
24
+ return checkInstanceName(given);
25
+ return stripEdges(definitionName.replace(/[^A-Za-z0-9_-]/g, "-"), (char) => char === "-") || "agent";
26
+ }
package/dist/lark.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/lark` — the Lark-international compatibility surface over the canonical Feishu engine. */
2
- export { larkChannel, larkIngress, larkWebSocketChannel, defaultLarkRoute, larkEnvelope, type LarkChannelOptions, type LarkWebSocketChannelOptions, type LarkMessageEvent, type LarkMessage, type LarkRoute, type LarkFailure, } from "./channels/lark/lark.ts";
2
+ export { larkChannel, larkWebSocketChannel, defaultLarkRoute, larkEnvelope, type LarkChannelOptions, type LarkWebSocketChannelOptions, type LarkMessageEvent, type LarkMessage, type LarkRoute, type LarkFailure, } from "./channels/lark/lark.ts";
3
3
  export { larkThreads, larkTransport, type FeishuTransport as LarkTransport } from "./channels/feishu/shared-api.ts";
package/dist/lark.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/lark` — the Lark-international compatibility surface over the canonical Feishu engine. */
2
- export { larkChannel, larkIngress, larkWebSocketChannel, defaultLarkRoute, larkEnvelope, } from "./channels/lark/lark.js";
2
+ export { larkChannel, larkWebSocketChannel, defaultLarkRoute, larkEnvelope, } from "./channels/lark/lark.js";
3
3
  export { larkThreads, larkTransport } from "./channels/feishu/shared-api.js";
@@ -1,6 +1,6 @@
1
1
  /** Instance-owned MCP clients. The scope also joins refreshes after a caller abandons its wait. */
2
2
  import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
3
- import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
3
+ import { DEFAULT_INHERITED_ENV_VARS, StdioClientTransport } from "@modelcontextprotocol/client/stdio";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Exit from "effect/Exit";
6
6
  import * as Fiber from "effect/Fiber";
@@ -11,6 +11,15 @@ import { checkGrantTarget, discoverMcp, grantToken, mcpFailure } from "./auth.js
11
11
  import { mcpFetch, openMcpNetwork } from "./network.js";
12
12
  import { checkedGrant } from "./grants.js";
13
13
  import { log } from "../log.js";
14
+ /**
15
+ * The SDK starts a stdio server over `{ ...getDefaultEnvironment(), ...env }`, which adds this process's own `HOME`,
16
+ * `PATH`, `USER` and the like under whatever is given. Naming each of them with no value, where `env` holds none,
17
+ * leaves the server with exactly `env`: Node drops a variable whose value is undefined.
18
+ */
19
+ function ownEnvironment(env) {
20
+ const withheld = Object.fromEntries(DEFAULT_INHERITED_ENV_VARS.filter((name) => !(name in env)).map((name) => [name, undefined]));
21
+ return { ...withheld, ...env };
22
+ }
14
23
  export async function openMcpClients(servers, auth, options = {}) {
15
24
  const lifetime = Scope.makeUnsafe();
16
25
  const cleanupErrors = [];
@@ -52,7 +61,7 @@ export async function openMcpClients(servers, auth, options = {}) {
52
61
  command: server.command,
53
62
  args: server.args,
54
63
  cwd: server.cwd,
55
- env: server.env,
64
+ env: ownEnvironment(server.env),
56
65
  stderr: "pipe",
57
66
  });
58
67
  // Draining prevents a noisy child from blocking. Raw stderr can contain its supplied secrets.
@@ -1,19 +1,18 @@
1
1
  import * as Schema from "effect/Schema";
2
2
  import { type DeclaredSecret } from "../declared-secrets.ts";
3
- export declare const MCP_FILE = ".mcp.json";
4
- declare const Configuration: Schema.Struct<{
5
- readonly mcpServers: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Struct<{
6
- readonly type: Schema.optional<Schema.Literal<"stdio">>;
7
- readonly command: Schema.NonEmptyString;
8
- readonly args: Schema.optional<Schema.$Array<Schema.String>>;
9
- readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
10
- }>, Schema.Struct<{
11
- readonly type: Schema.Literal<"http">;
12
- readonly url: Schema.String;
13
- readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
14
- }>]>>;
15
- }>;
16
- export type McpConfiguration = typeof Configuration.Type;
3
+ import { type McpConfigInput } from "../mcp-api.ts";
4
+ declare const Declaration: Schema.Union<readonly [Schema.Struct<{
5
+ readonly type: Schema.optional<Schema.Literal<"stdio">>;
6
+ readonly command: Schema.NonEmptyString;
7
+ readonly args: Schema.optional<Schema.$Array<Schema.String>>;
8
+ readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
9
+ }>, Schema.Struct<{
10
+ readonly type: Schema.Literal<"http">;
11
+ readonly url: Schema.String;
12
+ readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
13
+ }>]>;
14
+ /** One server as an `mcpServers` map holds it. */
15
+ export type McpServerDeclaration = typeof Declaration.Type;
17
16
  export type McpServer = {
18
17
  name: string;
19
18
  type: "stdio";
@@ -27,8 +26,61 @@ export type McpServer = {
27
26
  url: string;
28
27
  headers: Record<string, string>;
29
28
  };
30
- export declare function readMcpConfiguration(dir: string): McpConfiguration;
31
- export declare function mcpSecrets(dir: string): DeclaredSecret[];
32
- export declare function checkMcpUrl(value: string, server?: string): URL;
33
- export declare function resolveMcpServers(dir: string, env: Readonly<Record<string, string | undefined>>, only?: string): McpServer[];
29
+ /** A server and where it was declared: what its refusals name, and what a relative command is relative to. */
30
+ export interface McpDeclaration {
31
+ name: string;
32
+ server: McpServerDeclaration;
33
+ /** The file that declared it, as a refusal names it. */
34
+ file: string;
35
+ /** The directory that file is in. None for an object, whose commands with a path must be absolute. */
36
+ base?: string;
37
+ origin: "program" | "instance";
38
+ }
39
+ /** The instance configuration as far as MCP reads it: its `mcpServers`, and where it is from. */
40
+ export interface McpInstance {
41
+ values: unknown;
42
+ label: string;
43
+ directory?: string;
44
+ }
45
+ /** An instance's servers, checked: what `resolveConfig` asks of a configuration, so its refusal is the file's. */
46
+ export declare function checkInstanceServers(instance: McpInstance): void;
47
+ /**
48
+ * The servers of the program in `dir` and of `instance`. An instance only adds: a name both declare is refused, naming
49
+ * both, so nothing is shadowed.
50
+ */
51
+ export declare function declareMcp(dir: string, instance?: McpInstance): McpDeclaration[];
52
+ /** An instance configuration given to `inspectMcp` or `signInMcp`: an object, or the absolute path of a `fastagent.json`. */
53
+ export declare function mcpInstance(config: McpConfigInput | undefined): McpInstance | undefined;
54
+ /** The secrets the servers' `${NAME}` references need: each required, declared where the server is. */
55
+ export declare function mcpSecrets(declarations: readonly McpDeclaration[]): DeclaredSecret[];
56
+ /**
57
+ * The header and environment values with no `${VAR}` in them: the format allows a literal credential, and where the
58
+ * file travels (a build publishes it, a deploy bakes or delivers it) each is said. FastAgent cannot tell a secret from a
59
+ * setting, so a caller warns and never refuses.
60
+ */
61
+ export declare function literalCredentials(declarations: readonly McpDeclaration[]): {
62
+ file: string;
63
+ server: string;
64
+ field: "headers" | "env";
65
+ key: string;
66
+ }[];
67
+ /**
68
+ * The stdio servers whose command is a path relative to the file that declares it, as it is written: what a build or a
69
+ * deploy has to be able to deliver, since a script is not found on a `PATH`. A command that starts with a `${VAR}` is
70
+ * left to the variable, and `args` are never read as paths, since FastAgent cannot know which strings are.
71
+ */
72
+ export declare function relativeCommands(declarations: readonly McpDeclaration[]): (McpDeclaration & {
73
+ command: string;
74
+ })[];
75
+ /** What to write instead of a literal credential, which a header's value usually carries a scheme in front of. */
76
+ export declare function literalCredentialAdvice({ field, key }: {
77
+ field: "headers" | "env";
78
+ key: string;
79
+ }): string;
80
+ export declare function checkMcpUrl(value: string, server?: string, file?: string): URL;
81
+ export declare function resolveMcpServers(declarations: readonly McpDeclaration[], env: Readonly<Record<string, string | undefined>>, only: string | undefined,
82
+ /** Where a stdio server runs: the instance's workspace, whoever declared it. */
83
+ cwd: string,
84
+ /** What every process the instance starts gets (child-env.ts), under a stdio server's own `env`. */
85
+ processEnv: Readonly<Record<string, string>>): McpServer[];
34
86
  export {};