@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
|
@@ -181,6 +181,32 @@ export interface SlackRouteModify {
|
|
|
181
181
|
target: { kind: "agent"; agentId: string } | { kind: "lead" } | { kind: "broadcast" };
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/** A Chat SDK message after its thread buffer flushed, before routing. */
|
|
185
|
+
export interface ChatRouteEvent {
|
|
186
|
+
/** Adapter name, such as `telegram` or `slack`. */
|
|
187
|
+
adapter: string;
|
|
188
|
+
channelId: string;
|
|
189
|
+
/** The Chat SDK thread id, prefixed with the adapter name. */
|
|
190
|
+
threadId: string;
|
|
191
|
+
/** The first buffered sender. */
|
|
192
|
+
userId: string;
|
|
193
|
+
userName?: string;
|
|
194
|
+
/** The buffered messages, joined. */
|
|
195
|
+
text: string;
|
|
196
|
+
botMentioned: boolean;
|
|
197
|
+
isDirectMessage: boolean;
|
|
198
|
+
/** Set when earlier swarm tasks exist in this thread. */
|
|
199
|
+
threadContext?: { adapter: string; threadId: string };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface ChatRouteModify {
|
|
203
|
+
target:
|
|
204
|
+
| { kind: "agent"; agentId: string }
|
|
205
|
+
| { kind: "lead" }
|
|
206
|
+
| { kind: "broadcast" }
|
|
207
|
+
| { kind: "workflow"; workflowId: string };
|
|
208
|
+
}
|
|
209
|
+
|
|
184
210
|
export type HeartbeatClassification = "no-session" | "stale-session" | "fresh-stalled";
|
|
185
211
|
export type HeartbeatAction = "supersede-resume" | "fail" | "record";
|
|
186
212
|
|
|
@@ -236,6 +262,19 @@ export interface SlackMessageEvent {
|
|
|
236
262
|
taskId?: string;
|
|
237
263
|
}
|
|
238
264
|
|
|
265
|
+
/** A Chat SDK message the swarm received or sent. */
|
|
266
|
+
export interface ChatMessageEvent {
|
|
267
|
+
adapter: string;
|
|
268
|
+
channelId: string;
|
|
269
|
+
threadId: string;
|
|
270
|
+
/** The sender, for an inbound message. */
|
|
271
|
+
userId?: string;
|
|
272
|
+
text: string;
|
|
273
|
+
direction: "inbound" | "outbound";
|
|
274
|
+
taskId?: string;
|
|
275
|
+
messageId?: string;
|
|
276
|
+
}
|
|
277
|
+
|
|
239
278
|
/**
|
|
240
279
|
* A person answered an approval request. Fires for workflow and standalone
|
|
241
280
|
* requests. Timeouts and cancellations do not emit it today.
|
|
@@ -352,6 +391,11 @@ export interface SwarmEventMap {
|
|
|
352
391
|
modify: SlackRouteModify;
|
|
353
392
|
result: PreResult<SlackRouteModify>;
|
|
354
393
|
};
|
|
394
|
+
"pre.chat.route": {
|
|
395
|
+
event: ChatRouteEvent;
|
|
396
|
+
modify: ChatRouteModify;
|
|
397
|
+
result: PreResult<ChatRouteModify>;
|
|
398
|
+
};
|
|
355
399
|
"pre.heartbeat.remediate": {
|
|
356
400
|
event: HeartbeatRemediateEvent;
|
|
357
401
|
modify: HeartbeatRemediateModify;
|
|
@@ -369,6 +413,7 @@ export interface SwarmEventMap {
|
|
|
369
413
|
"post.task.superseded": { event: TaskSupersededEvent; modify: never; result: void };
|
|
370
414
|
"post.task.progress": { event: TaskProgressEvent; modify: never; result: void };
|
|
371
415
|
"post.slack.message": { event: SlackMessageEvent; modify: never; result: void };
|
|
416
|
+
"post.chat.message": { event: ChatMessageEvent; modify: never; result: void };
|
|
372
417
|
"post.approval.resolved": { event: ApprovalResolvedEvent; modify: never; result: void };
|
|
373
418
|
"post.task.budgetRefused": { event: TaskBudgetRefusedEvent; modify: never; result: void };
|
|
374
419
|
"post.email.received": { event: EmailReceivedEvent; modify: never; result: void };
|
|
@@ -171,6 +171,13 @@ export function runContext(event: keyof SwarmEventMap, payload: unknown): RunCon
|
|
|
171
171
|
case "post.kapso.message":
|
|
172
172
|
subject = `number ${stringField(payload, "phoneNumberId") ?? "?"}: message ${stringField(payload, "messageId") ?? "?"}`;
|
|
173
173
|
break;
|
|
174
|
+
// Ids only: a chat message text is personal content.
|
|
175
|
+
case "pre.chat.route":
|
|
176
|
+
case "post.chat.message": {
|
|
177
|
+
const taskId = stringField(payload, "taskId");
|
|
178
|
+
subject = `${stringField(payload, "adapter") ?? "?"} thread ${stringField(payload, "threadId") ?? "?"}${taskId ? `: task ${taskId}` : ""}`;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
174
181
|
case "post.vcs.event": {
|
|
175
182
|
const number = field(payload, "number");
|
|
176
183
|
subject = `${stringField(payload, "provider") ?? "?"} ${stringField(payload, "kind") ?? "?"}.${stringField(payload, "action") ?? "?"}: ${stringField(payload, "repo") ?? "?"}${typeof number === "number" ? `#${number}` : ""}`;
|
|
@@ -3,6 +3,7 @@ import { scrubSecrets } from "../utils/secret-scrubber";
|
|
|
3
3
|
import { workflowEventBus } from "../workflows/event-bus";
|
|
4
4
|
import type {
|
|
5
5
|
ApprovalResolvedEvent,
|
|
6
|
+
ChatMessageEvent,
|
|
6
7
|
KapsoMessageEvent,
|
|
7
8
|
TaskBudgetRefusedEvent,
|
|
8
9
|
VcsEvent,
|
|
@@ -258,6 +259,41 @@ const onKapsoMessage = observed("kapso.message.received", async (data) => {
|
|
|
258
259
|
);
|
|
259
260
|
});
|
|
260
261
|
|
|
262
|
+
/** `chat.message.received` and `chat.message.sent` carry the same fields; the name sets the direction. */
|
|
263
|
+
function chatMessageBridge(eventName: string, direction: ChatMessageEvent["direction"]) {
|
|
264
|
+
return observed(eventName, async (data) => {
|
|
265
|
+
const source = (data ?? {}) as Record<string, unknown>;
|
|
266
|
+
const adapter = text(source.adapter);
|
|
267
|
+
const channelId = text(source.channelId);
|
|
268
|
+
const threadId = text(source.threadId);
|
|
269
|
+
const messageText = text(source.text);
|
|
270
|
+
if (
|
|
271
|
+
adapter === undefined ||
|
|
272
|
+
channelId === undefined ||
|
|
273
|
+
threadId === undefined ||
|
|
274
|
+
messageText === undefined
|
|
275
|
+
) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
await dispatchPost(
|
|
279
|
+
"post.chat.message",
|
|
280
|
+
compact<ChatMessageEvent>({
|
|
281
|
+
adapter,
|
|
282
|
+
channelId,
|
|
283
|
+
threadId,
|
|
284
|
+
userId: text(source.userId),
|
|
285
|
+
text: messageText,
|
|
286
|
+
direction,
|
|
287
|
+
taskId: text(source.taskId),
|
|
288
|
+
messageId: text(source.messageId),
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const onChatMessageReceived = chatMessageBridge("chat.message.received", "inbound");
|
|
295
|
+
const onChatMessageSent = chatMessageBridge("chat.message.sent", "outbound");
|
|
296
|
+
|
|
261
297
|
// The code-host events are named `<provider>.<kind>.<action>` and the last
|
|
262
298
|
// segment is open-ended (`github.pull_request.<action>`, `gitlab.pipeline.<status>`),
|
|
263
299
|
// so one prefix subscription per provider covers them all.
|
|
@@ -331,6 +367,8 @@ export function initExtensionPostBridge(): void {
|
|
|
331
367
|
workflowEventBus.on("task.budget_refused", onTaskBudgetRefused);
|
|
332
368
|
workflowEventBus.on("agentmail.message.received", onEmailReceived);
|
|
333
369
|
workflowEventBus.on("kapso.message.received", onKapsoMessage);
|
|
370
|
+
workflowEventBus.on("chat.message.received", onChatMessageReceived);
|
|
371
|
+
workflowEventBus.on("chat.message.sent", onChatMessageSent);
|
|
334
372
|
for (const prefix of VCS_PREFIXES) workflowEventBus.onPrefix(prefix, onVcsEvent);
|
|
335
373
|
}
|
|
336
374
|
|
|
@@ -348,5 +386,7 @@ export function teardownExtensionPostBridge(): void {
|
|
|
348
386
|
workflowEventBus.off("task.budget_refused", onTaskBudgetRefused);
|
|
349
387
|
workflowEventBus.off("agentmail.message.received", onEmailReceived);
|
|
350
388
|
workflowEventBus.off("kapso.message.received", onKapsoMessage);
|
|
389
|
+
workflowEventBus.off("chat.message.received", onChatMessageReceived);
|
|
390
|
+
workflowEventBus.off("chat.message.sent", onChatMessageSent);
|
|
351
391
|
for (const prefix of VCS_PREFIXES) workflowEventBus.offPrefix(prefix, onVcsEvent);
|
|
352
392
|
}
|
package/src/github/handlers.ts
CHANGED
|
@@ -877,20 +877,27 @@ export async function handleComment(
|
|
|
877
877
|
comment.body.slice(0, 100),
|
|
878
878
|
);
|
|
879
879
|
|
|
880
|
+
const skip = (reason: string) => {
|
|
881
|
+
console.log(
|
|
882
|
+
`[GitHub] Skipped ${eventType}.${action} by ${sender.login} on ${repository.full_name} comment ${comment.id}: ${reason}`,
|
|
883
|
+
);
|
|
884
|
+
return { created: false };
|
|
885
|
+
};
|
|
886
|
+
|
|
880
887
|
// Only handle created action
|
|
881
888
|
if (action !== "created") {
|
|
882
|
-
return
|
|
889
|
+
return skip("action is not created");
|
|
883
890
|
}
|
|
884
891
|
|
|
885
892
|
// Check for @agent-swarm mention in comment
|
|
886
893
|
if (!detectMention(comment.body)) {
|
|
887
|
-
return
|
|
894
|
+
return skip("no bot mention");
|
|
888
895
|
}
|
|
889
896
|
|
|
890
897
|
// Deduplicate
|
|
891
898
|
const eventKey = `comment:${repository.full_name}:${comment.id}`;
|
|
892
899
|
if (isDuplicate(eventKey)) {
|
|
893
|
-
return
|
|
900
|
+
return skip("duplicate delivery");
|
|
894
901
|
}
|
|
895
902
|
|
|
896
903
|
// Find lead agent (may be null - task will be unassigned)
|
|
@@ -932,7 +939,7 @@ export async function handleComment(
|
|
|
932
939
|
);
|
|
933
940
|
|
|
934
941
|
if (result.skipped) {
|
|
935
|
-
return
|
|
942
|
+
return skip("github.comment.mentioned template is skipped");
|
|
936
943
|
}
|
|
937
944
|
|
|
938
945
|
// Create task (assigned to lead if available, otherwise unassigned)
|
|
@@ -11,15 +11,15 @@ import { registerTemplate } from "../prompts/registry";
|
|
|
11
11
|
// Heartbeat checklist
|
|
12
12
|
// ============================================================================
|
|
13
13
|
|
|
14
|
+
// Standing orders and instructions come before the live status: they are
|
|
15
|
+
// usually identical from one tick to the next, so they can form a cacheable
|
|
16
|
+
// prefix of the task text, and the status that changes every tick goes last.
|
|
14
17
|
registerTemplate({
|
|
15
18
|
eventType: "heartbeat.checklist",
|
|
16
19
|
header: "",
|
|
17
20
|
defaultBody: `Task Type: Heartbeat Checklist
|
|
18
21
|
Goal: Review system status and your standing orders, take action if needed.
|
|
19
22
|
|
|
20
|
-
## Current System Status [auto-generated]
|
|
21
|
-
{{system_status}}
|
|
22
|
-
|
|
23
23
|
## Your Standing Orders (snapshot from HEARTBEAT.md)
|
|
24
24
|
{{heartbeat_content}}
|
|
25
25
|
|
|
@@ -29,7 +29,7 @@ Goal: Review system status and your standing orders, take action if needed.
|
|
|
29
29
|
1. **Read your HEARTBEAT.md** — run \`read /workspace/HEARTBEAT.md\` to get the latest standing orders (the snapshot above may be slightly stale).
|
|
30
30
|
2. **Prune tracked HEARTBEAT items FIRST.** Active Blockers + Watch Items + Open Discussion combined must stay at ≤10 items; 20 is the absolute max only when genuinely super busy. This cap does not apply to evergreen Standing Orders / Governance / Playbook-index reference sections. Before adding anything, re-check every tracked item against its lift trigger and remove anything resolved, stale, or past its trigger date. Lift incident detail to memory; never keep it inline in HEARTBEAT.md. Every new tracked item must include an explicit lift trigger + date; if it has no removal condition, do not add it. If the tracked list is already at/over cap, prune before (or instead of) adding — the cap is binding.
|
|
31
31
|
3. **Run seeded heartbeat data-gathering.** Use \`script-run\` with the global scripts \`schedule-health\` and \`task-failure-audit\`, both with args \`{ "days": 1, "publishPage": false }\`. They report failing schedules and failure clusters. Check any other standing order in HEARTBEAT.md yourself. Scripts can read Slack too (\`ctx.swarm.slack_read\` or the seeded \`slack-thread-flatten\` script), so a recurring Slack check can be scripted.
|
|
32
|
-
4. Review the system status
|
|
32
|
+
4. Review the system status below plus the script results for anything that needs attention (stalled tasks, idle workers with available work, anomalies).
|
|
33
33
|
5. **CRITICAL — Reboot failure triage:** Failures with reason "worker session not found" or "worker session heartbeat is stale" indicate tasks that were INTERRUPTED by a server restart. These are NOT "expected auto-cleanup" — they represent work that was lost mid-execution. For each such failure:
|
|
34
34
|
- Check what the task was (via \`get-task-details\` with the task ID from the failure)
|
|
35
35
|
- If a retry task was auto-created (tagged \`reboot-retry\`), verify it is progressing
|
|
@@ -39,7 +39,10 @@ Goal: Review system status and your standing orders, take action if needed.
|
|
|
39
39
|
7. If something needs attention — take action now using your available tools (create tasks, post to Slack, cancel stuck tasks, etc.).
|
|
40
40
|
8. If everything looks healthy and no standing orders are actionable — complete this task with a brief "All clear" summary. You may NOT say "All clear" if reboot-related failures exist that haven't been triaged.
|
|
41
41
|
9. Do NOT create another heartbeat-checklist task — the system handles scheduling.
|
|
42
|
-
10. **Update HEARTBEAT.md only after pruning.** Keep it current, but keep tracked items capped: remove resolved items, add only dated lift-triggered items, and lift detail to memory instead of growing the file
|
|
42
|
+
10. **Update HEARTBEAT.md only after pruning.** Keep it current, but keep tracked items capped: remove resolved items, add only dated lift-triggered items, and lift detail to memory instead of growing the file.
|
|
43
|
+
|
|
44
|
+
## Current System Status [auto-generated]
|
|
45
|
+
{{system_status}}`,
|
|
43
46
|
variables: [
|
|
44
47
|
{
|
|
45
48
|
name: "system_status",
|
package/src/hooks/hook.ts
CHANGED
|
@@ -1336,32 +1336,6 @@ export async function handleHook(): Promise<void> {
|
|
|
1336
1336
|
break;
|
|
1337
1337
|
|
|
1338
1338
|
case "Stop":
|
|
1339
|
-
// Clean up any artifact tunnels managed by PM2
|
|
1340
|
-
try {
|
|
1341
|
-
const pm2ListOutput = await Bun.$`pm2 jlist 2>/dev/null || echo "[]"`.text();
|
|
1342
|
-
const pm2Processes = JSON.parse(pm2ListOutput.trim());
|
|
1343
|
-
const artifactProcesses = (pm2Processes as { name?: string }[]).filter((p) =>
|
|
1344
|
-
p.name?.startsWith("artifact-"),
|
|
1345
|
-
);
|
|
1346
|
-
|
|
1347
|
-
for (const proc of artifactProcesses) {
|
|
1348
|
-
try {
|
|
1349
|
-
await Bun.$`pm2 delete ${proc.name!}`.quiet();
|
|
1350
|
-
} catch {
|
|
1351
|
-
// Process might already be stopped
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
} catch {
|
|
1355
|
-
// Non-fatal: PM2 might not be available
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
// Save PM2 processes before shutdown (for container restart persistence)
|
|
1359
|
-
try {
|
|
1360
|
-
await Bun.$`pm2 save`.quiet();
|
|
1361
|
-
} catch {
|
|
1362
|
-
// PM2 not available or no processes - silently ignore
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
1339
|
// Sync CLAUDE.md (then restore its backup), identity files, and setup script back to database
|
|
1366
1340
|
if (agentInfo?.id) {
|
|
1367
1341
|
try {
|
package/src/http/all-routes.ts
CHANGED
package/src/http/config.ts
CHANGED
|
@@ -18,10 +18,12 @@ import { fillOrgLogoFromWebsite } from "../be/org-profile";
|
|
|
18
18
|
import { getUserGrant } from "../be/rbac-roles";
|
|
19
19
|
import {
|
|
20
20
|
internalConfigKeyError,
|
|
21
|
+
isApiOnlyConfigKey,
|
|
21
22
|
isInternalConfigKey,
|
|
22
23
|
isReservedConfigKey,
|
|
23
24
|
reservedKeyError,
|
|
24
25
|
validateConfigValue,
|
|
26
|
+
validateExportToEnv,
|
|
25
27
|
} from "../be/swarm-config-guard";
|
|
26
28
|
import { can, isRbacEnabled, type PermissionVerb } from "../rbac";
|
|
27
29
|
import { SwarmConfigSchema } from "../types";
|
|
@@ -35,16 +37,9 @@ const MAX_ENV_PRESENCE_KEYS = 200;
|
|
|
35
37
|
const SECRETS_FORCE_MASK_NOTE =
|
|
36
38
|
" (secret values masked: this token cannot read secrets in this scope)";
|
|
37
39
|
|
|
38
|
-
// Config keys the API server owns for its own use and must NEVER hand out over
|
|
39
|
-
// HTTP — workers/entrypoints read config to build their env, and the agent-fs
|
|
40
|
-
// bootstrap admin key would let a compromised worker administer the shared org.
|
|
41
|
-
// The API materializes these into its own process.env at boot via
|
|
42
|
-
// getInjectableGlobalConfigs, so no HTTP consumer legitimately needs them.
|
|
43
|
-
const API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY", "SLACK_SIGNING_SECRET"]);
|
|
44
|
-
|
|
45
40
|
function stripApiOnlyKeys<T extends { key: string }>(configs: T[]): T[] {
|
|
46
41
|
return configs.filter(
|
|
47
|
-
(config) => !
|
|
42
|
+
(config) => !isApiOnlyConfigKey(config.key) && !isInternalConfigKey(config.key),
|
|
48
43
|
);
|
|
49
44
|
}
|
|
50
45
|
|
|
@@ -116,6 +111,28 @@ export async function ensureConfigAdmin(
|
|
|
116
111
|
req: IncomingMessage,
|
|
117
112
|
res: ServerResponse,
|
|
118
113
|
verb: Extract<PermissionVerb, "config.write.any" | "config.delete.any">,
|
|
114
|
+
): Promise<boolean> {
|
|
115
|
+
return ensureHttpPermission(
|
|
116
|
+
req,
|
|
117
|
+
res,
|
|
118
|
+
verb,
|
|
119
|
+
verb === "config.write.any"
|
|
120
|
+
? "Writing swarm config requires the lead agent"
|
|
121
|
+
: "Deleting swarm config requires the lead agent",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The general form of `ensureConfigAdmin`: operators and users pass, guests
|
|
127
|
+
* get a plain 403, and an agent principal (session identity first, then the
|
|
128
|
+
* legacy X-Agent-ID header) must hold `verb`. On denial it writes a 403 with
|
|
129
|
+
* `forbiddenMessage` and returns false.
|
|
130
|
+
*/
|
|
131
|
+
export async function ensureHttpPermission(
|
|
132
|
+
req: IncomingMessage,
|
|
133
|
+
res: ServerResponse,
|
|
134
|
+
verb: PermissionVerb,
|
|
135
|
+
forbiddenMessage: string,
|
|
119
136
|
): Promise<boolean> {
|
|
120
137
|
const auth = getRequestAuth(req);
|
|
121
138
|
if (auth?.kind === "operator" || auth?.kind === "user") return true;
|
|
@@ -132,13 +149,7 @@ export async function ensureConfigAdmin(
|
|
|
132
149
|
source: "http",
|
|
133
150
|
});
|
|
134
151
|
if (!decision.allow) {
|
|
135
|
-
jsonError(
|
|
136
|
-
res,
|
|
137
|
-
verb === "config.write.any"
|
|
138
|
-
? "Writing swarm config requires the lead agent"
|
|
139
|
-
: "Deleting swarm config requires the lead agent",
|
|
140
|
-
403,
|
|
141
|
-
);
|
|
152
|
+
jsonError(res, forbiddenMessage, 403);
|
|
142
153
|
return false;
|
|
143
154
|
}
|
|
144
155
|
return true;
|
|
@@ -276,6 +287,12 @@ const upsertConfig = route({
|
|
|
276
287
|
isSecret: z.boolean().optional(),
|
|
277
288
|
envPath: z.string().nullish(),
|
|
278
289
|
description: z.string().nullish(),
|
|
290
|
+
exportToEnv: z
|
|
291
|
+
.boolean()
|
|
292
|
+
.optional()
|
|
293
|
+
.describe(
|
|
294
|
+
"Export this row into agent process env (default true on create, false for a few server-side secrets; omitted on update keeps the stored flag). Rejected as true for keys only the API server reads.",
|
|
295
|
+
),
|
|
279
296
|
}),
|
|
280
297
|
responses: {
|
|
281
298
|
200: { description: "Config entry upserted", schema: SwarmConfigSchema },
|
|
@@ -442,7 +459,7 @@ export async function handleConfig(
|
|
|
442
459
|
const parsed = await upsertConfig.parse(req, res, pathSegments, queryParams);
|
|
443
460
|
if (!parsed) return true;
|
|
444
461
|
if (!(await ensureConfigAdmin(req, res, "config.write.any"))) return true;
|
|
445
|
-
const { scope, scopeId, key, value, isSecret, envPath, description } = parsed.body;
|
|
462
|
+
const { scope, scopeId, key, value, isSecret, envPath, description, exportToEnv } = parsed.body;
|
|
446
463
|
|
|
447
464
|
if (scope === "global" && scopeId) {
|
|
448
465
|
jsonError(res, "Global scope must not have scopeId", 400);
|
|
@@ -464,7 +481,8 @@ export async function handleConfig(
|
|
|
464
481
|
return true;
|
|
465
482
|
}
|
|
466
483
|
|
|
467
|
-
const validationError =
|
|
484
|
+
const validationError =
|
|
485
|
+
validateConfigValue(key, value) ?? validateExportToEnv(key, exportToEnv);
|
|
468
486
|
if (validationError) {
|
|
469
487
|
jsonError(res, validationError, 400);
|
|
470
488
|
return true;
|
|
@@ -480,6 +498,7 @@ export async function handleConfig(
|
|
|
480
498
|
isSecret: isSecret || false,
|
|
481
499
|
envPath: envPath || null,
|
|
482
500
|
description: description || null,
|
|
501
|
+
exportToEnv,
|
|
483
502
|
});
|
|
484
503
|
// Auto-reload integrations when a global-scoped config changes so callers
|
|
485
504
|
// (CLI, automation, dashboard) don't have to remember to hit /reload.
|
package/src/http/core.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { getUserGrant } from "../be/rbac-roles";
|
|
|
29
29
|
import { syncTelemetryEnabled } from "../be/telemetry-enable";
|
|
30
30
|
import { initGitHub, resetGitHub } from "../github";
|
|
31
31
|
import { initGitLab, isGitLabEnabled, resetGitLab } from "../gitlab";
|
|
32
|
+
import { restartChatSdkIfConfigChanged } from "../integrations/chat-sdk/runtime";
|
|
32
33
|
import { initJira, resetJira } from "../jira";
|
|
33
34
|
import { initLinear, resetLinear } from "../linear";
|
|
34
35
|
import { can, decideAdmission, isRbacEnabled } from "../rbac";
|
|
@@ -243,6 +244,9 @@ async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigRe
|
|
|
243
244
|
|
|
244
245
|
if (await startSlackApp()) integrations.push("slack");
|
|
245
246
|
|
|
247
|
+
// After Slack: the Chat SDK Slack clash check reads SLACK_DISABLE.
|
|
248
|
+
if (await restartChatSdkIfConfigChanged()) integrations.push("chat-sdk");
|
|
249
|
+
|
|
246
250
|
return {
|
|
247
251
|
configsLoaded: updated.length,
|
|
248
252
|
keysUpdated: updated,
|
package/src/http/index.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
import { initGitHub } from "../github";
|
|
42
42
|
import { initGitLab } from "../gitlab";
|
|
43
43
|
import { stopHeartbeat } from "../heartbeat";
|
|
44
|
+
import { startChatSdk, stopChatSdk } from "../integrations/chat-sdk/runtime";
|
|
44
45
|
import { initJira } from "../jira";
|
|
45
46
|
import { initLinear } from "../linear";
|
|
46
47
|
import {
|
|
@@ -81,7 +82,6 @@ import { handleConfig } from "./config";
|
|
|
81
82
|
import { handleContext } from "./context";
|
|
82
83
|
import { handleCore, loadGlobalConfigsIntoEnv } from "./core";
|
|
83
84
|
import { handleDbQuery } from "./db-query";
|
|
84
|
-
import { handleEcosystem } from "./ecosystem";
|
|
85
85
|
import { handleEvents } from "./events";
|
|
86
86
|
import { handleExtensions } from "./extensions";
|
|
87
87
|
import { handleFavorites } from "./favorites";
|
|
@@ -345,7 +345,6 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
345
345
|
() => handleAgentRegister(req, res, pathSegments, myAgentId),
|
|
346
346
|
() => handlePoll(req, res, pathSegments, queryParams, myAgentId),
|
|
347
347
|
() => handleSessionData(req, res, pathSegments, queryParams, myAgentId),
|
|
348
|
-
() => handleEcosystem(req, res, pathSegments, myAgentId),
|
|
349
348
|
() => handleTrackers(req, res, pathSegments),
|
|
350
349
|
() => handleWebhooks(req, res, pathSegments),
|
|
351
350
|
() => handleAgentsRest(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -503,6 +502,9 @@ async function shutdown() {
|
|
|
503
502
|
// Stop Slack bot
|
|
504
503
|
await stopSlackApp();
|
|
505
504
|
|
|
505
|
+
// Stop Chat SDK adapters and drain their background handlers before the DB closes
|
|
506
|
+
await stopChatSdk();
|
|
507
|
+
|
|
506
508
|
// Stop OAuth keepalive
|
|
507
509
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
508
510
|
const { stopOAuthKeepalive } = await import("../oauth/keepalive");
|
|
@@ -762,6 +764,16 @@ httpServer
|
|
|
762
764
|
console.error("[Slack] Failed to start, continuing boot without Slack:", err);
|
|
763
765
|
}
|
|
764
766
|
|
|
767
|
+
// Chat SDK channel: never the boot path, so not awaited. Adapter init
|
|
768
|
+
// makes network calls (Slack's auth.test retries for up to ~30 min), and
|
|
769
|
+
// the scheduler, heartbeat and supervisor below must start regardless.
|
|
770
|
+
void startChatSdk().catch((err: unknown) => {
|
|
771
|
+
console.error(
|
|
772
|
+
"[chat-sdk] Failed to start, continuing without Chat SDK:",
|
|
773
|
+
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
774
|
+
);
|
|
775
|
+
});
|
|
776
|
+
|
|
765
777
|
// Independent of workers, scheduler targets, and heartbeat agent tasks.
|
|
766
778
|
startQueueStallAlarm();
|
|
767
779
|
|
package/src/http/integrations.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import Anthropic from "@anthropic-ai/sdk";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import {
|
|
5
|
+
ChatSdkActionError,
|
|
6
|
+
ChatSdkStatusSchema,
|
|
7
|
+
getChatSdkStatus,
|
|
8
|
+
registerTelegramWebhook,
|
|
9
|
+
} from "../integrations/chat-sdk/status";
|
|
4
10
|
import { buildSlackManifest } from "../slack/manifest";
|
|
11
|
+
import { ensureHttpPermission } from "./config";
|
|
5
12
|
import { resolveConfigValue, resolveMcpBaseUrl } from "./config-values";
|
|
6
13
|
import { route } from "./route-def";
|
|
14
|
+
import { jsonError } from "./utils";
|
|
7
15
|
|
|
8
16
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
9
17
|
|
|
@@ -48,6 +56,20 @@ const McpUserConfigSchema = z.object({
|
|
|
48
56
|
|
|
49
57
|
const SlackManifestSchema = z.record(z.string(), z.unknown());
|
|
50
58
|
|
|
59
|
+
const TelegramRegisterWebhookSchema = z.object({
|
|
60
|
+
webhookUrl: z.string(),
|
|
61
|
+
webhookInfo: z.looseObject({
|
|
62
|
+
url: z.string(),
|
|
63
|
+
has_custom_certificate: z.boolean().optional(),
|
|
64
|
+
pending_update_count: z.number().optional(),
|
|
65
|
+
last_error_date: z.number().optional(),
|
|
66
|
+
last_error_message: z.string().optional(),
|
|
67
|
+
max_connections: z.number().optional(),
|
|
68
|
+
ip_address: z.string().optional(),
|
|
69
|
+
allowed_updates: z.array(z.string()).optional(),
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
|
|
51
73
|
// ─── Route Definition ────────────────────────────────────────────────────────
|
|
52
74
|
|
|
53
75
|
const claudeManagedTestRoute = route({
|
|
@@ -94,7 +116,38 @@ const slackManifestRoute = route({
|
|
|
94
116
|
},
|
|
95
117
|
});
|
|
96
118
|
|
|
97
|
-
|
|
119
|
+
const chatSdkStatusRoute = route({
|
|
120
|
+
method: "get",
|
|
121
|
+
path: "/api/integrations/chat/status",
|
|
122
|
+
pattern: ["api", "integrations", "chat", "status"],
|
|
123
|
+
summary:
|
|
124
|
+
"Chat SDK channel status: enabled flag, Slack clash, per-adapter health and webhook URL.",
|
|
125
|
+
tags: ["Integrations"],
|
|
126
|
+
responses: {
|
|
127
|
+
200: {
|
|
128
|
+
description:
|
|
129
|
+
"Per-adapter status. `health` is `disabled` for an adapter that is not running. Never contains secret values.",
|
|
130
|
+
schema: ChatSdkStatusSchema,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const chatSdkTelegramRegisterWebhookRoute = route({
|
|
136
|
+
method: "post",
|
|
137
|
+
path: "/api/integrations/chat/telegram/register-webhook",
|
|
138
|
+
pattern: ["api", "integrations", "chat", "telegram", "register-webhook"],
|
|
139
|
+
summary:
|
|
140
|
+
"Register this swarm's public Telegram webhook URL and secret token with the Bot API (setWebhook), then return getWebhookInfo.",
|
|
141
|
+
tags: ["Integrations"],
|
|
142
|
+
body: z.object({}).optional(),
|
|
143
|
+
responses: {
|
|
144
|
+
200: { description: "Webhook registered", schema: TelegramRegisterWebhookSchema },
|
|
145
|
+
400: { description: "Telegram is not configured for webhook mode" },
|
|
146
|
+
403: { description: "Only the lead agent or a dashboard user can register the webhook" },
|
|
147
|
+
502: { description: "The Telegram Bot API rejected the call" },
|
|
148
|
+
},
|
|
149
|
+
rbac: { permission: "integration.chat.manage" },
|
|
150
|
+
});
|
|
98
151
|
|
|
99
152
|
// ─── Public handler factory ──────────────────────────────────────────────────
|
|
100
153
|
|
|
@@ -122,6 +175,40 @@ export function createIntegrationsHandler(deps: TestConnectionDeps = {}) {
|
|
|
122
175
|
return true;
|
|
123
176
|
}
|
|
124
177
|
|
|
178
|
+
if (chatSdkStatusRoute.match(req.method, pathSegments)) {
|
|
179
|
+
chatSdkStatusRoute.respond(res, 200, await getChatSdkStatus());
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (chatSdkTelegramRegisterWebhookRoute.match(req.method, pathSegments)) {
|
|
184
|
+
const parsed = await chatSdkTelegramRegisterWebhookRoute.parse(
|
|
185
|
+
req,
|
|
186
|
+
res,
|
|
187
|
+
pathSegments,
|
|
188
|
+
queryParams,
|
|
189
|
+
);
|
|
190
|
+
if (!parsed) return true;
|
|
191
|
+
// Operators and dashboard users pass; agents need the verb (lead-only
|
|
192
|
+
// under the legacy policy).
|
|
193
|
+
if (
|
|
194
|
+
!(await ensureHttpPermission(
|
|
195
|
+
req,
|
|
196
|
+
res,
|
|
197
|
+
"integration.chat.manage",
|
|
198
|
+
"Registering the Telegram webhook requires the lead agent",
|
|
199
|
+
))
|
|
200
|
+
) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
chatSdkTelegramRegisterWebhookRoute.respond(res, 200, await registerTelegramWebhook());
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (!(error instanceof ChatSdkActionError)) throw error;
|
|
207
|
+
jsonError(res, error.message, error.status);
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
|
|
125
212
|
if (mcpUserConfigRoute.match(req.method, pathSegments)) {
|
|
126
213
|
const mcpBaseUrl = await resolveMcpBaseUrl();
|
|
127
214
|
mcpUserConfigRoute.respond(res, 200, { mcpBaseUrl, mcpUserUrl: `${mcpBaseUrl}/mcp-user` });
|
package/src/http/mcp-oauth.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
discoverProtectedResourceMetadata,
|
|
25
25
|
exchangeCodeForTokens,
|
|
26
26
|
isInvalidClientError,
|
|
27
|
+
mcpOAuthSsrfOptions,
|
|
27
28
|
normalizeTokenEndpointAuthMethod,
|
|
28
29
|
refreshMcpToken,
|
|
29
30
|
registerClient,
|
|
@@ -33,19 +34,11 @@ import {
|
|
|
33
34
|
} from "../oauth/mcp-wrapper";
|
|
34
35
|
import { McpAuthMethodSchema } from "../types";
|
|
35
36
|
import { getAppUrl, getPublicMcpBaseUrl } from "../utils/constants";
|
|
36
|
-
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
37
37
|
import { route } from "./route-def";
|
|
38
38
|
import { jsonError } from "./utils";
|
|
39
39
|
|
|
40
40
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
41
41
|
|
|
42
|
-
function ssrfOptions() {
|
|
43
|
-
return {
|
|
44
|
-
allowPrivateHosts: isEnvFlagEnabled("MCP_OAUTH_ALLOW_PRIVATE_HOSTS", false),
|
|
45
|
-
allowInsecure: process.env.NODE_ENV !== "production",
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
42
|
function callbackRedirectUri(): string {
|
|
50
43
|
// The callback route lives on the API server, so it must use the PUBLIC MCP
|
|
51
44
|
// base (externally reachable), not the dashboard APP_URL.
|
|
@@ -165,10 +158,10 @@ function manualClientFromToken(token: McpOAuthToken | null): OAuthClientForAutho
|
|
|
165
158
|
|
|
166
159
|
// The manual-client route validates these on write. Re-check before using the
|
|
167
160
|
// stored endpoints because /authorize redirects the browser to authorizeUrl.
|
|
168
|
-
assertUrlSafe(token.resourceUrl,
|
|
169
|
-
assertUrlSafe(token.authorizeUrl,
|
|
170
|
-
assertUrlSafe(token.tokenUrl,
|
|
171
|
-
if (token.revocationUrl) assertUrlSafe(token.revocationUrl,
|
|
161
|
+
assertUrlSafe(token.resourceUrl, mcpOAuthSsrfOptions());
|
|
162
|
+
assertUrlSafe(token.authorizeUrl, mcpOAuthSsrfOptions());
|
|
163
|
+
assertUrlSafe(token.tokenUrl, mcpOAuthSsrfOptions());
|
|
164
|
+
if (token.revocationUrl) assertUrlSafe(token.revocationUrl, mcpOAuthSsrfOptions());
|
|
172
165
|
|
|
173
166
|
return {
|
|
174
167
|
clientId: token.dcrClientId,
|
|
@@ -184,7 +177,7 @@ function manualClientFromToken(token: McpOAuthToken | null): OAuthClientForAutho
|
|
|
184
177
|
}
|
|
185
178
|
|
|
186
179
|
async function discoverForMcp(resourceUrl: string): Promise<DiscoveryResult | null> {
|
|
187
|
-
assertUrlSafe(resourceUrl,
|
|
180
|
+
assertUrlSafe(resourceUrl, mcpOAuthSsrfOptions());
|
|
188
181
|
|
|
189
182
|
const prmd = await discoverProtectedResourceMetadata(resourceUrl);
|
|
190
183
|
if (!prmd) return null;
|