@desplega.ai/agent-swarm 1.172.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-fpecx1g1.js → acp-adapter-sj2g0m84.js} +18 -17
- package/dist/{actions-pw87a1a8.js → actions-fegk44gr.js} +65 -35
- package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
- package/dist/{app-wmf0m843.js → app-kfvgbvzy.js} +19 -13
- package/dist/{assistant-emp1e108.js → assistant-z7zdy9ja.js} +65 -38
- package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-5c2z0mbe.js} +11 -9
- package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-j8xa181e.js} +11 -9
- package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-hnkn3rgt.js} +12 -10
- package/dist/{boot-seeds-8q637aew.js → boot-seeds-kwjq4d2z.js} +15 -13
- package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-9vmvpvgd.js} +20 -19
- 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-gsfsh662.js → claude-sdk-session-9jye6cxr.js} +12698 -11995
- package/dist/{cli-k46r9hmk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-kb4ehzx5.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-8vs9trd4.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-zxm9p3x4.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-6jb2rf77.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-pj6x9qce.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-388egyf6.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-qsztpfry.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-4nz8p1p5.js → cli-7x0g4sn1.js} +15474 -16082
- 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-bf7485pm.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-ztrv9y21.js → cli-bzszjf2p.js} +3689 -1081
- 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-1hgat6pa.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-j6kpj9az.js → cli-ee9gtz70.js} +1553 -68
- package/dist/{cli-e0a9sk00.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-tgpfnm6r.js → cli-ef1hg1wv.js} +518 -59
- 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-4fnb6884.js → cli-hvqbtna4.js} +7 -1259
- package/dist/{cli-pstq9888.js → cli-j0pfptdp.js} +220 -103
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-esnpehf5.js → cli-k382tshb.js} +12 -13
- package/dist/cli-k3dp5tpf.js +263 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-prrt9fa0.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-nv4zd0b9.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-g6t9texm.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-c2pq2rjb.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-5rv1j9ga.js → cli-ne236afy.js} +53 -7
- package/dist/cli-p0sg24zp.js +16 -0
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-g4f5pj26.js → cli-q0ke1yww.js} +111 -25
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-gj81e47t.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-r8smj8b8.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-nyhb1r6x.js → cli-s2sp957d.js} +52 -29
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-xczpdp9e.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-cf69s3ew.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-8hzfb6xz.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-cceskcvt.js → cli-txqb9s38.js} +8 -8
- package/dist/{cli-aspcaef3.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-j1pmc07z.js → cli-vatyhshy.js} +268 -40
- package/dist/{cli-04emfe2q.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-hwwvy36w.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-gp1rtyvk.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +32 -48
- package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-y81sssk0.js} +16 -15
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-8vx4j6a9.js} +16 -15
- package/dist/{commands-n0hshcvs.js → commands-kcpvpcrr.js} +21 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-kwpvj3rf.js} +45 -38
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-hkqvrdcb.js → db-k8ankj20.js} +13 -11
- package/dist/{db-4w7tmt1g.js → db-ww5d7jq3.js} +29 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-5sd69y14.js → dispatcher-e8gq5m68.js} +41 -34
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-2h8m4cab.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-h3359cwn.js +52 -0
- package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-g8db7x0f.js} +5 -3
- package/dist/{google-vertex-3f1jk41f.js → google-vertex-r2692q0r.js} +5 -3
- package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-0d4x7gkx.js} +19 -18
- package/dist/{handlers-s20xr10r.js → handlers-an1gzv21.js} +44 -37
- package/dist/{heartbeat-3k31q4g2.js → heartbeat-6dakeb61.js} +46 -39
- package/dist/{hook-gdn6a2fn.js → hook-6v6cv6d5.js} +12 -24
- package/dist/{hook-ygwerrdh.js → hook-sa5xye4y.js} +13 -12
- package/dist/{http-jdteeg81.js → http-r7f3eky0.js} +1598 -279
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-e0snym4t.js → index-7ep4gzdk.js} +41 -34
- 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-vm3bm9t7.js → index-rmpw9a8q.js} +41 -34
- package/dist/{index-6ksewd5y.js → index-s2wxgzr0.js} +1015 -732
- package/dist/{index-tsszt7g9.js → index-v1v6n7hg.js} +46 -39
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-ffqzqgwb.js → index-zk96aq1k.js} +11 -9
- package/dist/{keepalive-jekc896t.js → keepalive-be9vwy5h.js} +13 -11
- package/dist/{lead-4k9s9szx.js → lead-0861cwrf.js} +64 -57
- package/dist/{maintenance-shazch4n.js → maintenance-mzzgc8bn.js} +11 -9
- package/dist/node-c9we95xf.js +1 -1
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-cpdnfwdj.js} +12 -10
- package/dist/{onboard-ydrbnvww.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-nm42wwj8.js} +19 -14
- package/dist/{otel-impl-6pf5cv86.js → otel-impl-x6c1c9g4.js} +248 -869
- package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-9n2ya240.js} +245 -107
- package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-qb0rv0ap.js} +13 -11
- package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-1gzdwbh1.js} +12 -10
- package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-3mraey52.js} +13 -11
- package/dist/{render-v2-pd1rg54q.js → render-v2-agpa45zz.js} +19 -13
- package/dist/{resume-gw4a2c6v.js → resume-x0ydy9s9.js} +27 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-jda1ksdh.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-9gkxqrap.js} +7 -6
- package/dist/{work-objects-tb7ycye8.js → work-objects-y5g1y7ev.js} +49 -37
- package/dist/{worker-njjccz5d.js → worker-860c3e38.js} +64 -57
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +951 -356
- package/package.json +12 -13
- package/src/apps/catalog.generated.json +1309 -53
- package/src/apps/page-validator.ts +93 -31
- package/src/azure-devops/api.ts +94 -18
- package/src/azure-devops/auth.ts +32 -0
- package/src/azure-devops/boards-sync.ts +708 -0
- package/src/azure-devops/handlers.ts +52 -8
- package/src/azure-devops/html.ts +26 -0
- package/src/azure-devops/index.ts +15 -0
- package/src/azure-devops/templates.ts +86 -0
- package/src/azure-devops/types.ts +65 -1
- 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 +74 -13
- package/src/be/migrations/213_script_connections_sql.sql +79 -0
- 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-connections.ts +47 -8
- 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 +10 -18
- package/src/be/seed-skills/index.ts +107 -3
- package/src/be/sql-connections.ts +118 -0
- package/src/be/swarm-config-guard.ts +53 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/profile-sync.ts +5 -3
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +196 -56
- 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-oauth.ts +6 -13
- package/src/http/mcp.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +6 -5
- package/src/http/pages.ts +5 -14
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/route-def.ts +7 -6
- package/src/http/script-connection-proxy.ts +51 -4
- package/src/http/script-connections.ts +5 -2
- package/src/http/scripts.ts +1 -0
- package/src/http/session-cost-recompute.ts +12 -2
- package/src/http/session-data.ts +4 -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 +183 -2
- 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/jira/adf.ts +3 -1
- package/src/kv-reserved-namespaces.ts +2 -0
- package/src/oauth/mcp-wrapper.ts +10 -6
- package/src/otel-impl.ts +2 -1
- 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 +56 -3
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +27 -2
- package/src/providers/claude-flag-settings.ts +133 -0
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-sdk-session.ts +7 -0
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +268 -37
- package/src/providers/opencode-billing.ts +86 -0
- package/src/providers/opencode-swarm-plugin.ts +51 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +37 -3
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +11 -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/api-client.ts +15 -1
- package/src/scripts-runtime/api-types.ts +9 -1
- package/src/scripts-runtime/ctx.ts +1 -1
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/mcp-client.ts +30 -19
- 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/actions.ts +29 -1
- package/src/slack/app.ts +14 -2
- package/src/slack/assistant.ts +36 -2
- package/src/slack/channel-join.ts +3 -16
- package/src/slack/commands.ts +9 -1
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/external-channel-egress.ts +113 -0
- package/src/slack/external-channel.ts +309 -0
- package/src/slack/handlers.ts +61 -0
- package/src/slack/render-v2.ts +40 -47
- package/src/slack/work-objects.ts +5 -0
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +94 -2
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/telemetry.ts +2 -1
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-catalog-feedback.test.ts +310 -0
- package/src/tests/apps-catalog-inputs.test.ts +70 -0
- package/src/tests/apps-catalog-table.test.ts +113 -0
- package/src/tests/azure-devops-boards-sync.test.ts +584 -0
- package/src/tests/azure-devops-html.test.ts +13 -0
- package/src/tests/azure-devops-webhook-malformed.test.ts +4 -0
- 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 +144 -0
- package/src/tests/claude-flag-settings.test.ts +114 -0
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +148 -1
- package/src/tests/claude-sdk-process.fixture.ts +7 -0
- 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 +51 -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/deferred-task-wake.test.ts +52 -4
- 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/azure-boards/workitem.commented.json +89 -0
- package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
- package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
- package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
- package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
- package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
- package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
- package/src/tests/fixtures/catalog-table-definition.json.txt +468 -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-identity-refresh.ts +3 -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/node-env-runtime.test.ts +98 -0
- 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 +348 -3
- package/src/tests/opencode-billing.test.ts +28 -0
- 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-identity-refresh.test.ts +3 -0
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections-sql.test.ts +675 -0
- package/src/tests/script-connections.test.ts +7 -1
- 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 +94 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/slack-channel-join.test.ts +4 -1
- package/src/tests/slack-external-channel-config.test.ts +65 -0
- package/src/tests/slack-external-channel-egress.test.ts +237 -0
- package/src/tests/slack-external-channel-guard.test.ts +280 -0
- package/src/tests/slack-external-channel-tools.test.ts +173 -0
- package/src/tests/slack-external-channel.test.ts +370 -0
- package/src/tests/slack-render-v2.test.ts +86 -10
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/spec-base-url.test.ts +16 -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/vendored-openapi.test.ts +16 -1
- 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/defer-task.ts +17 -0
- 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/slack-post.ts +6 -0
- package/src/tools/slack-read.ts +8 -0
- package/src/tools/slack-reply.ts +6 -0
- package/src/tools/slack-start-thread.ts +6 -0
- package/src/tools/slack-update.ts +6 -0
- package/src/tools/slack-upload-file.ts +6 -0
- 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/node-env.ts +24 -0
- 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 +749 -0
- package/templates/skills/apps/content.md +60 -22
- 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/qa-loom/config.json +14 -0
- package/templates/skills/qa-loom/content.md +187 -0
- package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
- package/templates/skills/qa-loom/files/run-qa.sh +57 -0
- package/templates/skills/slack-interaction/content.md +4 -0
- 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-9xsmdpdt.js +0 -56377
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- 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/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
- package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
- package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
- package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
- package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
- package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
|
@@ -324,6 +324,8 @@ declare module "swarm-sdk" {
|
|
|
324
324
|
types?: Array<"public" | "private" | "dm" | "mpim">;
|
|
325
325
|
limit?: number;
|
|
326
326
|
}): Promise<unknown>;
|
|
327
|
+
// --- chat sdk ---
|
|
328
|
+
chat_readThread(args: { threadId: string; limit?: number }): Promise<unknown>;
|
|
327
329
|
// --- messaging ---
|
|
328
330
|
message_read(args?: {
|
|
329
331
|
channel?: string;
|
|
@@ -333,13 +335,6 @@ declare module "swarm-sdk" {
|
|
|
333
335
|
mentionsOnly?: boolean;
|
|
334
336
|
markAsRead?: boolean;
|
|
335
337
|
}): Promise<unknown>;
|
|
336
|
-
// --- services ---
|
|
337
|
-
service_list(args?: {
|
|
338
|
-
agentId?: string;
|
|
339
|
-
name?: string;
|
|
340
|
-
status?: "starting" | "healthy" | "unhealthy" | "stopped";
|
|
341
|
-
includeOwn?: boolean;
|
|
342
|
-
}): Promise<unknown>;
|
|
343
338
|
// --- context / profiles ---
|
|
344
339
|
context_history(args?: {
|
|
345
340
|
agentId?: string;
|
|
@@ -434,6 +429,7 @@ declare module "swarm-sdk" {
|
|
|
434
429
|
scope?: "global" | "agent" | "repo";
|
|
435
430
|
scopeId?: string;
|
|
436
431
|
isSecret?: boolean;
|
|
432
|
+
exportToEnv?: boolean;
|
|
437
433
|
}): Promise<unknown>;
|
|
438
434
|
config_delete(args: { id: string }): Promise<unknown>;
|
|
439
435
|
|
|
@@ -474,20 +470,36 @@ declare module "swarm-sdk" {
|
|
|
474
470
|
slack_delete(args: { channelId: string; messageTs: string }): Promise<unknown>;
|
|
475
471
|
slack_update(args: { channelId: string; messageTs: string; message: string }): Promise<unknown>;
|
|
476
472
|
|
|
473
|
+
// --- write: chat sdk ---
|
|
474
|
+
chat_reply(args: { text: string }): Promise<unknown>;
|
|
475
|
+
chat_post(args: {
|
|
476
|
+
adapter: "telegram" | "slack" | "discord" | "teams";
|
|
477
|
+
threadId?: string;
|
|
478
|
+
channelId?: string;
|
|
479
|
+
text: string;
|
|
480
|
+
}): Promise<unknown>;
|
|
481
|
+
chat_openDm(args: {
|
|
482
|
+
adapter: "telegram" | "slack" | "discord" | "teams";
|
|
483
|
+
userId: string;
|
|
484
|
+
}): Promise<unknown>;
|
|
485
|
+
chat_registerChannel(args: {
|
|
486
|
+
adapter: "telegram" | "slack" | "discord" | "teams";
|
|
487
|
+
channelId: string;
|
|
488
|
+
agentId?: string;
|
|
489
|
+
workflowId?: string;
|
|
490
|
+
name?: string;
|
|
491
|
+
}): Promise<unknown>;
|
|
492
|
+
chat_unregisterChannel(args: {
|
|
493
|
+
adapter: "telegram" | "slack" | "discord" | "teams";
|
|
494
|
+
channelId: string;
|
|
495
|
+
}): Promise<unknown>;
|
|
496
|
+
|
|
477
497
|
// --- write: messaging (internal) ---
|
|
478
498
|
message_post(args: { channel?: string; content: string; to?: string }): Promise<unknown>;
|
|
479
499
|
|
|
480
500
|
// --- write: profiles ---
|
|
481
501
|
profile_update(args: Record<string, unknown>): Promise<unknown>;
|
|
482
502
|
|
|
483
|
-
// --- write: services ---
|
|
484
|
-
service_register(args: Record<string, unknown>): Promise<unknown>;
|
|
485
|
-
service_unregister(args: { name: string }): Promise<unknown>;
|
|
486
|
-
service_updateStatus(args: {
|
|
487
|
-
name: string;
|
|
488
|
-
status: "starting" | "healthy" | "unhealthy" | "stopped";
|
|
489
|
-
}): Promise<unknown>;
|
|
490
|
-
|
|
491
503
|
// --- write: schedules ---
|
|
492
504
|
schedule_create(args: Record<string, unknown>): Promise<unknown>;
|
|
493
505
|
schedule_update(args: Record<string, unknown>): Promise<unknown>;
|
package/src/server.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { startPricingRefreshLoop } from "./be/pricing-refresh";
|
|
|
6
6
|
import { isSteeringEnabled } from "./be/steering";
|
|
7
7
|
import { registerGithubTaskReactions } from "./github/task-reactions";
|
|
8
8
|
import { loadGlobalConfigsIntoEnv } from "./http/core";
|
|
9
|
+
import { isChatSdkEnabled } from "./integrations/chat-sdk/config";
|
|
9
10
|
import { resolveTemplate } from "./prompts/resolver";
|
|
10
11
|
import { isRbacEnabled } from "./rbac";
|
|
11
12
|
import { getSlackConfiguration } from "./slack/config";
|
|
@@ -21,6 +22,10 @@ import { registerAppSyncTool } from "./tools/app-sync";
|
|
|
21
22
|
import { registerAppUpsertTool } from "./tools/app-upsert";
|
|
22
23
|
import { registerCancelApprovalRequestTool } from "./tools/cancel-approval-request";
|
|
23
24
|
import { registerCancelTaskTool } from "./tools/cancel-task";
|
|
25
|
+
import { registerChatOpenDmTool } from "./tools/chat-open-dm";
|
|
26
|
+
import { registerChatPostTool } from "./tools/chat-post";
|
|
27
|
+
import { registerChatReadThreadTool } from "./tools/chat-read-thread";
|
|
28
|
+
import { registerChatReplyTool } from "./tools/chat-reply";
|
|
24
29
|
import { registerContextDiffTool } from "./tools/context-diff";
|
|
25
30
|
import { registerContextHistoryTool } from "./tools/context-history";
|
|
26
31
|
import { registerCreateChannelTool } from "./tools/create-channel";
|
|
@@ -54,7 +59,6 @@ import {
|
|
|
54
59
|
} from "./tools/kv";
|
|
55
60
|
// Messaging capability
|
|
56
61
|
import { registerListChannelsTool } from "./tools/list-channels";
|
|
57
|
-
import { registerListServicesTool } from "./tools/list-services";
|
|
58
62
|
import { registerManageUserTool } from "./tools/manage-user";
|
|
59
63
|
// MCP Servers capability
|
|
60
64
|
import {
|
|
@@ -91,12 +95,15 @@ import {
|
|
|
91
95
|
} from "./tools/prompt-templates";
|
|
92
96
|
import { registerReadMessagesTool } from "./tools/read-messages";
|
|
93
97
|
import { registerRegisterAgentmailInboxTool } from "./tools/register-agentmail-inbox";
|
|
98
|
+
import {
|
|
99
|
+
registerRegisterChatChannelTool,
|
|
100
|
+
registerUnregisterChatChannelTool,
|
|
101
|
+
} from "./tools/register-chat-channel";
|
|
94
102
|
import {
|
|
95
103
|
registerRegisterKapsoNumberTool,
|
|
96
104
|
registerUnregisterKapsoNumberTool,
|
|
97
105
|
} from "./tools/register-kapso-number";
|
|
98
106
|
// Services capability
|
|
99
|
-
import { registerRegisterServiceTool } from "./tools/register-service";
|
|
100
107
|
// Repo management tools
|
|
101
108
|
import { registerGetReposTool, registerUpdateRepoTool } from "./tools/repos";
|
|
102
109
|
import { registerRequestHumanInputTool } from "./tools/request-human-input";
|
|
@@ -178,10 +185,8 @@ import {
|
|
|
178
185
|
registerTrackerSyncStatusTool,
|
|
179
186
|
registerTrackerUnlinkTool,
|
|
180
187
|
} from "./tools/tracker";
|
|
181
|
-
import { registerUnregisterServiceTool } from "./tools/unregister-service";
|
|
182
188
|
// Profiles capability
|
|
183
189
|
import { registerUpdateProfileTool } from "./tools/update-profile";
|
|
184
|
-
import { registerUpdateServiceStatusTool } from "./tools/update-service-status";
|
|
185
190
|
import { setPreloadedTools } from "./tools/utils";
|
|
186
191
|
import {
|
|
187
192
|
registerReplyWhatsappMessageTool,
|
|
@@ -216,7 +221,6 @@ export const ALL_CAPABILITIES = [
|
|
|
216
221
|
"prompt-templates",
|
|
217
222
|
"mcp",
|
|
218
223
|
"profiles",
|
|
219
|
-
"services",
|
|
220
224
|
"scheduling",
|
|
221
225
|
"memory",
|
|
222
226
|
"workflows",
|
|
@@ -230,6 +234,7 @@ export const ALL_CAPABILITIES = [
|
|
|
230
234
|
"repo",
|
|
231
235
|
"agentmail",
|
|
232
236
|
"kapso",
|
|
237
|
+
"chat-sdk",
|
|
233
238
|
"swarm-x",
|
|
234
239
|
] as const;
|
|
235
240
|
|
|
@@ -256,12 +261,12 @@ const DEFAULT_CAPABILITIES: string = [
|
|
|
256
261
|
//
|
|
257
262
|
// Disabled by default
|
|
258
263
|
//
|
|
259
|
-
// "services",
|
|
260
264
|
// "prompt-templates",
|
|
261
265
|
// "messaging",
|
|
262
266
|
// "swarm-x",
|
|
263
267
|
// "agentmail",
|
|
264
268
|
// "kapso",
|
|
269
|
+
// "chat-sdk", (added by getEnabledCapabilities when CHAT_SDK_ENABLED is true)
|
|
265
270
|
].join(",");
|
|
266
271
|
|
|
267
272
|
// Note: unknown names are kept (they never match hasCapability); workers
|
|
@@ -280,11 +285,22 @@ export function hasCapability(cap: CAPABILITIES_T): boolean {
|
|
|
280
285
|
return getCapabilities().has(cap);
|
|
281
286
|
}
|
|
282
287
|
|
|
288
|
+
const KNOWN_CAPABILITIES: ReadonlySet<string> = new Set(ALL_CAPABILITIES);
|
|
289
|
+
|
|
283
290
|
export function getEnabledCapabilities(): CAPABILITIES_T[] {
|
|
284
|
-
|
|
291
|
+
// Only known flags: the register response validates against ALL_CAPABILITIES,
|
|
292
|
+
// so a retired name still listed in CAPABILITIES (e.g. `services`) would
|
|
293
|
+
// otherwise turn every agent registration into a 500.
|
|
294
|
+
const capabilities: CAPABILITIES_T[] = Array.from(getCapabilities()).filter(
|
|
295
|
+
(capability) => KNOWN_CAPABILITIES.has(capability) && capability !== "chat-sdk",
|
|
296
|
+
);
|
|
297
|
+
// The Chat SDK channel follows CHAT_SDK_ENABLED, not the CAPABILITIES list.
|
|
298
|
+
if (isChatSdkEnabled()) capabilities.push("chat-sdk");
|
|
285
299
|
// Phase 1 has no HTTP receiver. Do not advertise usable Slack tools to
|
|
286
|
-
// workers when HTTP (or an invalid transport) was selected
|
|
287
|
-
|
|
300
|
+
// workers when HTTP (or an invalid transport) was selected, or when the
|
|
301
|
+
// built-in Slack app is off (SLACK_DISABLE, required by the Chat SDK Slack adapter).
|
|
302
|
+
const slack = getSlackConfiguration();
|
|
303
|
+
return slack.mode === "socket" && !slack.disabled
|
|
288
304
|
? capabilities
|
|
289
305
|
: capabilities.filter((capability) => capability !== "slack");
|
|
290
306
|
}
|
|
@@ -344,6 +360,13 @@ function applyToolAllowlist(server: McpServer, allowlist: readonly string[]): vo
|
|
|
344
360
|
}
|
|
345
361
|
}
|
|
346
362
|
|
|
363
|
+
function removeTools(server: McpServer, names: readonly string[]): void {
|
|
364
|
+
// oxlint-disable-next-line anti-slop/no-chained-type-assertions -- the MCP SDK keeps its tool registry private.
|
|
365
|
+
const tools = (server as unknown as { _registeredTools: Record<string, { remove(): void }> })
|
|
366
|
+
._registeredTools;
|
|
367
|
+
for (const name of names) tools[name]?.remove();
|
|
368
|
+
}
|
|
369
|
+
|
|
347
370
|
export async function createServer(
|
|
348
371
|
opts: {
|
|
349
372
|
scriptsOnly?: boolean;
|
|
@@ -351,6 +374,8 @@ export async function createServer(
|
|
|
351
374
|
preloadedTools?: readonly string[];
|
|
352
375
|
/** Raw SWARM_ENABLED_TOOLS value for this session; defaults to the env. */
|
|
353
376
|
enabledTools?: string;
|
|
377
|
+
/** Tool names removed from this session after registration (task-context filters). */
|
|
378
|
+
excludedTools?: readonly string[];
|
|
354
379
|
} = {},
|
|
355
380
|
) {
|
|
356
381
|
// Reload env
|
|
@@ -368,8 +393,12 @@ export async function createServer(
|
|
|
368
393
|
// fullSurface to register every tool group regardless of CAPABILITIES.
|
|
369
394
|
// An allowlist also registers every group, then prunes to the listed names.
|
|
370
395
|
// This shadows the module-level hasCapability for the registrations below.
|
|
396
|
+
// `chat-sdk` follows CHAT_SDK_ENABLED (as in getEnabledCapabilities), not
|
|
397
|
+
// the CAPABILITIES list.
|
|
371
398
|
const hasCapability = (cap: CAPABILITIES_T): boolean =>
|
|
372
|
-
opts.fullSurface === true ||
|
|
399
|
+
opts.fullSurface === true ||
|
|
400
|
+
toolAllowlist !== undefined ||
|
|
401
|
+
(cap === "chat-sdk" ? isChatSdkEnabled() : getCapabilities().has(cap));
|
|
373
402
|
|
|
374
403
|
// Initialize database with WAL mode
|
|
375
404
|
// Uses DATABASE_PATH env var for Docker volume compatibility (WAL needs .sqlite, .sqlite-wal, .sqlite-shm on same filesystem)
|
|
@@ -677,6 +706,16 @@ export async function createServer(
|
|
|
677
706
|
registerReplyWhatsappMessageTool(server);
|
|
678
707
|
}
|
|
679
708
|
|
|
709
|
+
// Chat SDK capability - Telegram/Slack/Discord/Teams through the Chat SDK channel (reply, post, DM, read, channel routes)
|
|
710
|
+
if (hasCapability("chat-sdk")) {
|
|
711
|
+
registerChatReplyTool(server);
|
|
712
|
+
registerChatPostTool(server);
|
|
713
|
+
registerChatOpenDmTool(server);
|
|
714
|
+
registerChatReadThreadTool(server);
|
|
715
|
+
registerRegisterChatChannelTool(server);
|
|
716
|
+
registerUnregisterChatChannelTool(server);
|
|
717
|
+
}
|
|
718
|
+
|
|
680
719
|
// Swarm-x capability - external command routes mirroring the `agent-swarm x ...` CLI surface
|
|
681
720
|
if (hasCapability("swarm-x")) {
|
|
682
721
|
registerSwarmXTool(server);
|
|
@@ -693,15 +732,10 @@ export async function createServer(
|
|
|
693
732
|
registerDeleteChannelTool(server);
|
|
694
733
|
}
|
|
695
734
|
|
|
696
|
-
// Services capability - PM2/background service registry
|
|
697
|
-
if (hasCapability("services")) {
|
|
698
|
-
registerRegisterServiceTool(server);
|
|
699
|
-
registerUnregisterServiceTool(server);
|
|
700
|
-
registerListServicesTool(server);
|
|
701
|
-
registerUpdateServiceStatusTool(server);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
735
|
if (toolAllowlist) applyToolAllowlist(server, toolAllowlist);
|
|
736
|
+
if (opts.fullSurface !== true && opts.excludedTools?.length) {
|
|
737
|
+
removeTools(server, opts.excludedTools);
|
|
738
|
+
}
|
|
705
739
|
|
|
706
740
|
return server;
|
|
707
741
|
}
|
package/src/slack/actions.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { App } from "@slack/bolt";
|
|
2
|
-
import type { ChatUpdateArguments } from "@slack/web-api";
|
|
2
|
+
import type { ChatUpdateArguments, WebClient } from "@slack/web-api";
|
|
3
3
|
import { cancelTask, getAgentById, getLeadAgent, getTaskById } from "../be/db";
|
|
4
4
|
import { slackContextKey } from "../tasks/context-key";
|
|
5
5
|
import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
|
|
6
6
|
import { buildCancelledBlocks, getTaskLink } from "./blocks";
|
|
7
7
|
import { resolveSlackUserId } from "./enrich";
|
|
8
|
+
import { EXTERNAL_CHANNEL_BLOCKED_REPLY, shouldBlockChannel } from "./external-channel";
|
|
8
9
|
import { ignoreSlackInbound, noteSlackInboundSideEffect } from "./inbound-dispatch";
|
|
9
10
|
import { ensureSlackThreadTree, isSlackRenderV2Enabled } from "./render-v2";
|
|
10
11
|
import { getAgentDisplayName, getAgentEmoji } from "./responses";
|
|
@@ -14,6 +15,22 @@ type ChatUpdatePayload = ChatUpdateArguments & {
|
|
|
14
15
|
unfurl_media: false;
|
|
15
16
|
};
|
|
16
17
|
|
|
18
|
+
async function postExternalChannelNotice(
|
|
19
|
+
client: WebClient,
|
|
20
|
+
channelId: string,
|
|
21
|
+
userId: string,
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
try {
|
|
24
|
+
await client.chat.postEphemeral({
|
|
25
|
+
channel: channelId,
|
|
26
|
+
user: userId,
|
|
27
|
+
text: EXTERNAL_CHANNEL_BLOCKED_REPLY,
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("[Slack] Failed to post external channel notice:", error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
export function registerActionHandlers(app: App): void {
|
|
18
35
|
// "View Full Logs" — URL button, just ack (Slack opens the link automatically)
|
|
19
36
|
app.action("view_task_logs", async ({ ack }) => {
|
|
@@ -31,6 +48,12 @@ export function registerActionHandlers(app: App): void {
|
|
|
31
48
|
const triggerId = "trigger_id" in body ? body.trigger_id : undefined;
|
|
32
49
|
if (!triggerId) return;
|
|
33
50
|
|
|
51
|
+
const channelId = "channel" in body ? body.channel?.id : undefined;
|
|
52
|
+
if (channelId && (await shouldBlockChannel(client, channelId))) {
|
|
53
|
+
await postExternalChannelNotice(client, channelId, body.user.id);
|
|
54
|
+
return ignoreSlackInbound("external_channel");
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
try {
|
|
35
58
|
await client.views.open({
|
|
36
59
|
trigger_id: triggerId,
|
|
@@ -78,6 +101,11 @@ export function registerActionHandlers(app: App): void {
|
|
|
78
101
|
return ignoreSlackInbound("follow_up_target_missing");
|
|
79
102
|
}
|
|
80
103
|
|
|
104
|
+
if (await shouldBlockChannel(client, originalTask.slackChannelId)) {
|
|
105
|
+
await postExternalChannelNotice(client, originalTask.slackChannelId, body.user.id);
|
|
106
|
+
return ignoreSlackInbound("external_channel");
|
|
107
|
+
}
|
|
108
|
+
|
|
81
109
|
const lead = await getLeadAgent();
|
|
82
110
|
// Resolve via the shared cascade. Sample context = the modal callback ID
|
|
83
111
|
// so operators can see *which* modal-submit triggered an unmapped entry.
|
package/src/slack/app.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { App, LogLevel, SocketModeReceiver } from "@slack/bolt";
|
|
2
2
|
import { emitBuiltInIntegrationConnectedOnce, ensureSlackRenderV2Activation } from "../be/db";
|
|
3
|
+
import { getNodeEnv } from "../utils/node-env";
|
|
3
4
|
import { getSlackConfiguration } from "./config";
|
|
4
5
|
import { type SlackConnectionState, setSlackConnectionState } from "./connection-state";
|
|
5
6
|
import { installSlackEgressScrub } from "./egress-scrub";
|
|
7
|
+
import { installSlackExternalChannelGuard } from "./external-channel-egress";
|
|
6
8
|
import { slackInboundOutcomeMiddleware } from "./inbound-dispatch";
|
|
7
9
|
import { getSlackSocketModeBlockReason, SLACK_DEV_SOCKET_MODE_OPT_IN } from "./socket-mode-guard";
|
|
8
10
|
import { startTaskWatcher, stopTaskWatcher } from "./watcher";
|
|
@@ -78,13 +80,15 @@ export async function initSlackApp(): Promise<App | null> {
|
|
|
78
80
|
return null;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
// Every client Bolt builds from here on scrubs outbound text
|
|
83
|
+
// Every client Bolt builds from here on scrubs outbound text and refuses
|
|
84
|
+
// writes to external channels when SLACK_BLOCK_EXTERNAL_CHANNELS is on.
|
|
82
85
|
installSlackEgressScrub();
|
|
86
|
+
installSlackExternalChannelGuard();
|
|
83
87
|
|
|
84
88
|
// SLACK_API_URL points Bolt (Web API and apps.connections.open) at a mock Slack server for e2e tests.
|
|
85
89
|
const slackApiUrl = process.env.SLACK_API_URL;
|
|
86
90
|
const clientOptions = slackApiUrl ? { slackApiUrl } : undefined;
|
|
87
|
-
const logLevel =
|
|
91
|
+
const logLevel = getNodeEnv() === "development" ? LogLevel.DEBUG : LogLevel.INFO;
|
|
88
92
|
// Build the receiver ourselves so its SocketModeClient lifecycle events can
|
|
89
93
|
// feed /status. Bolt would otherwise forward clientOptions via installerOptions.
|
|
90
94
|
receiver = new SocketModeReceiver({
|
|
@@ -123,6 +127,14 @@ export async function initSlackApp(): Promise<App | null> {
|
|
|
123
127
|
const { createAssistant } = await import("./assistant");
|
|
124
128
|
app.assistant(createAssistant());
|
|
125
129
|
|
|
130
|
+
// Report-only scan for external (Slack Connect) channels the bot is already in.
|
|
131
|
+
const { isBlockExternalEnabled, scanExternalChannelsOnBoot } = await import("./external-channel");
|
|
132
|
+
if (isBlockExternalEnabled()) {
|
|
133
|
+
scanExternalChannelsOnBoot(app.client).catch((error) => {
|
|
134
|
+
console.error("[Slack] External channel boot scan failed:", error);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
126
138
|
return app;
|
|
127
139
|
}
|
|
128
140
|
|
package/src/slack/assistant.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Assistant } from "@slack/bolt";
|
|
2
|
+
import type { WebClient } from "@slack/web-api";
|
|
2
3
|
import { getAgentWorkingOnThread, getLeadAgent, getMostRecentTaskInThread } from "../be/db";
|
|
3
4
|
import { resolveTemplate } from "../prompts/resolver";
|
|
4
5
|
import { slackContextKey } from "../tasks/context-key";
|
|
5
6
|
import { ackSlackMessage, reactionName } from "./ack";
|
|
6
7
|
import { resolveSlackUserId, rewriteSlackMentions } from "./enrich";
|
|
7
8
|
import { wasEventSeen } from "./event-dedup";
|
|
9
|
+
import { shouldBlockChannel } from "./external-channel";
|
|
8
10
|
import type { SlackFile } from "./files";
|
|
9
11
|
import {
|
|
10
12
|
ignoreSlackInbound,
|
|
@@ -32,9 +34,24 @@ const isAdditiveSlack = () => isEnvFlagEnabled("ADDITIVE_SLACK", false);
|
|
|
32
34
|
// a different agent (e.g. Devin) rather than our bot.
|
|
33
35
|
let cachedBotUserId: string | null = null;
|
|
34
36
|
|
|
37
|
+
/** Startup and context callbacks write to the thread, so they get the inbound guard too. */
|
|
38
|
+
async function isExternalAssistantThread(
|
|
39
|
+
client: WebClient,
|
|
40
|
+
channelId: string,
|
|
41
|
+
body: unknown,
|
|
42
|
+
): Promise<boolean> {
|
|
43
|
+
const envelope = (body as { is_ext_shared_channel?: boolean }).is_ext_shared_channel;
|
|
44
|
+
if (!(await shouldBlockChannel(client, channelId, envelope))) return false;
|
|
45
|
+
console.log(`[Slack] Ignoring assistant thread event in external channel ${channelId}`);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
export function createAssistant(): Assistant {
|
|
36
50
|
return new Assistant({
|
|
37
|
-
threadStarted: async ({ say, setSuggestedPrompts, saveThreadContext }) => {
|
|
51
|
+
threadStarted: async ({ event, body, client, say, setSuggestedPrompts, saveThreadContext }) => {
|
|
52
|
+
if (await isExternalAssistantThread(client, event.assistant_thread.channel_id, body)) {
|
|
53
|
+
return ignoreSlackInbound("external_channel");
|
|
54
|
+
}
|
|
38
55
|
try {
|
|
39
56
|
await saveThreadContext();
|
|
40
57
|
|
|
@@ -57,7 +74,10 @@ export function createAssistant(): Assistant {
|
|
|
57
74
|
}
|
|
58
75
|
},
|
|
59
76
|
|
|
60
|
-
threadContextChanged: async ({ saveThreadContext }) => {
|
|
77
|
+
threadContextChanged: async ({ event, body, client, saveThreadContext }) => {
|
|
78
|
+
if (await isExternalAssistantThread(client, event.assistant_thread.channel_id, body)) {
|
|
79
|
+
return ignoreSlackInbound("external_channel");
|
|
80
|
+
}
|
|
61
81
|
await saveThreadContext();
|
|
62
82
|
},
|
|
63
83
|
|
|
@@ -71,6 +91,20 @@ export function createAssistant(): Assistant {
|
|
|
71
91
|
return ignoreSlackInbound("duplicate_event");
|
|
72
92
|
}
|
|
73
93
|
|
|
94
|
+
// Block external (Slack Connect) channels when SLACK_BLOCK_EXTERNAL_CHANNELS is on.
|
|
95
|
+
const externalCheckChannel = (message as { channel?: string }).channel;
|
|
96
|
+
if (
|
|
97
|
+
externalCheckChannel &&
|
|
98
|
+
(await shouldBlockChannel(
|
|
99
|
+
client,
|
|
100
|
+
externalCheckChannel,
|
|
101
|
+
(body as { is_ext_shared_channel?: boolean }).is_ext_shared_channel,
|
|
102
|
+
))
|
|
103
|
+
) {
|
|
104
|
+
console.log(`[Slack] Ignoring message in external channel ${externalCheckChannel}`);
|
|
105
|
+
return ignoreSlackInbound("external_channel");
|
|
106
|
+
}
|
|
107
|
+
|
|
74
108
|
// Wrap setStatus/setTitle to swallow all errors gracefully.
|
|
75
109
|
// These calls can fail for various reasons (no_permission when the thread
|
|
76
110
|
// wasn't started by the assistant, network errors, etc.), so we log and continue.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { WebClient } from "@slack/web-api";
|
|
2
|
-
|
|
3
|
-
const logger = console;
|
|
2
|
+
import { getChannelExternality } from "./external-channel";
|
|
4
3
|
|
|
5
4
|
type SlackErrorData = {
|
|
6
5
|
error?: unknown;
|
|
@@ -36,20 +35,8 @@ export function slackMissingScopeMessage(error: unknown): string | undefined {
|
|
|
36
35
|
* are the authoritative org-boundary signal per Slack's API docs.
|
|
37
36
|
*/
|
|
38
37
|
async function isKnownExternalChannel(client: WebClient, channelId: string): Promise<boolean> {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const ch = (resp.channel ?? {}) as {
|
|
42
|
-
is_ext_shared?: boolean;
|
|
43
|
-
is_pending_ext_shared?: boolean;
|
|
44
|
-
};
|
|
45
|
-
return ch.is_ext_shared === true || ch.is_pending_ext_shared === true;
|
|
46
|
-
} catch (error) {
|
|
47
|
-
logger.warn(
|
|
48
|
-
`[Slack] conversations.info failed for ${channelId}; attempting join fallback:`,
|
|
49
|
-
error,
|
|
50
|
-
);
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
38
|
+
// "unknown" (conversations.info failed) falls through to the join attempt.
|
|
39
|
+
return (await getChannelExternality(client, channelId)) === "external";
|
|
53
40
|
}
|
|
54
41
|
|
|
55
42
|
/**
|
package/src/slack/commands.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { App } from "@slack/bolt";
|
|
2
2
|
import { getAllAgents, getAllTasks } from "../be/db";
|
|
3
3
|
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
4
|
+
import { EXTERNAL_CHANNEL_BLOCKED_REPLY, shouldBlockChannel } from "./external-channel";
|
|
4
5
|
|
|
5
6
|
export function registerCommandHandler(app: App): void {
|
|
6
|
-
app.command("/agent-swarm-status", async ({ ack, respond }) => {
|
|
7
|
+
app.command("/agent-swarm-status", async ({ ack, respond, command, client }) => {
|
|
7
8
|
await ack();
|
|
8
9
|
|
|
10
|
+
// The status reply lists task summaries; never show them in an external channel.
|
|
11
|
+
if (await shouldBlockChannel(client, command.channel_id)) {
|
|
12
|
+
console.log(`[Slack] Refusing /agent-swarm-status in external channel ${command.channel_id}`);
|
|
13
|
+
await respond({ response_type: "ephemeral", text: EXTERNAL_CHANNEL_BLOCKED_REPLY });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
const agents = await getAllAgents();
|
|
10
18
|
const tasks = await getAllTasks({ status: "in_progress" });
|
|
11
19
|
|
package/src/slack/event-dedup.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* safe-but-tight bound.
|
|
19
19
|
*
|
|
20
20
|
* Single-pod-only. The API server (which owns the Slack socket) runs as a
|
|
21
|
-
* single
|
|
21
|
+
* single API process; if that ever changes, swap this for a DB-backed table.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
const DEFAULT_TTL_MS = 300_000; // 5 minutes
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { webApi } from "@slack/bolt";
|
|
2
|
+
import type { WebClient } from "@slack/web-api";
|
|
3
|
+
import { type ChannelBlockReason, getChannelBlockReason } from "./external-channel";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Client-level external-channel guard for every outbound Slack write.
|
|
7
|
+
*
|
|
8
|
+
* Tools, the watcher, render-v2 streaming, outcome cards, progress edits and
|
|
9
|
+
* alerts all call `client.chat.*` / `filesUploadV2` directly, so the guard sits
|
|
10
|
+
* on `WebClient` itself, like `egress-scrub.ts`. The prototype is patched
|
|
11
|
+
* before any client is built, which covers Bolt's `app.client` and the
|
|
12
|
+
* per-request clients it hands to listeners. A no-op unless
|
|
13
|
+
* SLACK_BLOCK_EXTERNAL_CHANNELS is on.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Methods that publish content into a channel, and the argument naming it. */
|
|
17
|
+
const GUARDED_METHODS: Record<string, readonly string[]> = {
|
|
18
|
+
"chat.postMessage": ["channel"],
|
|
19
|
+
"chat.postEphemeral": ["channel"],
|
|
20
|
+
"chat.update": ["channel"],
|
|
21
|
+
"chat.scheduleMessage": ["channel"],
|
|
22
|
+
"chat.meMessage": ["channel"],
|
|
23
|
+
"chat.startStream": ["channel"],
|
|
24
|
+
"chat.appendStream": ["channel"],
|
|
25
|
+
"chat.stopStream": ["channel"],
|
|
26
|
+
"assistant.threads.setStatus": ["channel_id"],
|
|
27
|
+
"assistant.threads.setSuggestedPrompts": ["channel_id"],
|
|
28
|
+
"assistant.threads.setTitle": ["channel_id"],
|
|
29
|
+
"files.completeUploadExternal": ["channel_id", "channels"],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Slack-shaped error codes, so delivery code classifies them like platform errors. */
|
|
33
|
+
export const EXTERNAL_CHANNEL_BLOCKED_CODE = "external_channel_blocked";
|
|
34
|
+
export const EXTERNAL_CHANNEL_UNVERIFIED_CODE = "external_channel_unverified";
|
|
35
|
+
|
|
36
|
+
type Args = Record<string, unknown>;
|
|
37
|
+
|
|
38
|
+
/** Channel IDs named by a write's arguments (`channels` may be comma-separated). */
|
|
39
|
+
function targetChannels(fields: readonly string[], args: Args | undefined): string[] {
|
|
40
|
+
if (!args) return [];
|
|
41
|
+
const out: string[] = [];
|
|
42
|
+
for (const field of fields) {
|
|
43
|
+
const value = args[field];
|
|
44
|
+
if (typeof value !== "string") continue;
|
|
45
|
+
for (const id of value.split(",")) {
|
|
46
|
+
const trimmed = id.trim();
|
|
47
|
+
if (trimmed) out.push(trimmed);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function blockedError(method: string, channelId: string, reason: ChannelBlockReason): Error {
|
|
54
|
+
const code =
|
|
55
|
+
reason === "external" ? EXTERNAL_CHANNEL_BLOCKED_CODE : EXTERNAL_CHANNEL_UNVERIFIED_CODE;
|
|
56
|
+
const error = new Error(
|
|
57
|
+
`An API error occurred: ${code} (${method} to ${channelId} refused by SLACK_BLOCK_EXTERNAL_CHANNELS)`,
|
|
58
|
+
);
|
|
59
|
+
return Object.assign(error, {
|
|
60
|
+
code: webApi.ErrorCode.PlatformError,
|
|
61
|
+
data: { ok: false, error: code },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function assertChannelsAllowed(
|
|
66
|
+
client: WebClient,
|
|
67
|
+
method: string,
|
|
68
|
+
channels: string[],
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
for (const channelId of channels) {
|
|
71
|
+
const reason = await getChannelBlockReason(client, channelId);
|
|
72
|
+
if (reason) {
|
|
73
|
+
console.warn(`[Slack] Refused ${method} to ${channelId} (${reason})`);
|
|
74
|
+
throw blockedError(method, channelId, reason);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const INSTALLED = Symbol.for("agent-swarm.slack-external-channel-guard");
|
|
80
|
+
|
|
81
|
+
type PatchableProto = {
|
|
82
|
+
apiCall: (method: string, options?: Args) => Promise<unknown>;
|
|
83
|
+
filesUploadV2: (options: Args) => Promise<unknown>;
|
|
84
|
+
[INSTALLED]?: true;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Route every `WebClient` built after this call through the guard. Idempotent.
|
|
89
|
+
* Call it before the Bolt `App` (and its receiver) are constructed.
|
|
90
|
+
*/
|
|
91
|
+
export function installSlackExternalChannelGuard(): void {
|
|
92
|
+
// oxlint-disable-next-line anti-slop/no-chained-type-assertions -- we patch private WebClient prototype methods.
|
|
93
|
+
const proto = webApi.WebClient.prototype as unknown as PatchableProto;
|
|
94
|
+
if (proto[INSTALLED]) return;
|
|
95
|
+
const originalApiCall = proto.apiCall;
|
|
96
|
+
const originalUpload = proto.filesUploadV2;
|
|
97
|
+
proto.apiCall = async function (this: WebClient, method: string, options?: Args) {
|
|
98
|
+
const fields = GUARDED_METHODS[method];
|
|
99
|
+
if (fields) await assertChannelsAllowed(this, method, targetChannels(fields, options));
|
|
100
|
+
return originalApiCall.call(this, method, options);
|
|
101
|
+
};
|
|
102
|
+
// Check before the bytes leave: completeUploadExternal is guarded too, but by
|
|
103
|
+
// then the file already sits in Slack storage.
|
|
104
|
+
proto.filesUploadV2 = async function (this: WebClient, options: Args) {
|
|
105
|
+
await assertChannelsAllowed(
|
|
106
|
+
this,
|
|
107
|
+
"files.uploadV2",
|
|
108
|
+
targetChannels(["channel_id", "channels"], options),
|
|
109
|
+
);
|
|
110
|
+
return originalUpload.call(this, options);
|
|
111
|
+
};
|
|
112
|
+
proto[INSTALLED] = true;
|
|
113
|
+
}
|