@desplega.ai/agent-swarm 2.6.0 → 2.7.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 (324) hide show
  1. package/README.md +2 -0
  2. package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
  3. package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
  4. package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
  5. package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
  6. package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
  7. package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
  8. package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
  9. package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
  10. package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
  11. package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
  12. package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
  13. package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
  14. package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
  15. package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
  16. package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
  17. package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
  18. package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
  19. package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
  20. package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
  21. package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
  22. package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
  23. package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
  24. package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
  25. package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
  26. package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
  27. package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
  28. package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
  29. package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
  30. package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
  31. package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
  32. package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
  33. package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
  34. package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
  35. package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
  36. package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
  37. package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
  38. package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
  39. package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
  40. package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
  41. package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
  42. package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
  43. package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
  44. package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
  45. package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
  46. package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
  47. package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
  48. package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
  49. package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
  50. package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
  51. package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
  52. package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
  53. package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
  54. package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
  55. package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
  56. package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
  57. package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
  58. package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
  59. package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
  60. package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
  61. package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
  62. package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
  63. package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
  64. package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
  65. package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
  66. package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
  67. package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
  68. package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
  69. package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
  70. package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
  71. package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
  72. package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
  73. package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
  74. package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
  75. package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
  76. package/dist/cli.js +31 -21
  77. package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
  78. package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
  79. package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
  80. package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
  81. package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
  82. package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
  83. package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
  84. package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
  85. package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
  86. package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
  87. package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
  88. package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
  89. package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
  90. package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
  91. package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
  92. package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
  93. package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
  94. package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
  95. package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
  96. package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
  97. package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
  98. package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
  99. package/dist/handlers-pspryh6g.js +41 -0
  100. package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
  101. package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
  102. package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
  103. package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
  104. package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
  105. package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
  106. package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
  107. package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
  108. package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
  109. package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
  110. package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
  111. package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
  112. package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
  113. package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
  114. package/dist/lead-c0pjcaw4.js +79 -0
  115. package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
  116. package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
  117. package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
  118. package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
  119. package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
  120. package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
  121. package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
  122. package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
  123. package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
  124. package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
  125. package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
  126. package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
  127. package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
  128. package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
  129. package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
  130. package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
  131. package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
  132. package/dist/worker-d4q9sqbc.js +79 -0
  133. package/openapi.json +1536 -407
  134. package/package.json +7 -5
  135. package/src/be/automation-preflight.ts +2 -0
  136. package/src/be/connector-codes.ts +65 -22
  137. package/src/be/db/api-keys.ts +9 -0
  138. package/src/be/db/workflow-runs.ts +36 -5
  139. package/src/be/db.ts +80 -22
  140. package/src/be/memory/boot-reembed.ts +4 -1
  141. package/src/be/memory/constants.ts +6 -0
  142. package/src/be/memory/embedding-recovery.ts +48 -0
  143. package/src/be/memory/index-content.ts +5 -2
  144. package/src/be/memory/providers/openai-embedding.ts +65 -14
  145. package/src/be/memory/task-learning.ts +257 -0
  146. package/src/be/memory/types.ts +13 -2
  147. package/src/be/migrations/245_script_executor.sql +3 -0
  148. package/src/be/migrations/246_connector_code_kind.sql +11 -0
  149. package/src/be/oauth-refresh-sweep.ts +2 -1
  150. package/src/be/onboarding.ts +125 -15
  151. package/src/be/rbac-roles.ts +4 -1
  152. package/src/be/scripts/boot-reembed.ts +2 -0
  153. package/src/be/scripts/db.ts +14 -4
  154. package/src/be/scripts/embeddings.ts +4 -2
  155. package/src/be/scripts/run-saved.ts +1 -0
  156. package/src/be/scripts/typecheck.ts +9 -3
  157. package/src/be/seed/agent-fs-provision.ts +75 -14
  158. package/src/be/seed-skills/index.ts +7 -0
  159. package/src/be/swarm-config-guard.ts +3 -0
  160. package/src/cli.tsx +9 -0
  161. package/src/comb/config.ts +6 -1
  162. package/src/commands/onboard/agent-fs-version.ts +5 -0
  163. package/src/commands/onboard/compose-generator.ts +117 -4
  164. package/src/commands/onboard/env-generator.ts +9 -0
  165. package/src/commands/onboard/tei-image.ts +12 -0
  166. package/src/commands/onboard/types.ts +7 -0
  167. package/src/commands/onboard.tsx +11 -1
  168. package/src/commands/provider-credentials.ts +2 -1
  169. package/src/commands/runner.ts +14 -1
  170. package/src/heartbeat/heartbeat.ts +14 -1
  171. package/src/heartbeat/script-runner-probe.ts +47 -0
  172. package/src/http/agentfs-proxy.ts +369 -0
  173. package/src/http/all-routes.ts +1 -0
  174. package/src/http/api-keys.ts +24 -9
  175. package/src/http/codex-oauth-device.ts +8 -1
  176. package/src/http/fs.ts +64 -10
  177. package/src/http/idempotency-key.ts +1 -1
  178. package/src/http/inbox-state.ts +27 -1
  179. package/src/http/index.ts +7 -4
  180. package/src/http/memory.ts +7 -4
  181. package/src/http/onboarding.ts +139 -11
  182. package/src/http/pages-public.ts +38 -5
  183. package/src/http/scripts.ts +27 -0
  184. package/src/http/session-access.ts +24 -0
  185. package/src/http/sessions.ts +10 -6
  186. package/src/http/tasks.ts +85 -11
  187. package/src/http/telemetry-ui.ts +1 -1
  188. package/src/http/user-roles.ts +4 -1
  189. package/src/http/users.ts +272 -3
  190. package/src/http/utils.ts +22 -3
  191. package/src/http/workflows.ts +10 -0
  192. package/src/http/x.ts +5 -0
  193. package/src/prompts/base-prompt.ts +45 -10
  194. package/src/prompts/session-templates.ts +43 -5
  195. package/src/providers/amp-adapter.ts +1 -1
  196. package/src/providers/codex-adapter.ts +6 -1
  197. package/src/providers/cursor-adapter.ts +1 -1
  198. package/src/providers/dsh-adapter.ts +1 -1
  199. package/src/providers/pi-mono-adapter.ts +99 -14
  200. package/src/rbac/legacy-policy.ts +1 -0
  201. package/src/rbac/permissions.ts +4 -0
  202. package/src/scheduler/scheduler.ts +2 -1
  203. package/src/scripts-runtime/eval-harness.ts +8 -1
  204. package/src/scripts-runtime/executors/native.ts +22 -16
  205. package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
  206. package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
  207. package/src/scripts-runtime/executors/quickjs.ts +7 -0
  208. package/src/scripts-runtime/executors/registry.ts +32 -0
  209. package/src/scripts-runtime/executors/types.ts +7 -0
  210. package/src/scripts-runtime/host-validated-args.ts +133 -0
  211. package/src/scripts-runtime/loader.ts +30 -3
  212. package/src/scripts-runtime/swarm-sdk.ts +9 -2
  213. package/src/scripts-runtime/types/stdlib.d.ts +9 -1
  214. package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
  215. package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
  216. package/src/tests/agentfs-proxy.test.ts +1106 -0
  217. package/src/tests/api-key-status-by-suffix.test.ts +242 -0
  218. package/src/tests/base-prompt.test.ts +139 -4
  219. package/src/tests/codex-adapter.test.ts +46 -8
  220. package/src/tests/codex-oauth-device.test.ts +12 -1
  221. package/src/tests/comb-config-guard.test.ts +20 -0
  222. package/src/tests/comb-links.test.ts +79 -1
  223. package/src/tests/comb-status.test.ts +44 -0
  224. package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
  225. package/src/tests/credential-check.test.ts +4 -2
  226. package/src/tests/docs-index-footprint.test.js +3 -3
  227. package/src/tests/docs-index.test.ts +135 -0
  228. package/src/tests/dsh-adapter.test.ts +1 -1
  229. package/src/tests/fetch-resolved-env.test.ts +16 -2
  230. package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
  231. package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
  232. package/src/tests/fixtures/onboard/content.env.txt +29 -0
  233. package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
  234. package/src/tests/fixtures/onboard/dev.env.txt +28 -0
  235. package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
  236. package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
  237. package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
  238. package/src/tests/fixtures/onboard/full.env.txt +36 -0
  239. package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
  240. package/src/tests/fixtures/onboard/research.env.txt +28 -0
  241. package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
  242. package/src/tests/fixtures/onboard/solo.env.txt +26 -0
  243. package/src/tests/heartbeat-checklist.test.ts +89 -0
  244. package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
  245. package/src/tests/http-log-scrubbing.test.ts +34 -1
  246. package/src/tests/http-task-write-ownership.test.ts +5 -1
  247. package/src/tests/invites-http.test.ts +464 -0
  248. package/src/tests/memory-embedding-input-cap.test.ts +122 -1
  249. package/src/tests/memory-embedding-recovery.test.ts +137 -0
  250. package/src/tests/oauth-refresh-sweep.test.ts +34 -1
  251. package/src/tests/onboard-compose.test.ts +162 -0
  252. package/src/tests/onboard-env.test.ts +24 -0
  253. package/src/tests/onboard-non-interactive.test.ts +29 -0
  254. package/src/tests/onboard-tei-image.test.ts +36 -0
  255. package/src/tests/onboarding.test.ts +451 -4
  256. package/src/tests/openai-embedding-provider.test.ts +105 -2
  257. package/src/tests/opencode-adapter.test.ts +2 -1
  258. package/src/tests/pages-frame-ancestors.test.ts +162 -0
  259. package/src/tests/pi-codemode.test.ts +2 -1
  260. package/src/tests/pi-mono-adapter.test.ts +54 -0
  261. package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
  262. package/src/tests/pi-native-mcp.test.ts +13 -1
  263. package/src/tests/pi-tool-exposure.test.ts +8 -8
  264. package/src/tests/preload-env.test.ts +4 -0
  265. package/src/tests/preload.ts +4 -0
  266. package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
  267. package/src/tests/prompt-template-session.test.ts +20 -3
  268. package/src/tests/rbac-admission-e2e.test.ts +22 -6
  269. package/src/tests/rbac-admission.test.ts +342 -4
  270. package/src/tests/rbac-engine.test.ts +1 -0
  271. package/src/tests/rbac-roles.test.ts +9 -1
  272. package/src/tests/request-composition-derive.test.ts +329 -0
  273. package/src/tests/script-connections.test.ts +2 -0
  274. package/src/tests/script-executor-conformance.test.ts +36 -1
  275. package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
  276. package/src/tests/script-executor-quickjs.test.ts +140 -0
  277. package/src/tests/script-host-validated-args-native.test.ts +196 -0
  278. package/src/tests/script-per-script-executor.test.ts +415 -0
  279. package/src/tests/scripts-external-api.test.ts +45 -0
  280. package/src/tests/sdk-allowlist.test.ts +3 -2
  281. package/src/tests/self-improvement.test.ts +4 -4
  282. package/src/tests/send-task-requester-gate.test.ts +64 -3
  283. package/src/tests/sessions-move.test.ts +39 -1
  284. package/src/tests/store-progress-learning.test.ts +381 -0
  285. package/src/tests/ui-logs-parser.test.ts +180 -0
  286. package/src/tests/whoami-route.test.ts +41 -0
  287. package/src/tests/workflow-engine-v2.test.ts +116 -1
  288. package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
  289. package/src/tests/workflow-run-finished-event.test.ts +263 -0
  290. package/src/tests/workflow-swarm-script.test.ts +34 -0
  291. package/src/tools/inject-learning.ts +1 -1
  292. package/src/tools/memory-edit.ts +1 -1
  293. package/src/tools/memory-search.ts +1 -1
  294. package/src/tools/script-common.ts +1 -0
  295. package/src/tools/script-run.ts +7 -0
  296. package/src/tools/script-upsert.ts +7 -0
  297. package/src/tools/store-progress.ts +71 -1
  298. package/src/tools/utils.ts +12 -1
  299. package/src/tools/workflows/trigger-workflow.ts +104 -10
  300. package/src/types.ts +12 -1
  301. package/src/utils/constants.ts +39 -0
  302. package/src/utils/credentials.ts +5 -2
  303. package/src/utils/task-attachment-links.ts +15 -3
  304. package/src/workflows/engine.ts +18 -16
  305. package/src/workflows/executors/swarm-script.ts +5 -0
  306. package/src/workflows/foreach-driver.ts +5 -11
  307. package/src/workflows/node-timeout.ts +30 -0
  308. package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
  309. package/templates/community/linear-delivery-loop/README.md +38 -0
  310. package/templates/community/linear-delivery-loop/config.example.json +31 -0
  311. package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
  312. package/templates/community/linear-delivery-loop/schedule.json +10 -0
  313. package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
  314. package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
  315. package/templates/skills/swarm-docs/config.json +14 -0
  316. package/templates/skills/swarm-docs/content.md +144 -0
  317. package/dist/handlers-eqsnmv95.js +0 -41
  318. package/dist/lead-23e7z59t.js +0 -79
  319. package/dist/worker-e3w7vye1.js +0 -79
  320. package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
  321. package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
  322. package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
  323. package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
  324. /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
@@ -8,6 +8,7 @@ import {
8
8
  getSwarmConfigs,
9
9
  upsertSwarmConfig,
10
10
  } from "../db";
11
+ import { getUserGrant } from "../rbac-roles";
11
12
  import type { Seeder, SeedItem } from "./types";
12
13
 
13
14
  const KIND = "agent-fs-provision";
@@ -16,6 +17,10 @@ const PROVISION_VERSION = 1;
16
17
  const ORG_NAME = "swarm";
17
18
  const DRIVE_NAME = "shared";
18
19
  const PROVISION_HASH_KEY = "AGENT_FS_PROVISION_HASH";
20
+ // Stored in place of the hash when invites failed transiently. upstreamHash() maps it to
21
+ // null, so the next boot re-applies even when the org, drive and key rows already exist
22
+ // (otherwise the adopt-current-state fallback below would mark the failed run as in sync).
23
+ const PROVISION_RETRY_MARKER = "retry-pending";
19
24
  const API_KEY_CONFIG = "API_AGENT_FS_API_KEY";
20
25
  const LEGACY_SHARED_KEY_CONFIG = "AGENT_FS_API_KEY";
21
26
 
@@ -80,6 +85,7 @@ export const agentFsProvisionSeeder: Seeder<AgentFsSeedItem> = {
80
85
 
81
86
  async upstreamHash(): Promise<string | null> {
82
87
  const existingHash = await getConfigValue(PROVISION_HASH_KEY);
88
+ if (existingHash === PROVISION_RETRY_MARKER) return null;
83
89
  if (existingHash) return existingHash;
84
90
 
85
91
  const hasOrg = !!(await getConfigValue("AGENT_FS_DEFAULT_ORG_ID"));
@@ -113,16 +119,33 @@ async function provisionAgentFs(item: AgentFsSeedItem): Promise<void> {
113
119
  shared.orgId,
114
120
  );
115
121
 
122
+ let transientFailures = 0;
116
123
  for (const invite of item.invites) {
117
124
  const currentRole = currentRoles.get(invite.email.toLowerCase());
118
125
  if (currentRole && roleRank(currentRole) >= roleRank(invite.role)) continue;
119
- await inviteToSharedOrg(item.apiUrl, shared.authHeaders, shared.orgId, invite);
126
+ const outcome = await inviteToSharedOrg(item.apiUrl, shared.authHeaders, shared.orgId, invite);
127
+ if (outcome === "transient-failure") transientFailures++;
120
128
  }
129
+ // "no-account" is not a failure to retry: agent-fs has no pending invites. The person's
130
+ // account is created later in the browser, by the /setup Files step or Comb's connect card,
131
+ // and both invite in the same flow. A transient failure (network, 5xx other than not-found,
132
+ // 401/403) must not store the hash, so the next boot re-runs the invites. The org and drive
133
+ // config rows were already written by ensureAgentFsSharedProvisioning above.
134
+ if (transientFailures > 0) {
135
+ await storeProvisionHash(PROVISION_RETRY_MARKER);
136
+ throw new Error(
137
+ `agent-fs invites failed for ${transientFailures} member(s); will retry on next boot`,
138
+ );
139
+ }
140
+
141
+ await storeProvisionHash(item.contentHash);
142
+ }
121
143
 
144
+ async function storeProvisionHash(value: string): Promise<void> {
122
145
  await upsertSwarmConfig({
123
146
  scope: "global",
124
147
  key: PROVISION_HASH_KEY,
125
- value: item.contentHash,
148
+ value,
126
149
  isSecret: false,
127
150
  description: "Internal hash for agent-fs provisioning reconciliation",
128
151
  });
@@ -203,17 +226,31 @@ export async function ensureAgentFsSharedProvisioning(options?: {
203
226
  return { apiUrl, apiKey, orgId, driveId, authHeaders };
204
227
  }
205
228
 
229
+ export type SharedOrgInviteResult = {
230
+ orgId: string;
231
+ invited: boolean;
232
+ status: "already_member" | "added" | "pending";
233
+ inviteId?: string;
234
+ expiresAt?: string;
235
+ acceptUrl?: string;
236
+ };
237
+
206
238
  /**
207
239
  * Invite an external (non-agent) member into the shared org using the
208
240
  * API-owned bootstrap credentials. Powers `POST /api/fs/members/invite` so
209
241
  * callers holding the tenant API key (e.g. the cloud control plane's
210
242
  * Connect-to-Drive flow) never need the bootstrap key itself — it is
211
243
  * API-only and never served over HTTP.
244
+ *
245
+ * agent-fs (>= 0.18.9) adds an email that has an account directly
246
+ * (`status: "added"`) and answers an email without one with a pending invite
247
+ * (`status: "pending"`, plus `acceptUrl` when its app URL is configured).
248
+ * agent-fs sends no email, so the accept link is passed back to the caller.
212
249
  */
213
250
  export async function inviteEmailToSharedOrg(
214
251
  email: string,
215
252
  role: Role,
216
- ): Promise<{ orgId: string; invited: boolean }> {
253
+ ): Promise<SharedOrgInviteResult> {
217
254
  const shared = await ensureAgentFsSharedProvisioning();
218
255
  const currentRoles = await getCurrentOrgMemberRoles(
219
256
  shared.apiUrl,
@@ -222,15 +259,31 @@ export async function inviteEmailToSharedOrg(
222
259
  );
223
260
  const currentRole = currentRoles.get(email.trim().toLowerCase());
224
261
  if (currentRole && roleRank(currentRole) >= roleRank(role)) {
225
- return { orgId: shared.orgId, invited: false };
262
+ return { orgId: shared.orgId, invited: false, status: "already_member" };
226
263
  }
227
- await agentFsRequest(shared.apiUrl, `/orgs/${encodeURIComponent(shared.orgId)}/members/invite`, {
264
+ const response = await agentFsRequest<{
265
+ status?: unknown;
266
+ inviteId?: unknown;
267
+ expiresAt?: unknown;
268
+ acceptUrl?: unknown;
269
+ }>(shared.apiUrl, `/orgs/${encodeURIComponent(shared.orgId)}/members/invite`, {
228
270
  method: "POST",
229
271
  headers: shared.authHeaders,
230
272
  body: { email: email.trim(), role },
231
273
  allowConflict: true,
232
274
  });
233
- return { orgId: shared.orgId, invited: true };
275
+ // Older agent-fs answers a direct add with a bare `{ ok: true }`.
276
+ if (response.status !== "pending") {
277
+ return { orgId: shared.orgId, invited: true, status: "added" };
278
+ }
279
+ return {
280
+ orgId: shared.orgId,
281
+ invited: true,
282
+ status: "pending",
283
+ ...(typeof response.inviteId === "string" ? { inviteId: response.inviteId } : {}),
284
+ ...(typeof response.expiresAt === "string" ? { expiresAt: response.expiresAt } : {}),
285
+ ...(typeof response.acceptUrl === "string" ? { acceptUrl: response.acceptUrl } : {}),
286
+ };
234
287
  }
235
288
 
236
289
  export async function ensureAgentFsCredentialsForAgent(agentId: string): Promise<{
@@ -396,7 +449,7 @@ async function inviteToSharedOrg(
396
449
  headers: Record<string, string>,
397
450
  orgId: string,
398
451
  invite: InviteTarget,
399
- ): Promise<void> {
452
+ ): Promise<"invited" | "no-account" | "transient-failure"> {
400
453
  try {
401
454
  await agentFsRequest(apiUrl, `/orgs/${encodeURIComponent(orgId)}/members/invite`, {
402
455
  method: "POST",
@@ -404,6 +457,7 @@ async function inviteToSharedOrg(
404
457
  body: invite,
405
458
  allowConflict: true,
406
459
  });
460
+ return "invited";
407
461
  } catch (error) {
408
462
  console.warn(
409
463
  scrubSecrets(
@@ -412,6 +466,12 @@ async function inviteToSharedOrg(
412
466
  }`,
413
467
  ),
414
468
  );
469
+ // agent-fs answers an unregistered invitee with HTTP 500 "User with email ... not found".
470
+ const noAccount =
471
+ error instanceof AgentFsRequestError &&
472
+ error.status === 500 &&
473
+ /not found/i.test(error.message);
474
+ return noAccount ? "no-account" : "transient-failure";
415
475
  }
416
476
  }
417
477
 
@@ -499,18 +559,19 @@ async function resolveInviteTargets(): Promise<InviteTarget[]> {
499
559
  const targets = new Map<string, InviteTarget>();
500
560
 
501
561
  for (const user of await getAllUsers()) {
502
- if (!user.email) continue;
503
- const role = isExplicitViewerRole(user.role) ? "viewer" : "editor";
504
- targets.set(user.email.toLowerCase(), { email: user.email, role });
562
+ const email = user.email?.trim();
563
+ if (!email) continue;
564
+ // Only active users get a seat: invited and suspended users cannot sign in yet.
565
+ if ((user.status ?? "active") !== "active") continue;
566
+ // Follow the RBAC grant, not the free-text profile role, so an RBAC Viewer stays a viewer.
567
+ const grant = await getUserGrant(user.id);
568
+ const role: Role = grant.grantsAll || grant.verbs.has("task.fs.mutate") ? "editor" : "viewer";
569
+ targets.set(email.toLowerCase(), { email, role });
505
570
  }
506
571
 
507
572
  return [...targets.values()].sort((a, b) => a.email.localeCompare(b.email));
508
573
  }
509
574
 
510
- function isExplicitViewerRole(role: string | undefined): boolean {
511
- return /\b(viewer|read[-\s]?only|guest)\b/i.test(role ?? "");
512
- }
513
-
514
575
  async function provisionHashFromCurrentState(): Promise<string> {
515
576
  return provisionHash({
516
577
  apiUrl: await resolveApiUrl(),
@@ -208,6 +208,12 @@ import stepRunningConfig from "../../../templates/skills/step-running/config.jso
208
208
  import stepRunningContent from "../../../templates/skills/step-running/content.md" with {
209
209
  type: "text",
210
210
  };
211
+ import swarmDocsConfig from "../../../templates/skills/swarm-docs/config.json" with {
212
+ type: "text",
213
+ };
214
+ import swarmDocsContent from "../../../templates/skills/swarm-docs/content.md" with {
215
+ type: "text",
216
+ };
211
217
  import swarmExtensionsConfig from "../../../templates/skills/swarm-extensions/config.json" with {
212
218
  type: "text",
213
219
  };
@@ -367,6 +373,7 @@ const BUILT_IN_SKILL_SOURCES = [
367
373
  { config: reviewingConfig, body: reviewingContent },
368
374
  { config: scheduledTaskResilienceConfig, body: scheduledTaskResilienceContent },
369
375
  { config: schedulingConfig, body: schedulingContent },
376
+ { config: swarmDocsConfig, body: swarmDocsContent },
370
377
  { config: swarmExtensionsConfig, body: swarmExtensionsContent },
371
378
  { config: scriptBuilderConfig, body: scriptBuilderContent },
372
379
  { config: scriptWorkflowsConfig, body: scriptWorkflowsContent },
@@ -410,6 +410,8 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
410
410
  value,
411
411
  "Invalid AGENT_FS_PUBLIC_URL (must be an http(s) URL with no query string or fragment, e.g. https://agent-fs.example.com. Leave blank to use AGENT_FS_API_URL)",
412
412
  ),
413
+ // Where people-facing file links point. Blank keeps the automatic choice.
414
+ ...enumValidator("AGENT_FS_LINK_TARGET", ["dashboard", "live", ""]),
413
415
  HARNESS_PROVIDER: (value) => {
414
416
  const parsed = ProviderNameSchema.safeParse(value);
415
417
  if (parsed.success) return null;
@@ -520,6 +522,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
520
522
  "DB_RETENTION_DRY_RUN",
521
523
  "MODEL_AUTO_UPGRADE",
522
524
  "COMB_ENABLED",
525
+ "AGENT_FS_PROXY",
523
526
  ]),
524
527
  ...enumValidator("SLACK_MODE", ["socket", "http"]),
525
528
  ...enumValidator("SLACK_THREAD_STEERING", ["off", "lead", "all"]),
package/src/cli.tsx CHANGED
@@ -41,6 +41,7 @@ interface ParsedArgs {
41
41
  maxConcurrentTasks: string | undefined;
42
42
  pullPolicy: string | undefined;
43
43
  containerEngine: string | undefined;
44
+ noAgentFs: boolean;
44
45
  open: boolean;
45
46
  showHelp: boolean;
46
47
  dbPath: string;
@@ -63,6 +64,7 @@ function parseArgs(args: string[]): ParsedArgs {
63
64
  let maxConcurrentTasks: string | undefined;
64
65
  let pullPolicy: string | undefined;
65
66
  let containerEngine: string | undefined;
67
+ let noAgentFs = false;
66
68
  let open = false;
67
69
  let showHelp = false;
68
70
  let dbPath = "";
@@ -122,6 +124,8 @@ function parseArgs(args: string[]): ParsedArgs {
122
124
  if (value && !value.startsWith("-")) i++;
123
125
  } else if (arg?.startsWith("--container-engine=")) {
124
126
  containerEngine = arg.slice("--container-engine=".length);
127
+ } else if (arg === "--no-agent-fs") {
128
+ noAgentFs = true;
125
129
  } else if (arg === "--open") {
126
130
  open = true;
127
131
  } else if (arg === "--help" || arg === "-h") {
@@ -149,6 +153,7 @@ function parseArgs(args: string[]): ParsedArgs {
149
153
  maxConcurrentTasks,
150
154
  pullPolicy,
151
155
  containerEngine,
156
+ noAgentFs,
152
157
  open,
153
158
  showHelp,
154
159
  dbPath,
@@ -185,6 +190,7 @@ const COMMAND_HELP: Record<
185
190
  " --max-concurrent-tasks <n> Tasks per agent (1-100; default: lead 2, worker 1)",
186
191
  " --pull-policy <policy> Pull policy: always, missing, never (default: always)",
187
192
  " --container-engine <engine> auto, docker, podman (default: $AGENT_SWARM_CONTAINER_ENGINE, else auto; auto prefers Docker)",
193
+ " --no-agent-fs With --yes: skip the bundled agent-fs (files, Comb) and TEI (memory embeddings)",
188
194
  " -h, --help Show this help",
189
195
  ].join("\n"),
190
196
  examples: [
@@ -192,6 +198,7 @@ const COMMAND_HELP: Record<
192
198
  ` ${binName} onboard --dry-run`,
193
199
  ` ${binName} onboard --yes --preset=full`,
194
200
  ` ${binName} onboard --yes --preset=dev`,
201
+ ` ${binName} onboard --yes --preset=solo --no-agent-fs`,
195
202
  ` ${binName} onboard --container-engine=podman`,
196
203
  ` ANTHROPIC_API_KEY=sk-... ${binName} onboard --yes --preset=solo`,
197
204
  ].join("\n"),
@@ -693,6 +700,7 @@ function App({ args }: { args: ParsedArgs }) {
693
700
  maxConcurrentTasks,
694
701
  pullPolicy,
695
702
  containerEngine,
703
+ noAgentFs,
696
704
  } = args;
697
705
 
698
706
  switch (command) {
@@ -707,6 +715,7 @@ function App({ args }: { args: ParsedArgs }) {
707
715
  maxConcurrentTasks,
708
716
  pullPolicy,
709
717
  containerEngine,
718
+ noAgentFs,
710
719
  }}
711
720
  />
712
721
  );
@@ -2,6 +2,8 @@ import {
2
2
  getAgentFsDefaultDriveId,
3
3
  getAgentFsDefaultOrgId,
4
4
  getAgentFsLiveUrl,
5
+ getPublicMcpBaseUrl,
6
+ isAgentFsProxyEnabled,
5
7
  isCombEnabled,
6
8
  } from "../utils/constants";
7
9
 
@@ -27,9 +29,12 @@ function trimUrl(value: string | undefined): string | null {
27
29
  */
28
30
  export function getCombConfig(): CombConfig {
29
31
  const internalUrl = trimUrl(process.env.AGENT_FS_API_URL);
32
+ // With the proxy on, the browser reaches agent-fs through the API itself.
33
+ const browserDefault =
34
+ isAgentFsProxyEnabled() && internalUrl ? `${getPublicMcpBaseUrl()}/agentfs` : internalUrl;
30
35
  return {
31
36
  enabled: isCombEnabled(),
32
- apiUrl: trimUrl(process.env.AGENT_FS_PUBLIC_URL) ?? internalUrl,
37
+ apiUrl: trimUrl(process.env.AGENT_FS_PUBLIC_URL) ?? browserDefault,
33
38
  liveUrl: getAgentFsLiveUrl(),
34
39
  orgId: getAgentFsDefaultOrgId() ?? null,
35
40
  driveId: getAgentFsDefaultDriveId() ?? null,
@@ -0,0 +1,5 @@
1
+ /**
2
+ * agent-fs image tag bundled by `agent-swarm onboard --yes`.
3
+ * `bun run bump:agent-fs` rewrites it together with the other pins.
4
+ */
5
+ export const AGENT_FS_VERSION = "0.18.9";
@@ -1,3 +1,4 @@
1
+ import { AGENT_FS_VERSION } from "./agent-fs-version.ts";
1
2
  import { composeCommandText, engineForCommands } from "./container-engine.ts";
2
3
  import { expandServices } from "./service-names.ts";
3
4
  import type { OnboardState } from "./types.ts";
@@ -71,6 +72,91 @@ const ENV_SENTRY_AUTH_TOKEN = " - SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}";
71
72
  // biome-ignore lint/suspicious/noTemplateCurlyInString: Docker Compose env var syntax
72
73
  const ENV_SENTRY_ORG = " - SENTRY_ORG=${SENTRY_ORG}";
73
74
 
75
+ // ── Bundled agent-fs + TEI (`onboard --yes`, flag `bundleAgentFs`) ──
76
+ const APP_URL = "https://app.agent-swarm.dev";
77
+ const AGENT_FS_INTERNAL_URL = "http://agent-fs:7433";
78
+ const TEI_MODEL = "nomic-ai/nomic-embed-text-v1.5";
79
+ // Compose `${PUBLIC_MCP_BASE_URL:-http://localhost:<port>}`; the caller appends `<port>}`.
80
+ const ENV_AGENT_FS_PUBLIC_URL_PREFIX =
81
+ " - AGENT_FS_PUBLIC_URL=${PUBLIC_MCP_BASE_URL:-http://localhost:";
82
+ const TEI_IMAGE_EXPRESSION =
83
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: Docker Compose env var syntax
84
+ "${TEI_IMAGE:-ghcr.io/huggingface/text-embeddings-inference:cpu-1.9.4}";
85
+
86
+ /** The API side of the bundle: agent-fs proxy, Comb, dashboard links, TEI memory. */
87
+ function appendBundleApiEnvironment(lines: string[]) {
88
+ lines.push(` - AGENT_FS_API_URL=${AGENT_FS_INTERNAL_URL}`);
89
+ lines.push(" - AGENT_FS_PROXY=true");
90
+ lines.push(" - COMB_ENABLED=true");
91
+ lines.push(" - AGENT_FS_LINK_TARGET=dashboard");
92
+ lines.push(" - EMBEDDING_API_BASE_URL=http://tei:80/v1");
93
+ lines.push(` - EMBEDDING_MODEL=${TEI_MODEL}`);
94
+ lines.push(" - EMBEDDING_API_KEY=local-tei");
95
+ // Quoted: unquoted, `KEY=search_query: ` parses as a mapping and drops the trailing space.
96
+ lines.push(' - "EMBEDDING_QUERY_PREFIX=search_query: "');
97
+ lines.push(' - "EMBEDDING_DOCUMENT_PREFIX=search_document: "');
98
+ }
99
+
100
+ function appendBundleServices(lines: string[], state: OnboardState, apiPort: number) {
101
+ lines.push("");
102
+ lines.push(" agent-fs:");
103
+ lines.push(` image: "ghcr.io/desplega-ai/agent-fs:${AGENT_FS_VERSION}"`);
104
+ lines.push(" container_name: swarm-agent-fs");
105
+ lines.push(` pull_policy: ${state.pullPolicy}`);
106
+ lines.push("");
107
+ lines.push(" environment:");
108
+ lines.push(" - AGENT_FS_HOME=/data");
109
+ lines.push(" - AGENT_FS_STORAGE_PROVIDER=local");
110
+ lines.push(" - AGENT_FS_LOCAL_ROOT=/data/storage");
111
+ lines.push(" - HOST=0.0.0.0");
112
+ lines.push(" - SERVER_HOST=0.0.0.0");
113
+ lines.push(" - PORT=7433");
114
+ lines.push(" - SERVER_PORT=7433");
115
+ lines.push(" - EMBEDDING_PROVIDER=local");
116
+ // The browser reaches agent-fs through the API proxy, on the tunnel URL when there is one.
117
+ lines.push(`${ENV_AGENT_FS_PUBLIC_URL_PREFIX}${apiPort}}/agentfs`);
118
+ lines.push(` - AGENT_FS_APP_URL=${APP_URL}`);
119
+ lines.push("");
120
+ lines.push(" volumes:");
121
+ lines.push(" - agent_fs_data:/data");
122
+ lines.push("");
123
+ lines.push(" healthcheck:");
124
+ lines.push(" # The agent-fs image has no curl or wget.");
125
+ lines.push(
126
+ ` test: ["CMD-SHELL", "node -e \\"fetch('http://127.0.0.1:7433/health').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\\""]`,
127
+ );
128
+ lines.push(" interval: 10s");
129
+ lines.push(" timeout: 5s");
130
+ lines.push(" retries: 30");
131
+ lines.push(" start_period: 10s");
132
+ lines.push("");
133
+ lines.push(" restart: unless-stopped");
134
+
135
+ lines.push("");
136
+ lines.push(" tei:");
137
+ lines.push(` image: "${TEI_IMAGE_EXPRESSION}"`);
138
+ lines.push(" container_name: swarm-tei");
139
+ lines.push(` pull_policy: ${state.pullPolicy}`);
140
+ // --max-batch-tokens 2048: the default warmup passed 5.4 GB and was OOM-killed on an 8 GB box.
141
+ // --max-client-batch-size 256: the default 32 rejects documents with more than 32 chunks.
142
+ lines.push(
143
+ ` command: ["--model-id", "${TEI_MODEL}", "--max-batch-tokens", "2048", "--max-client-batch-size", "256"]`,
144
+ );
145
+ lines.push(" mem_limit: 2g");
146
+ lines.push("");
147
+ lines.push(" volumes:");
148
+ lines.push(" - tei_data:/data");
149
+ lines.push("");
150
+ lines.push(" healthcheck:");
151
+ lines.push(' test: ["CMD-SHELL", "curl -sf http://127.0.0.1:80/health || exit 1"]');
152
+ lines.push(" interval: 10s");
153
+ lines.push(" timeout: 5s");
154
+ lines.push(" retries: 30");
155
+ lines.push(" start_period: 180s");
156
+ lines.push("");
157
+ lines.push(" restart: unless-stopped");
158
+ }
159
+
74
160
  function appendProviderEnvironment(lines: string[], state: OnboardState, includeHarness: boolean) {
75
161
  if (includeHarness) lines.push(ENV_HARNESS_PROVIDER);
76
162
  switch (state.provider) {
@@ -155,7 +241,7 @@ export function generateCompose(state: OnboardState): string {
155
241
  lines.push(" - MEMORY_DEMOTION_FLOOR=${MEMORY_DEMOTION_FLOOR-1.0}");
156
242
  appendProviderEnvironment(lines, state, false);
157
243
  lines.push(` - MCP_BASE_URL=http://localhost:${port}`);
158
- lines.push(" - APP_URL=https://app.agent-swarm.dev");
244
+ lines.push(` - APP_URL=${APP_URL}`);
159
245
 
160
246
  if (state.integrations.slack) {
161
247
  lines.push(" - SLACK_DISABLE=false");
@@ -168,6 +254,19 @@ export function generateCompose(state: OnboardState): string {
168
254
  lines.push(" - GITHUB_DISABLE=false");
169
255
  }
170
256
 
257
+ if (state.bundleAgentFs) {
258
+ appendBundleApiEnvironment(lines);
259
+ lines.push("");
260
+ // The boot seeder provisions agent-fs, so it must be up first. TEI only needs to
261
+ // start: a first boot downloads 1.1 GB of weights, and memory falls back to
262
+ // keyword search until it is ready.
263
+ lines.push(" depends_on:");
264
+ lines.push(" agent-fs:");
265
+ lines.push(" condition: service_healthy");
266
+ lines.push(" tei:");
267
+ lines.push(" condition: service_started");
268
+ }
269
+
171
270
  lines.push("");
172
271
  lines.push(" ports:");
173
272
  lines.push(` - "${port}:3013"`);
@@ -238,14 +337,22 @@ export function generateCompose(state: OnboardState): string {
238
337
  lines.push(ENV_SENTRY_ORG);
239
338
  }
240
339
 
340
+ if (state.bundleAgentFs) {
341
+ lines.push(` - AGENT_FS_API_URL=${AGENT_FS_INTERNAL_URL}`);
342
+ lines.push(` - APP_URL=${APP_URL}`);
343
+ lines.push(" - AGENT_FS_LINK_TARGET=dashboard");
344
+ }
345
+
241
346
  lines.push("");
242
347
  lines.push(" ports:");
243
348
  lines.push(` - "${port}:3000"`);
244
349
  lines.push("");
245
350
  lines.push(" volumes:");
246
- lines.push(" - swarm_logs:/app/logs");
247
- lines.push(" - swarm_shared:/app/shared");
248
- lines.push(` - swarm_${svc.sanitizedName}:/app/agent`);
351
+ // Same targets as docker-compose.example.yml: the worker logs to /logs and
352
+ // keeps its clones and files under /workspace.
353
+ lines.push(" - swarm_logs:/logs");
354
+ lines.push(" - swarm_shared:/workspace/shared");
355
+ lines.push(` - swarm_${svc.sanitizedName}:/workspace/personal`);
249
356
  if (state.provider === "bedrock" && state.awsProfile) {
250
357
  lines.push(engine === "podman" ? AWS_PROFILE_VOLUME_SELINUX : AWS_PROFILE_VOLUME);
251
358
  }
@@ -253,6 +360,8 @@ export function generateCompose(state: OnboardState): string {
253
360
  lines.push(" restart: unless-stopped");
254
361
  }
255
362
 
363
+ if (state.bundleAgentFs) appendBundleServices(lines, state, port);
364
+
256
365
  // ── Volumes section ──
257
366
  lines.push("");
258
367
  lines.push("volumes:");
@@ -262,6 +371,10 @@ export function generateCompose(state: OnboardState): string {
262
371
  for (const svc of expanded) {
263
372
  lines.push(` swarm_${svc.sanitizedName}:`);
264
373
  }
374
+ if (state.bundleAgentFs) {
375
+ lines.push(" agent_fs_data:");
376
+ lines.push(" tei_data:");
377
+ }
265
378
 
266
379
  return `${lines.join("\n")}\n`;
267
380
  }
@@ -43,6 +43,15 @@ export function generateEnv(state: OnboardState): string {
43
43
  lines.push("MEMORY_RATERS=implicit-citation,explicit-self");
44
44
  lines.push("MEMORY_DEMOTION_FLOOR=1.0");
45
45
 
46
+ if (state.bundleAgentFs) {
47
+ lines.push("");
48
+ lines.push("# Bundled agent-fs (local disk) and TEI (memory embeddings); settings are in");
49
+ lines.push(
50
+ "# docker-compose.yml. Generate without them: agent-swarm onboard --yes --no-agent-fs",
51
+ );
52
+ if (state.teiImage) lines.push(`TEI_IMAGE=${state.teiImage}`);
53
+ }
54
+
46
55
  // ── Authentication ──
47
56
  lines.push("");
48
57
  lines.push("# === Authentication ===");
@@ -0,0 +1,12 @@
1
+ /** TEI image for the bundled embeddings server on arm64 hosts. */
2
+ export const TEI_ARM64_IMAGE = "ghcr.io/huggingface/text-embeddings-inference:cpu-arm64-1.9.4";
3
+
4
+ /**
5
+ * TEI publishes one tag per CPU architecture, so Compose `platform:` cannot
6
+ * pick it. `install.sh` runs onboard on the Docker host, so the host arch is
7
+ * the engine arch. Returns `null` for amd64 and anything else: the compose
8
+ * default (`cpu-1.9.4`, amd64) applies.
9
+ */
10
+ export function teiImageForArch(arch: string): string | null {
11
+ return arch === "arm64" || arch === "aarch64" ? TEI_ARM64_IMAGE : null;
12
+ }
@@ -95,6 +95,10 @@ export interface OnboardState {
95
95
  containerEngine: ContainerEnginePreference;
96
96
  outputDir: string;
97
97
  nonInteractive: boolean;
98
+ /** Bundle agent-fs (local disk) and TEI. Only `--yes` without `--no-agent-fs` sets it. */
99
+ bundleAgentFs: boolean;
100
+ /** TEI image override for the host arch; `null` keeps the compose default (amd64). */
101
+ teiImage: string | null;
98
102
  error: string | null;
99
103
  logs: LogLine[];
100
104
  }
@@ -114,6 +118,7 @@ export interface OnboardProps {
114
118
  maxConcurrentTasks?: string;
115
119
  pullPolicy?: string;
116
120
  containerEngine?: string;
121
+ noAgentFs?: boolean;
117
122
  }
118
123
 
119
124
  export type PullPolicy = "always" | "missing" | "never";
@@ -212,6 +217,8 @@ export const INITIAL_STATE: OnboardState = {
212
217
  containerEngine: "auto",
213
218
  outputDir: process.cwd(),
214
219
  nonInteractive: false,
220
+ bundleAgentFs: false,
221
+ teiImage: null,
215
222
  error: null,
216
223
  logs: [],
217
224
  };
@@ -1,4 +1,5 @@
1
1
  import crypto from "node:crypto";
2
+ import os from "node:os";
2
3
  import { Select } from "@inkjs/ui";
3
4
  import { Box, Text, useApp, useInput } from "ink";
4
5
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -35,6 +36,7 @@ import { PostTaskStep } from "./onboard/steps/post-task.tsx";
35
36
  import { PrereqCheckStep } from "./onboard/steps/prereq-check.tsx";
36
37
  import { ReviewStep } from "./onboard/steps/review.tsx";
37
38
  import { StartStep } from "./onboard/steps/start.tsx";
39
+ import { teiImageForArch } from "./onboard/tei-image.ts";
38
40
  import {
39
41
  getStepProgress,
40
42
  INITIAL_STATE,
@@ -61,6 +63,7 @@ export function Onboard({
61
63
  maxConcurrentTasks,
62
64
  pullPolicy,
63
65
  containerEngine,
66
+ noAgentFs = false,
64
67
  }: OnboardProps) {
65
68
  const { exit } = useApp();
66
69
  const [state, setState] = useState<OnboardState>(() => {
@@ -229,8 +232,15 @@ export function Onboard({
229
232
  addLog(`Integrations: ${enabled.join(", ")}`);
230
233
  }
231
234
 
235
+ const bundleAgentFs = !noAgentFs;
236
+ if (bundleAgentFs) {
237
+ addLog("Bundling agent-fs and TEI (local files and memory; --no-agent-fs to skip)");
238
+ }
239
+
232
240
  goToStep("generate", {
233
241
  nonInteractive: true,
242
+ bundleAgentFs,
243
+ teiImage: bundleAgentFs ? teiImageForArch(os.arch()) : null,
234
244
  deployType: "local",
235
245
  presetId: selectedPreset.id,
236
246
  services: selectedPreset.services,
@@ -250,7 +260,7 @@ export function Onboard({
250
260
  sentryToken: process.env.SENTRY_AUTH_TOKEN || "",
251
261
  sentryOrg: process.env.SENTRY_ORG || "",
252
262
  });
253
- }, [yes, preset, state.step, addLog, goToStep, goToError]);
263
+ }, [yes, preset, noAgentFs, state.step, addLog, goToStep, goToError]);
254
264
 
255
265
  // Interactive welcome: auto-advance after detecting existing setup
256
266
  useEffect(() => {
@@ -49,6 +49,7 @@ import {
49
49
  CREDENTIAL_POOL_VARS,
50
50
  splitCredentialPool,
51
51
  } from "../utils/credentials";
52
+ import { isEnvFlagEnabled } from "../utils/env-flag";
52
53
  import {
53
54
  checkAnthropicApiKey,
54
55
  checkDeepSeekApiKey,
@@ -664,7 +665,7 @@ export async function validateProviderCredentials(
664
665
  * the dashboard surfaces "unreported".
665
666
  */
666
667
  export function isCredCheckDisabled(env: NodeJS.ProcessEnv): boolean {
667
- return env.CRED_CHECK_DISABLE === "1";
668
+ return isEnvFlagEnabled("CRED_CHECK_DISABLE", false, env);
668
669
  }
669
670
 
670
671
  /**