@desplega.ai/agent-swarm 1.173.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
  7. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
  8. package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
  15. package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
  16. package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
  17. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  18. package/dist/cli-2m1ktfzz.js +17795 -0
  19. package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
  20. package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
  21. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  22. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  23. package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
  24. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  25. package/dist/cli-6cvfxs8r.js +47 -0
  26. package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
  27. package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
  28. package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
  29. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  30. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  31. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  32. package/dist/cli-9traj3dn.js +638 -0
  33. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  34. package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
  35. package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
  36. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  37. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  38. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  39. package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
  40. package/dist/cli-e3g5mbkz.js +776 -0
  41. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  42. package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
  43. package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
  44. package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
  45. package/dist/cli-f527ce74.js +781 -0
  46. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  47. package/dist/cli-hjwmhcg1.js +10622 -0
  48. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  49. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  50. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  51. package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
  52. package/dist/cli-jta2eyzc.js +619 -0
  53. package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
  54. package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
  55. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  56. package/dist/cli-kceah1eh.js +92 -0
  57. package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
  58. package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
  59. package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
  60. package/dist/cli-n2wq4j7m.js +6985 -0
  61. package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
  62. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  63. package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
  64. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  65. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  66. package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
  67. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  68. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  69. package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
  70. package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
  71. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  72. package/dist/cli-rhs9pz33.js +294 -0
  73. package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
  74. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  75. package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
  76. package/dist/cli-skxvvpap.js +10140 -0
  77. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  78. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  79. package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
  80. package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
  81. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  82. package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
  83. package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
  84. package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
  85. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  86. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  87. package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
  88. package/dist/cli-wmgrkscj.js +23254 -0
  89. package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
  90. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  91. package/dist/cli.js +29 -45
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  99. package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
  100. package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
  117. package/dist/index-52j1aja4.js +3465 -0
  118. package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
  119. package/dist/index-9gw58n4k.js +193 -0
  120. package/dist/index-9j87mcm8.js +56305 -0
  121. package/dist/index-bsmcresk.js +79424 -0
  122. package/dist/index-mh85qhec.js +475 -0
  123. package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
  124. package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
  125. package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
  126. package/dist/index-we27kd31.js +12810 -0
  127. package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  148. package/openapi.json +523 -344
  149. package/package.json +10 -11
  150. package/src/be/config-refs.ts +60 -0
  151. package/src/be/db/page-versions.ts +15 -0
  152. package/src/be/db/runtime.ts +35 -8
  153. package/src/be/db/skill-name-resolution.ts +36 -0
  154. package/src/be/db/tasks/read.ts +230 -0
  155. package/src/be/db/tasks/write.ts +7 -0
  156. package/src/be/db.ts +52 -7
  157. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  158. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  159. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  160. package/src/be/model-catalog-store.ts +5 -1
  161. package/src/be/model-tier-resolution.ts +12 -1
  162. package/src/be/model-validation.ts +5 -2
  163. package/src/be/models-catalog.ts +26 -1
  164. package/src/be/modelsdev-cache.json +198 -63
  165. package/src/be/modelsdev-cache.ts +9 -1
  166. package/src/be/script-credential-broker.ts +39 -2
  167. package/src/be/scripts/run-saved.ts +1 -0
  168. package/src/be/scripts/typecheck.ts +10 -8
  169. package/src/be/seed/runner.ts +13 -1
  170. package/src/be/seed/state-db.ts +5 -0
  171. package/src/be/seed/types.ts +6 -0
  172. package/src/be/seed-pricing.ts +19 -2
  173. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  174. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  175. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  176. package/src/be/seed-skills/index.ts +104 -3
  177. package/src/be/swarm-config-guard.ts +52 -0
  178. package/src/cli.tsx +0 -16
  179. package/src/commands/onboard/non-interactive.ts +4 -2
  180. package/src/commands/provider-credentials.ts +52 -6
  181. package/src/commands/runner.ts +190 -54
  182. package/src/e2b/env.ts +5 -0
  183. package/src/extensions/contract-types.generated.ts +1 -1
  184. package/src/extensions/contract.ts +45 -0
  185. package/src/extensions/dispatcher.ts +7 -0
  186. package/src/extensions/post-bridge.ts +40 -0
  187. package/src/github/handlers.ts +11 -4
  188. package/src/heartbeat/templates.ts +8 -5
  189. package/src/hooks/hook.ts +0 -26
  190. package/src/http/all-routes.ts +0 -1
  191. package/src/http/config.ts +36 -17
  192. package/src/http/core.ts +4 -0
  193. package/src/http/index.ts +14 -2
  194. package/src/http/integrations.ts +88 -1
  195. package/src/http/mcp.ts +15 -5
  196. package/src/http/page-proxy.ts +2 -4
  197. package/src/http/pages-public.ts +3 -3
  198. package/src/http/pages.ts +3 -13
  199. package/src/http/poll.ts +28 -68
  200. package/src/http/requested-by.ts +54 -0
  201. package/src/http/scripts.ts +1 -0
  202. package/src/http/stats.ts +1 -30
  203. package/src/http/tasks.ts +61 -5
  204. package/src/http/utils.ts +14 -0
  205. package/src/http/webhooks.ts +159 -1
  206. package/src/http/x.ts +1 -0
  207. package/src/integrations/chat-sdk/adapters.ts +129 -0
  208. package/src/integrations/chat-sdk/attachments.ts +232 -0
  209. package/src/integrations/chat-sdk/background.ts +36 -0
  210. package/src/integrations/chat-sdk/commands.ts +194 -0
  211. package/src/integrations/chat-sdk/config.ts +198 -0
  212. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  213. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  214. package/src/integrations/chat-sdk/handlers.ts +28 -0
  215. package/src/integrations/chat-sdk/inbound.ts +426 -0
  216. package/src/integrations/chat-sdk/limits.ts +48 -0
  217. package/src/integrations/chat-sdk/logger.ts +81 -0
  218. package/src/integrations/chat-sdk/outbound.ts +123 -0
  219. package/src/integrations/chat-sdk/relay.ts +172 -0
  220. package/src/integrations/chat-sdk/router.ts +169 -0
  221. package/src/integrations/chat-sdk/runtime.ts +351 -0
  222. package/src/integrations/chat-sdk/schema.ts +245 -0
  223. package/src/integrations/chat-sdk/state.ts +243 -0
  224. package/src/integrations/chat-sdk/status.ts +244 -0
  225. package/src/integrations/chat-sdk/steering.ts +58 -0
  226. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  227. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  228. package/src/integrations/chat-sdk/templates.ts +16 -0
  229. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  230. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  231. package/src/integrations/chat-sdk/typing.ts +65 -0
  232. package/src/pages/version.ts +4 -5
  233. package/src/prompts/base-prompt.ts +111 -45
  234. package/src/providers/acp-target-catalog.ts +6 -0
  235. package/src/providers/acp-targets.ts +55 -2
  236. package/src/providers/amp-adapter.ts +1 -1
  237. package/src/providers/claude-adapter.ts +1 -1
  238. package/src/providers/claude-flag-settings.ts +17 -5
  239. package/src/providers/claude-managed-adapter.ts +1 -1
  240. package/src/providers/claude-session-events.ts +6 -1
  241. package/src/providers/dsh-adapter.ts +1 -1
  242. package/src/providers/opencode-adapter.ts +243 -26
  243. package/src/providers/opencode-swarm-plugin.ts +31 -0
  244. package/src/providers/pi-mono-adapter.ts +398 -153
  245. package/src/providers/pricing-sources.md +33 -2
  246. package/src/providers/reasoning-effort.ts +27 -0
  247. package/src/providers/types.ts +6 -2
  248. package/src/rbac/legacy-policy.ts +8 -2
  249. package/src/rbac/permissions.ts +21 -0
  250. package/src/scheduler/scheduler.ts +4 -2
  251. package/src/scripts-runtime/loader.ts +19 -7
  252. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  253. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  254. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  255. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  256. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  257. package/src/server.ts +52 -18
  258. package/src/slack/event-dedup.ts +1 -1
  259. package/src/tasks/additive-buffer.ts +4 -2
  260. package/src/tasks/context-key.ts +60 -1
  261. package/src/tasks/model-pin.ts +67 -0
  262. package/src/tasks/worker-follow-up.ts +4 -31
  263. package/src/tests/acp-adapter.test.ts +42 -0
  264. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  265. package/src/tests/base-prompt.test.ts +58 -54
  266. package/src/tests/chat-sdk-commands.test.ts +191 -0
  267. package/src/tests/chat-sdk-config.test.ts +334 -0
  268. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  269. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  270. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  271. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  272. package/src/tests/chat-sdk-harness.ts +146 -0
  273. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  274. package/src/tests/chat-sdk-relay.test.ts +470 -0
  275. package/src/tests/chat-sdk-reload.test.ts +128 -0
  276. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  277. package/src/tests/chat-sdk-slack.test.ts +268 -0
  278. package/src/tests/chat-sdk-state.test.ts +163 -0
  279. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  280. package/src/tests/chat-sdk-teams.test.ts +277 -0
  281. package/src/tests/chat-sdk-tools.test.ts +520 -0
  282. package/src/tests/chat-sdk-typing.test.ts +120 -0
  283. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  284. package/src/tests/claude-adapter-binary.test.ts +9 -4
  285. package/src/tests/claude-flag-settings.test.ts +11 -3
  286. package/src/tests/claude-managed-adapter.test.ts +1 -1
  287. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  288. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  289. package/src/tests/claude-worker-parity.test.ts +2 -7
  290. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  291. package/src/tests/config-refs.test.ts +66 -0
  292. package/src/tests/context-key.test.ts +19 -0
  293. package/src/tests/cors-allowlist.test.ts +25 -0
  294. package/src/tests/create-page-tool.test.ts +18 -0
  295. package/src/tests/credential-check.test.ts +139 -0
  296. package/src/tests/credentials.test.ts +21 -0
  297. package/src/tests/db-runtime.test.ts +61 -0
  298. package/src/tests/db-task-read-contract.test.ts +20 -0
  299. package/src/tests/db-task-write-contract.test.ts +10 -0
  300. package/src/tests/docs-index-footprint.test.js +3 -3
  301. package/src/tests/dsh-adapter.test.ts +1 -1
  302. package/src/tests/enabled-capabilities.test.ts +20 -0
  303. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  304. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  305. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  306. package/src/tests/explicit-model-validation.test.ts +105 -1
  307. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  308. package/src/tests/extensions-post-bridge.test.ts +51 -0
  309. package/src/tests/fetch-resolved-env.test.ts +128 -0
  310. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  311. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  312. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  313. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  314. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  315. package/src/tests/github-handlers.test.ts +35 -0
  316. package/src/tests/harness-provider-resolution.test.ts +12 -0
  317. package/src/tests/heartbeat-checklist.test.ts +3 -2
  318. package/src/tests/hook-runtime-close.test.ts +0 -2
  319. package/src/tests/http-api-integration.test.ts +0 -25
  320. package/src/tests/migration-046-budgets.test.ts +17 -16
  321. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  322. package/src/tests/model-tier-resolution.test.ts +93 -0
  323. package/src/tests/models-catalog.test.ts +42 -1
  324. package/src/tests/openai-compatible-providers.test.ts +134 -0
  325. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  326. package/src/tests/opencode-adapter.test.ts +216 -2
  327. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  328. package/src/tests/opencode-shell-env.test.ts +145 -0
  329. package/src/tests/openrouter-base-url.test.ts +101 -151
  330. package/src/tests/page-proxy.test.ts +1 -1
  331. package/src/tests/pages-browser-sdk.test.ts +76 -0
  332. package/src/tests/pages-feedback.test.ts +1 -1
  333. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  334. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  335. package/src/tests/pi-mono-adapter.test.ts +385 -5
  336. package/src/tests/pi-shell-env.test.ts +170 -0
  337. package/src/tests/prompt-template-remaining.test.ts +35 -0
  338. package/src/tests/rbac-engine.test.ts +11 -3
  339. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  340. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  341. package/src/tests/script-connections.test.ts +2 -0
  342. package/src/tests/scripts-http.test.ts +201 -1
  343. package/src/tests/scripts-only-gating.test.ts +2 -3
  344. package/src/tests/scripts-runtime.test.ts +58 -40
  345. package/src/tests/secret-scrubber.test.ts +86 -0
  346. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  347. package/src/tests/seed-skills-retire.test.ts +182 -0
  348. package/src/tests/send-task-requester-gate.test.ts +58 -0
  349. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  350. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  351. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  352. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  353. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  354. package/src/tests/swarm-diff.test.ts +1 -1
  355. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  356. package/src/tests/task-session-prompts.test.ts +132 -0
  357. package/src/tests/task-tool-manifest.test.ts +14 -0
  358. package/src/tests/task-tools-ctx.test.ts +2 -0
  359. package/src/tests/tool-annotations.test.ts +6 -6
  360. package/src/tests/ui-logs-parser.test.ts +43 -0
  361. package/src/tests/used-by-route.test.ts +6 -0
  362. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  363. package/src/tests/workflow-swarm-script.test.ts +38 -0
  364. package/src/tests/workflow-wait-filter.test.ts +5 -3
  365. package/src/tools/chat-access.ts +41 -0
  366. package/src/tools/chat-open-dm.ts +59 -0
  367. package/src/tools/chat-post.ts +74 -0
  368. package/src/tools/chat-read-thread.ts +100 -0
  369. package/src/tools/chat-reply.ts +75 -0
  370. package/src/tools/create-page.ts +3 -13
  371. package/src/tools/model-catalog/index.ts +2 -1
  372. package/src/tools/register-chat-channel.ts +139 -0
  373. package/src/tools/send-task.ts +24 -0
  374. package/src/tools/skills/skill-get.ts +17 -9
  375. package/src/tools/swarm-config/get-config.ts +2 -1
  376. package/src/tools/swarm-config/list-config.ts +2 -1
  377. package/src/tools/swarm-config/set-config.ts +16 -3
  378. package/src/tools/tool-config.ts +8 -6
  379. package/src/types.ts +12 -2
  380. package/src/utils/config-env-export.ts +74 -0
  381. package/src/utils/credentials.test.ts +92 -0
  382. package/src/utils/credentials.ts +52 -4
  383. package/src/utils/harness-provider.ts +15 -6
  384. package/src/utils/runtime-model-catalog.ts +11 -1
  385. package/src/utils/secret-scrubber.ts +71 -2
  386. package/src/utils/task-tool-manifest.ts +19 -2
  387. package/src/workflows/executors/swarm-script.ts +1 -0
  388. package/templates/skills/agent-browser/SKILL.md +1 -1
  389. package/templates/skills/agent-browser/content.md +1 -1
  390. package/templates/skills/apps/SKILL.md +8 -1
  391. package/templates/skills/apps/content.md +8 -1
  392. package/templates/skills/chat-sdk/config.json +14 -0
  393. package/templates/skills/chat-sdk/content.md +84 -0
  394. package/templates/skills/composio-gmail/content.md +7 -0
  395. package/templates/skills/composio-google-docs/content.md +1 -1
  396. package/templates/skills/email-domain-health/config.json +14 -0
  397. package/templates/skills/email-domain-health/content.md +131 -0
  398. package/templates/skills/kv-storage/content.md +2 -2
  399. package/templates/skills/pages/content.md +8 -10
  400. package/templates/skills/swarm-extensions/content.md +2 -0
  401. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  402. package/templates/skills/swarm-scripts/config.json +1 -1
  403. package/templates/skills/swarm-scripts/content.md +4 -2
  404. package/templates/skills/workflow-iterate/content.md +38 -0
  405. package/dist/artifact-eczsknc3.js +0 -3843
  406. package/dist/cli-bd957dgb.js +0 -153
  407. package/dist/cli-q51yprfq.js +0 -1104
  408. package/dist/cli-t1ze64h3.js +0 -811
  409. package/dist/cli-tcc4wd5d.js +0 -56471
  410. package/dist/index-9ch1n761.js +0 -1102
  411. package/src/artifact-sdk/index.ts +0 -2
  412. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  413. package/src/artifact-sdk/port.ts +0 -12
  414. package/src/artifact-sdk/server.ts +0 -183
  415. package/src/artifact-sdk/tunnel.ts +0 -19
  416. package/src/commands/artifact.ts +0 -249
  417. package/src/http/ecosystem.ts +0 -77
  418. package/src/tests/artifact-commands.test.ts +0 -92
  419. package/src/tests/artifact-sdk.test.ts +0 -793
  420. package/src/tests/register-service-security.test.ts +0 -194
  421. package/src/tools/list-services.ts +0 -107
  422. package/src/tools/register-service.ts +0 -177
  423. package/src/tools/unregister-service.ts +0 -73
  424. package/src/tools/update-service-status.ts +0 -96
  425. package/templates/skills/artifacts/config.json +0 -14
  426. package/templates/skills/artifacts/content.md +0 -344
  427. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  428. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  429. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  430. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  431. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  432. /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
@@ -1,793 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
2
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { BROWSER_SDK_JS } from "../artifact-sdk/browser-sdk";
4
- import { getAvailablePort } from "../artifact-sdk/port";
5
- import {
6
- createArtifactServer,
7
- createBunHonoFetchHandler,
8
- createBunResponse,
9
- } from "../artifact-sdk/server";
10
- import { getBasePrompt } from "../prompts/base-prompt";
11
-
12
- function restoreEnvValue(key: string, value: string | undefined): void {
13
- if (value === undefined) delete process.env[key];
14
- else process.env[key] = value;
15
- }
16
-
17
- // ─── Port allocation tests ──────────────────────────────────────────────
18
-
19
- describe("getAvailablePort", () => {
20
- test("returns a valid port number", async () => {
21
- const port = await getAvailablePort();
22
- expect(port).toBeGreaterThan(0);
23
- expect(port).toBeLessThan(65536);
24
- });
25
-
26
- test("returns different ports on consecutive calls", async () => {
27
- const port1 = await getAvailablePort();
28
- const port2 = await getAvailablePort();
29
- expect(port1).not.toBe(port2);
30
- });
31
-
32
- test("returns a port that is actually available", async () => {
33
- const port = await getAvailablePort();
34
- // Try to start a Bun server on the port — should succeed
35
- const server = Bun.serve({
36
- port,
37
- fetch: () => createBunResponse("ok"),
38
- });
39
- expect(server.port).toBe(port);
40
- server.stop();
41
- });
42
-
43
- test("returns ports in ephemeral range", async () => {
44
- const ports: number[] = [];
45
- for (let i = 0; i < 5; i++) {
46
- ports.push(await getAvailablePort());
47
- }
48
- // All ports should be in the ephemeral range (typically > 1024)
49
- for (const p of ports) {
50
- expect(p).toBeGreaterThan(1024);
51
- }
52
- });
53
-
54
- test("all returned ports are unique", async () => {
55
- const ports = new Set<number>();
56
- for (let i = 0; i < 10; i++) {
57
- ports.add(await getAvailablePort());
58
- }
59
- // Allow minor collisions — OS can recycle ports between bind/release cycles
60
- expect(ports.size).toBeGreaterThanOrEqual(8);
61
- });
62
- });
63
-
64
- // ─── Browser SDK tests ─────────────────────────────────────────────────
65
-
66
- describe("BROWSER_SDK_JS", () => {
67
- test("is a non-empty string", () => {
68
- expect(typeof BROWSER_SDK_JS).toBe("string");
69
- expect(BROWSER_SDK_JS.length).toBeGreaterThan(100);
70
- });
71
-
72
- test("contains SwarmSDK class", () => {
73
- expect(BROWSER_SDK_JS).toContain("class SwarmSDK");
74
- });
75
-
76
- test("is syntactically valid JavaScript", () => {
77
- expect(() => new Function(BROWSER_SDK_JS)).not.toThrow();
78
- });
79
-
80
- test("exposes the eight canonical domains", () => {
81
- const expectedDomains = [
82
- "this.tasks",
83
- "this.agents",
84
- "this.events",
85
- "this.memory",
86
- "this.repos",
87
- "this.schedules",
88
- "this.approvalRequests",
89
- "this.assets",
90
- ];
91
- for (const domain of expectedDomains) {
92
- expect(BROWSER_SDK_JS).toContain(domain);
93
- }
94
- });
95
-
96
- test("removed domains are NOT exposed (messages, services, slack)", () => {
97
- const removed = ["this.messages", "this.services", "this.slack", "postMessage", "readMessages"];
98
- for (const r of removed) {
99
- expect(BROWSER_SDK_JS).not.toContain(r);
100
- }
101
- });
102
-
103
- test("assigns SwarmSDK class + swarmSdk singleton to window", () => {
104
- expect(BROWSER_SDK_JS).toContain("window.SwarmSDK = SwarmSDK");
105
- expect(BROWSER_SDK_JS).toContain("window.swarmSdk = new SwarmSDK()");
106
- });
107
-
108
- test("routes calls through the /@swarm/api/* proxy", () => {
109
- expect(BROWSER_SDK_JS).toContain("const base = '/@swarm/api'");
110
- // Sentinel endpoints — one per domain.
111
- expect(BROWSER_SDK_JS).toContain("'/tasks'");
112
- expect(BROWSER_SDK_JS).toContain("'/agents'");
113
- expect(BROWSER_SDK_JS).toContain("'/events'");
114
- expect(BROWSER_SDK_JS).toContain("'/memory/search'");
115
- expect(BROWSER_SDK_JS).toContain("'/repos'");
116
- expect(BROWSER_SDK_JS).toContain("'/schedules'");
117
- expect(BROWSER_SDK_JS).toContain("'/approval-requests'");
118
- expect(BROWSER_SDK_JS).toContain("'/assets'");
119
- expect(BROWSER_SDK_JS).toContain("'/assets/key-audit'");
120
- expect(BROWSER_SDK_JS).toContain("'/assets/mappings'");
121
- });
122
-
123
- test("does not fetch on SDK load or construction", () => {
124
- const fetchMock = mock(() => Promise.resolve(Response.json({})));
125
- const window = {};
126
- const loadSdk = new Function("window", "fetch", `${BROWSER_SDK_JS}\nreturn window.SwarmSDK;`);
127
-
128
- const SwarmSDK = loadSdk(window, fetchMock);
129
- expect(window).toHaveProperty("swarmSdk");
130
- expect(fetchMock).not.toHaveBeenCalled();
131
-
132
- new SwarmSDK();
133
- new SwarmSDK();
134
- expect(fetchMock).not.toHaveBeenCalled();
135
- });
136
- });
137
-
138
- // ─── createArtifactServer factory tests ──────────────────────────────────
139
-
140
- describe("createArtifactServer", () => {
141
- const originalEnv = { ...process.env };
142
-
143
- beforeAll(() => {
144
- process.env.AGENT_ID = "test-agent-id";
145
- process.env.API_KEY = "example-test-api-key";
146
- process.env.MCP_BASE_URL = "http://localhost:19999"; // Intentionally unreachable
147
- });
148
-
149
- afterAll(() => {
150
- restoreEnvValue("AGENT_ID", originalEnv.AGENT_ID);
151
- restoreEnvValue("API_KEY", originalEnv.API_KEY);
152
- restoreEnvValue("MCP_BASE_URL", originalEnv.MCP_BASE_URL);
153
- });
154
-
155
- describe("factory return shape", () => {
156
- test("returns object with expected properties", () => {
157
- const server = createArtifactServer({ name: "test" });
158
- expect(server).toHaveProperty("start");
159
- expect(server).toHaveProperty("stop");
160
- expect(server).toHaveProperty("url");
161
- expect(server).toHaveProperty("port");
162
- expect(server).toHaveProperty("tunnel");
163
- expect(typeof server.start).toBe("function");
164
- expect(typeof server.stop).toBe("function");
165
- });
166
-
167
- test("initial url is empty string", () => {
168
- const server = createArtifactServer({ name: "test" });
169
- expect(server.url).toBe("");
170
- });
171
-
172
- test("initial port is 0", () => {
173
- const server = createArtifactServer({ name: "test" });
174
- expect(server.port).toBe(0);
175
- });
176
-
177
- test("initial tunnel is null", () => {
178
- const server = createArtifactServer({ name: "test" });
179
- expect(server.tunnel).toBeNull();
180
- });
181
- });
182
-
183
- describe("Hono app routes", () => {
184
- test("server serves static content via Hono", async () => {
185
- const { Hono } = await import("hono");
186
- const port = await getAvailablePort();
187
-
188
- const app = new Hono();
189
- app.get("/", (c) => c.text("Hello from test artifact"));
190
-
191
- // Create a minimal Hono server without tunnel
192
- const honoApp = new Hono();
193
- honoApp.get("/@swarm/sdk.js", (c) => {
194
- c.header("Content-Type", "application/javascript");
195
- return c.body(BROWSER_SDK_JS);
196
- });
197
- honoApp.get("/@swarm/config", (c) => {
198
- return c.json({ agentId: "test-agent-id", artifactName: "test" });
199
- });
200
- honoApp.route("/", app);
201
-
202
- const server = Bun.serve({ port, fetch: createBunHonoFetchHandler(honoApp) });
203
-
204
- try {
205
- // Test content serving
206
- const res = await fetch(`http://localhost:${port}/`);
207
- expect(res.status).toBe(200);
208
- expect(await res.text()).toBe("Hello from test artifact");
209
-
210
- // Test SDK endpoint
211
- const sdkRes = await fetch(`http://localhost:${port}/@swarm/sdk.js`);
212
- expect(sdkRes.status).toBe(200);
213
- expect(sdkRes.headers.get("content-type")).toContain("javascript");
214
- const sdkBody = await sdkRes.text();
215
- expect(sdkBody).toContain("class SwarmSDK");
216
-
217
- // Test config endpoint
218
- const configRes = await fetch(`http://localhost:${port}/@swarm/config`);
219
- expect(configRes.status).toBe(200);
220
- const config = await configRes.json();
221
- expect(config).toEqual({ agentId: "test-agent-id", artifactName: "test" });
222
- } finally {
223
- server.stop();
224
- }
225
- });
226
-
227
- test("server serves static files from directory", async () => {
228
- const { Hono } = await import("hono");
229
- const { serveStatic } = await import("hono/bun");
230
- const port = await getAvailablePort();
231
- const testDir = "/tmp/test-artifact-static";
232
-
233
- // Create test directory with content
234
- mkdirSync(testDir, { recursive: true });
235
- writeFileSync(`${testDir}/index.html`, "<h1>Test Static</h1>");
236
- writeFileSync(`${testDir}/data.json`, '{"test": true}');
237
-
238
- const app = new Hono();
239
- app.use("/*", serveStatic({ root: testDir }));
240
-
241
- const server = Bun.serve({ port, fetch: createBunHonoFetchHandler(app) });
242
-
243
- try {
244
- const res = await fetch(`http://localhost:${port}/index.html`);
245
- expect(res.status).toBe(200);
246
- expect(await res.text()).toBe("<h1>Test Static</h1>");
247
-
248
- const jsonRes = await fetch(`http://localhost:${port}/data.json`);
249
- expect(jsonRes.status).toBe(200);
250
- const data = await jsonRes.json();
251
- expect(data).toEqual({ test: true });
252
- } finally {
253
- server.stop();
254
- rmSync(testDir, { recursive: true, force: true });
255
- }
256
- });
257
-
258
- test("API proxy returns 502 when MCP server is unreachable", async () => {
259
- const { Hono } = await import("hono");
260
- const port = await getAvailablePort();
261
-
262
- const app = new Hono();
263
- app.all("/@swarm/api/*", async (c) => {
264
- const path = c.req.path.replace("/@swarm/api", "/api");
265
- const targetUrl = `http://localhost:19999${path}`; // Unreachable
266
- try {
267
- const res = await fetch(targetUrl, { method: c.req.method });
268
- return new Response(res.body, { status: res.status });
269
- } catch (e) {
270
- return c.json({ error: "Proxy error", message: String(e) }, 502);
271
- }
272
- });
273
-
274
- const server = Bun.serve({ port, fetch: createBunHonoFetchHandler(app) });
275
-
276
- try {
277
- const res = await fetch(`http://localhost:${port}/@swarm/api/agents`);
278
- expect(res.status).toBe(502);
279
- const body = await res.json();
280
- expect(body.error).toBe("Proxy error");
281
- } finally {
282
- server.stop();
283
- }
284
- });
285
-
286
- test("CORS preflight returns 204 with correct headers", async () => {
287
- const { Hono } = await import("hono");
288
- const port = await getAvailablePort();
289
-
290
- const app = new Hono();
291
- app.options("/@swarm/api/*", (_c) => {
292
- return new Response(null, {
293
- status: 204,
294
- headers: {
295
- "Access-Control-Allow-Origin": "*",
296
- "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
297
- "Access-Control-Allow-Headers": "Content-Type",
298
- },
299
- });
300
- });
301
-
302
- const server = Bun.serve({ port, fetch: createBunHonoFetchHandler(app) });
303
-
304
- try {
305
- const res = await fetch(`http://localhost:${port}/@swarm/api/tasks`, {
306
- method: "OPTIONS",
307
- });
308
- expect(res.status).toBe(204);
309
- expect(res.headers.get("access-control-allow-origin")).toBe("*");
310
- expect(res.headers.get("access-control-allow-methods")).toContain("GET");
311
- expect(res.headers.get("access-control-allow-methods")).toContain("POST");
312
- expect(res.headers.get("access-control-allow-methods")).toContain("DELETE");
313
- expect(res.headers.get("access-control-allow-headers")).toContain("Content-Type");
314
- } finally {
315
- server.stop();
316
- }
317
- });
318
-
319
- test("API proxy forwards auth headers to MCP server", async () => {
320
- const { Hono } = await import("hono");
321
- const proxyPort = await getAvailablePort();
322
- const mockMcpPort = await getAvailablePort();
323
-
324
- // Mock MCP server that captures headers
325
- let capturedHeaders: Record<string, string> = {};
326
- const mockMcp = Bun.serve({
327
- port: mockMcpPort,
328
- fetch: (req) => {
329
- capturedHeaders = {};
330
- for (const [key, value] of req.headers.entries()) {
331
- capturedHeaders[key.toLowerCase()] = value;
332
- }
333
- return createBunResponse(JSON.stringify({ success: true }), {
334
- headers: { "Content-Type": "application/json" },
335
- });
336
- },
337
- });
338
-
339
- // Build proxy app matching server.ts pattern
340
- const app = new Hono();
341
- app.all("/@swarm/api/*", async (c) => {
342
- const path = c.req.path.replace("/@swarm/api", "/api");
343
- const targetUrl = `http://localhost:${mockMcpPort}${path}`;
344
- const headers: Record<string, string> = {
345
- Authorization: "Bearer example-test-api-key",
346
- "X-Agent-ID": "test-agent-id",
347
- };
348
- if (c.req.method !== "GET") {
349
- headers["Content-Type"] = "application/json";
350
- }
351
- try {
352
- const res = await fetch(targetUrl, { method: c.req.method, headers });
353
- return new Response(res.body, {
354
- status: res.status,
355
- headers: {
356
- "Access-Control-Allow-Origin": "*",
357
- "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
358
- "Access-Control-Allow-Headers": "Content-Type",
359
- },
360
- });
361
- } catch (e) {
362
- return c.json({ error: "Proxy error", message: String(e) }, 502);
363
- }
364
- });
365
-
366
- const proxy = Bun.serve({ port: proxyPort, fetch: createBunHonoFetchHandler(app) });
367
-
368
- try {
369
- // Test GET request headers
370
- await fetch(`http://localhost:${proxyPort}/@swarm/api/agents`);
371
- expect(capturedHeaders.authorization).toBe("Bearer example-test-api-key");
372
- expect(capturedHeaders["x-agent-id"]).toBe("test-agent-id");
373
-
374
- // GET should NOT have Content-Type
375
- expect(capturedHeaders["content-type"]).toBeUndefined();
376
-
377
- // Test POST request headers
378
- await fetch(`http://localhost:${proxyPort}/@swarm/api/tasks`, {
379
- method: "POST",
380
- body: JSON.stringify({ task: "test" }),
381
- });
382
- expect(capturedHeaders.authorization).toBe("Bearer example-test-api-key");
383
- expect(capturedHeaders["x-agent-id"]).toBe("test-agent-id");
384
- expect(capturedHeaders["content-type"]).toBe("application/json");
385
- } finally {
386
- proxy.stop();
387
- mockMcp.stop();
388
- }
389
- });
390
-
391
- test("API proxy response includes CORS headers", async () => {
392
- const { Hono } = await import("hono");
393
- const proxyPort = await getAvailablePort();
394
- const mockMcpPort = await getAvailablePort();
395
-
396
- const mockMcp = Bun.serve({
397
- port: mockMcpPort,
398
- fetch: () =>
399
- createBunResponse(JSON.stringify({ ok: true }), {
400
- headers: { "Content-Type": "application/json" },
401
- }),
402
- });
403
-
404
- const app = new Hono();
405
- app.all("/@swarm/api/*", async (c) => {
406
- const path = c.req.path.replace("/@swarm/api", "/api");
407
- const targetUrl = `http://localhost:${mockMcpPort}${path}`;
408
- try {
409
- const res = await fetch(targetUrl, { method: c.req.method });
410
- return new Response(res.body, {
411
- status: res.status,
412
- headers: {
413
- "Access-Control-Allow-Origin": "*",
414
- "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
415
- "Access-Control-Allow-Headers": "Content-Type",
416
- },
417
- });
418
- } catch (_e) {
419
- return c.json({ error: "Proxy error" }, 502);
420
- }
421
- });
422
-
423
- const proxy = Bun.serve({ port: proxyPort, fetch: createBunHonoFetchHandler(app) });
424
-
425
- try {
426
- const res = await fetch(`http://localhost:${proxyPort}/@swarm/api/agents`);
427
- expect(res.status).toBe(200);
428
- expect(res.headers.get("access-control-allow-origin")).toBe("*");
429
- } finally {
430
- proxy.stop();
431
- mockMcp.stop();
432
- }
433
- });
434
-
435
- test("API proxy correctly rewrites path from /@swarm/api to /api", async () => {
436
- const { Hono } = await import("hono");
437
- const proxyPort = await getAvailablePort();
438
- const mockMcpPort = await getAvailablePort();
439
-
440
- let capturedPath = "";
441
- const mockMcp = Bun.serve({
442
- port: mockMcpPort,
443
- fetch: (req) => {
444
- capturedPath = new URL(req.url).pathname;
445
- return createBunResponse(JSON.stringify({ ok: true }));
446
- },
447
- });
448
-
449
- const app = new Hono();
450
- app.all("/@swarm/api/*", async (c) => {
451
- const path = c.req.path.replace("/@swarm/api", "/api");
452
- const targetUrl = `http://localhost:${mockMcpPort}${path}`;
453
- try {
454
- const res = await fetch(targetUrl, { method: c.req.method });
455
- return new Response(res.body, { status: res.status });
456
- } catch (_e) {
457
- return c.json({ error: "Proxy error" }, 502);
458
- }
459
- });
460
-
461
- const proxy = Bun.serve({ port: proxyPort, fetch: createBunHonoFetchHandler(app) });
462
-
463
- try {
464
- await fetch(`http://localhost:${proxyPort}/@swarm/api/tasks/123/progress`);
465
- expect(capturedPath).toBe("/api/tasks/123/progress");
466
-
467
- await fetch(`http://localhost:${proxyPort}/@swarm/api/agents`);
468
- expect(capturedPath).toBe("/api/agents");
469
-
470
- await fetch(`http://localhost:${proxyPort}/@swarm/api/services`);
471
- expect(capturedPath).toBe("/api/services");
472
- } finally {
473
- proxy.stop();
474
- mockMcp.stop();
475
- }
476
- });
477
- });
478
-
479
- describe("stop()", () => {
480
- test("stop() is safe to call when server was never started", async () => {
481
- const server = createArtifactServer({ name: "never-started" });
482
- // Should not throw
483
- await server.stop();
484
- expect(server.port).toBe(0);
485
- expect(server.tunnel).toBeNull();
486
- });
487
- });
488
- });
489
-
490
- // ─── tunnel.ts tests ──────────────────────────────────────────────────────
491
-
492
- describe("createTunnel", () => {
493
- test("module exports createTunnel function", async () => {
494
- const tunnelMod = await import("../artifact-sdk/tunnel");
495
- expect(typeof tunnelMod.createTunnel).toBe("function");
496
- });
497
- });
498
-
499
- // ─── index.ts re-export tests ──────────────────────────────────────────
500
-
501
- describe("artifact-sdk index exports", () => {
502
- test("exports createArtifactServer", async () => {
503
- const sdk = await import("../artifact-sdk/index");
504
- expect(typeof sdk.createArtifactServer).toBe("function");
505
- });
506
-
507
- test("createArtifactServer from index is same as from server", async () => {
508
- const sdk = await import("../artifact-sdk/index");
509
- const { createArtifactServer: direct } = await import("../artifact-sdk/server");
510
- expect(sdk.createArtifactServer).toBe(direct);
511
- });
512
- });
513
-
514
- // ─── CLI command tests ─────────────────────────────────────────────────
515
-
516
- describe("artifact CLI command", () => {
517
- test("runArtifact module exports correctly", async () => {
518
- const mod = await import("../commands/artifact");
519
- expect(typeof mod.runArtifact).toBe("function");
520
- });
521
-
522
- test("runArtifact with unknown subcommand calls printHelp (no crash)", async () => {
523
- const { runArtifact } = await import("../commands/artifact");
524
- // "help" and unknown subcommands should just print help and return
525
- const consoleSpy = mock(() => {});
526
- const origLog = console.log;
527
- console.log = consoleSpy;
528
- try {
529
- await runArtifact("help", { additionalArgs: [] });
530
- expect(consoleSpy).toHaveBeenCalled();
531
- const output = consoleSpy.mock.calls.map((c) => c[0]).join("\n");
532
- expect(output).toContain("Usage: agent-swarm artifact");
533
- expect(output).toContain("serve");
534
- expect(output).toContain("list");
535
- expect(output).toContain("stop");
536
- } finally {
537
- console.log = origLog;
538
- }
539
- });
540
-
541
- test("runArtifact with no subcommand defaults to help", async () => {
542
- const { runArtifact } = await import("../commands/artifact");
543
- const consoleSpy = mock(() => {});
544
- const origLog = console.log;
545
- console.log = consoleSpy;
546
- try {
547
- // Default case when subcommand is not recognized
548
- await runArtifact("unknown-command", { additionalArgs: [] });
549
- expect(consoleSpy).toHaveBeenCalled();
550
- const output = consoleSpy.mock.calls.map((c) => c[0]).join("\n");
551
- expect(output).toContain("Usage: agent-swarm artifact");
552
- } finally {
553
- console.log = origLog;
554
- }
555
- });
556
-
557
- describe("artifact list", () => {
558
- test("lists artifacts from mock MCP server", async () => {
559
- const mockPort = await getAvailablePort();
560
- const mockServer = Bun.serve({
561
- port: mockPort,
562
- fetch: (req) => {
563
- const url = new URL(req.url);
564
- if (url.pathname === "/api/services") {
565
- return createBunResponse(
566
- JSON.stringify({
567
- services: [
568
- {
569
- id: "svc-1",
570
- name: "artifact-dashboard",
571
- agentId: "agent-123",
572
- status: "healthy",
573
- metadata: {
574
- type: "artifact",
575
- artifactName: "dashboard",
576
- port: 3001,
577
- publicUrl: "https://test.lt.example.com",
578
- },
579
- },
580
- {
581
- id: "svc-2",
582
- name: "some-other-service",
583
- agentId: "agent-456",
584
- status: "healthy",
585
- metadata: { type: "web" },
586
- },
587
- ],
588
- }),
589
- );
590
- }
591
- return createBunResponse("Not found", { status: 404 });
592
- },
593
- });
594
-
595
- const origEnv = { ...process.env };
596
- process.env.MCP_BASE_URL = `http://localhost:${mockPort}`;
597
- process.env.API_KEY = "example-test-key";
598
- process.env.AGENT_ID = "test-agent";
599
-
600
- const consoleSpy = mock(() => {});
601
- const origLog = console.log;
602
- console.log = consoleSpy;
603
-
604
- try {
605
- const { runArtifact } = await import("../commands/artifact");
606
- await runArtifact("list", { additionalArgs: [] });
607
-
608
- const output = consoleSpy.mock.calls.map((c) => c[0]).join("\n");
609
- // Should show the artifact but not the non-artifact service
610
- expect(output).toContain("dashboard");
611
- expect(output).toContain("https://test.lt.example.com");
612
- } finally {
613
- console.log = origLog;
614
- restoreEnvValue("MCP_BASE_URL", origEnv.MCP_BASE_URL);
615
- restoreEnvValue("API_KEY", origEnv.API_KEY);
616
- restoreEnvValue("AGENT_ID", origEnv.AGENT_ID);
617
- mockServer.stop();
618
- }
619
- });
620
-
621
- test("shows 'No active artifacts' when none exist", async () => {
622
- const mockPort = await getAvailablePort();
623
- const mockServer = Bun.serve({
624
- port: mockPort,
625
- fetch: () => createBunResponse(JSON.stringify({ services: [] })),
626
- });
627
-
628
- const origEnv = { ...process.env };
629
- process.env.MCP_BASE_URL = `http://localhost:${mockPort}`;
630
- process.env.API_KEY = "example-test-key";
631
- process.env.AGENT_ID = "test-agent";
632
-
633
- const consoleSpy = mock(() => {});
634
- const origLog = console.log;
635
- console.log = consoleSpy;
636
-
637
- try {
638
- const { runArtifact } = await import("../commands/artifact");
639
- await runArtifact("list", { additionalArgs: [] });
640
-
641
- const output = consoleSpy.mock.calls.map((c) => c[0]).join("\n");
642
- expect(output).toContain("No active artifacts");
643
- } finally {
644
- console.log = origLog;
645
- restoreEnvValue("MCP_BASE_URL", origEnv.MCP_BASE_URL);
646
- restoreEnvValue("API_KEY", origEnv.API_KEY);
647
- restoreEnvValue("AGENT_ID", origEnv.AGENT_ID);
648
- mockServer.stop();
649
- }
650
- });
651
-
652
- test("filters only artifact-type services from list", async () => {
653
- const mockPort = await getAvailablePort();
654
- const mockServer = Bun.serve({
655
- port: mockPort,
656
- fetch: () =>
657
- createBunResponse(
658
- JSON.stringify({
659
- services: [
660
- {
661
- id: "s1",
662
- name: "web-server",
663
- agentId: "a1",
664
- status: "healthy",
665
- metadata: { type: "web" },
666
- },
667
- {
668
- id: "s2",
669
- name: "api-server",
670
- agentId: "a2",
671
- status: "healthy",
672
- metadata: {},
673
- },
674
- ],
675
- }),
676
- ),
677
- });
678
-
679
- const origEnv = { ...process.env };
680
- process.env.MCP_BASE_URL = `http://localhost:${mockPort}`;
681
- process.env.API_KEY = "example-test-key";
682
- process.env.AGENT_ID = "test-agent";
683
-
684
- const consoleSpy = mock(() => {});
685
- const origLog = console.log;
686
- console.log = consoleSpy;
687
-
688
- try {
689
- const { runArtifact } = await import("../commands/artifact");
690
- await runArtifact("list", { additionalArgs: [] });
691
-
692
- const output = consoleSpy.mock.calls.map((c) => c[0]).join("\n");
693
- // None have type "artifact" so should say no artifacts
694
- expect(output).toContain("No active artifacts");
695
- } finally {
696
- console.log = origLog;
697
- restoreEnvValue("MCP_BASE_URL", origEnv.MCP_BASE_URL);
698
- restoreEnvValue("API_KEY", origEnv.API_KEY);
699
- restoreEnvValue("AGENT_ID", origEnv.AGENT_ID);
700
- mockServer.stop();
701
- }
702
- });
703
- });
704
-
705
- describe("artifact stop", () => {
706
- test("stop sends delete request to service registry", async () => {
707
- const mockPort = await getAvailablePort();
708
- let deletedServiceId = "";
709
-
710
- const mockServer = Bun.serve({
711
- port: mockPort,
712
- fetch: (req) => {
713
- const url = new URL(req.url);
714
- if (req.method === "GET" && url.pathname === "/api/services") {
715
- return createBunResponse(
716
- JSON.stringify({
717
- services: [
718
- {
719
- id: "svc-to-delete",
720
- name: "artifact-my-report",
721
- metadata: { type: "artifact", artifactName: "my-report" },
722
- },
723
- ],
724
- }),
725
- );
726
- }
727
- if (req.method === "DELETE" && url.pathname.startsWith("/api/services/")) {
728
- deletedServiceId = url.pathname.split("/").pop() || "";
729
- return createBunResponse(JSON.stringify({ success: true }));
730
- }
731
- return createBunResponse("Not found", { status: 404 });
732
- },
733
- });
734
-
735
- const origEnv = { ...process.env };
736
- process.env.MCP_BASE_URL = `http://localhost:${mockPort}`;
737
- process.env.API_KEY = "example-test-key";
738
- process.env.AGENT_ID = "test-agent";
739
-
740
- const consoleSpy = mock(() => {});
741
- const origLog = console.log;
742
- console.log = consoleSpy;
743
-
744
- try {
745
- const { runArtifact } = await import("../commands/artifact");
746
- await runArtifact("stop", { additionalArgs: ["my-report"] });
747
-
748
- expect(deletedServiceId).toBe("svc-to-delete");
749
- const output = consoleSpy.mock.calls.map((c) => c[0]).join("\n");
750
- expect(output).toContain("my-report");
751
- expect(output).toContain("stopped");
752
- } finally {
753
- console.log = origLog;
754
- restoreEnvValue("MCP_BASE_URL", origEnv.MCP_BASE_URL);
755
- restoreEnvValue("API_KEY", origEnv.API_KEY);
756
- restoreEnvValue("AGENT_ID", origEnv.AGENT_ID);
757
- mockServer.stop();
758
- }
759
- });
760
- });
761
- });
762
-
763
- // ─── Base prompt tests ──────────────────────────────────────────────────
764
-
765
- // Prompt v2: the artifacts block left the system prompt. Outputs go through
766
- // pages, apps, and agent-fs; the `artifacts` skill is opt-in (systemDefault
767
- // false). The prompt must not point at artifacts any more.
768
- describe("base prompt artifacts mention", () => {
769
- test("does not mention the artifacts path, localtunnel, or the SDK", async () => {
770
- const prompt = await getBasePrompt({
771
- role: "worker",
772
- agentId: "test-agent-id",
773
- capabilities: ["core", "artifacts", "services"],
774
- });
775
-
776
- expect(prompt).not.toContain("/workspace/personal/artifacts/");
777
- expect(prompt).not.toContain("localtunnel");
778
- expect(prompt).not.toContain("createArtifactServer");
779
- expect(prompt).not.toContain("SwarmSDK");
780
- });
781
-
782
- test("points at pages and apps for human-facing outputs instead", async () => {
783
- const prompt = await getBasePrompt({
784
- role: "worker",
785
- agentId: "test-agent-id",
786
- capabilities: ["core", "artifacts"],
787
- });
788
-
789
- expect(prompt).toContain("## Outputs");
790
- expect(prompt).toContain("`pages` skill");
791
- expect(prompt).toContain("`apps` skill");
792
- });
793
- });