@desplega.ai/agent-swarm 1.173.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
- package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
- package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
- package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
- package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
- package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
- package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
- package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
- package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
- package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
- package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
- package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
- package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
- package/dist/cli-skxvvpap.js +10140 -0
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
- package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
- package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +29 -45
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
- package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
- package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
- package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
- package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
- package/dist/index-9gw58n4k.js +193 -0
- package/dist/index-9j87mcm8.js +56305 -0
- package/dist/index-bsmcresk.js +79424 -0
- package/dist/index-mh85qhec.js +475 -0
- package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
- package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
- package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +523 -344
- package/package.json +10 -11
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +230 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +52 -7
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +12 -1
- package/src/be/model-validation.ts +5 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -8
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-skills/bundled-files.generated.json +0 -18
- package/src/be/seed-skills/index.ts +104 -3
- package/src/be/swarm-config-guard.ts +52 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +190 -54
- package/src/e2b/env.ts +5 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/all-routes.ts +0 -1
- package/src/http/config.ts +36 -17
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +14 -2
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +3 -3
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/scripts.ts +1 -0
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +61 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +159 -1
- package/src/http/x.ts +1 -0
- package/src/integrations/chat-sdk/adapters.ts +129 -0
- package/src/integrations/chat-sdk/attachments.ts +232 -0
- package/src/integrations/chat-sdk/background.ts +36 -0
- package/src/integrations/chat-sdk/commands.ts +194 -0
- package/src/integrations/chat-sdk/config.ts +198 -0
- package/src/integrations/chat-sdk/discord-api.ts +63 -0
- package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
- package/src/integrations/chat-sdk/handlers.ts +28 -0
- package/src/integrations/chat-sdk/inbound.ts +426 -0
- package/src/integrations/chat-sdk/limits.ts +48 -0
- package/src/integrations/chat-sdk/logger.ts +81 -0
- package/src/integrations/chat-sdk/outbound.ts +123 -0
- package/src/integrations/chat-sdk/relay.ts +172 -0
- package/src/integrations/chat-sdk/router.ts +169 -0
- package/src/integrations/chat-sdk/runtime.ts +351 -0
- package/src/integrations/chat-sdk/schema.ts +245 -0
- package/src/integrations/chat-sdk/state.ts +243 -0
- package/src/integrations/chat-sdk/status.ts +244 -0
- package/src/integrations/chat-sdk/steering.ts +58 -0
- package/src/integrations/chat-sdk/teams-origin.ts +77 -0
- package/src/integrations/chat-sdk/telegram-api.ts +56 -0
- package/src/integrations/chat-sdk/templates.ts +16 -0
- package/src/integrations/chat-sdk/test-hooks.ts +37 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
- package/src/integrations/chat-sdk/typing.ts +65 -0
- package/src/pages/version.ts +4 -5
- package/src/prompts/base-prompt.ts +111 -45
- package/src/providers/acp-target-catalog.ts +6 -0
- package/src/providers/acp-targets.ts +55 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +1 -1
- package/src/providers/claude-flag-settings.ts +17 -5
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +243 -26
- package/src/providers/opencode-swarm-plugin.ts +31 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +8 -2
- package/src/rbac/permissions.ts +21 -0
- package/src/scheduler/scheduler.ts +4 -2
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/sdk-allowlist.ts +8 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +27 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
- package/src/server.ts +52 -18
- package/src/slack/event-dedup.ts +1 -1
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +60 -1
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/base-prompt.test.ts +58 -54
- package/src/tests/chat-sdk-commands.test.ts +191 -0
- package/src/tests/chat-sdk-config.test.ts +334 -0
- package/src/tests/chat-sdk-discord-api.test.ts +68 -0
- package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
- package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
- package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
- package/src/tests/chat-sdk-harness.ts +146 -0
- package/src/tests/chat-sdk-inbound.test.ts +363 -0
- package/src/tests/chat-sdk-relay.test.ts +470 -0
- package/src/tests/chat-sdk-reload.test.ts +128 -0
- package/src/tests/chat-sdk-shutdown.test.ts +220 -0
- package/src/tests/chat-sdk-slack.test.ts +268 -0
- package/src/tests/chat-sdk-state.test.ts +163 -0
- package/src/tests/chat-sdk-status-route.test.ts +382 -0
- package/src/tests/chat-sdk-teams.test.ts +277 -0
- package/src/tests/chat-sdk-tools.test.ts +520 -0
- package/src/tests/chat-sdk-typing.test.ts +120 -0
- package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
- package/src/tests/claude-adapter-binary.test.ts +9 -4
- package/src/tests/claude-flag-settings.test.ts +11 -3
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
- package/src/tests/claude-thinking-tokens.test.ts +57 -0
- package/src/tests/claude-worker-parity.test.ts +2 -7
- package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
- package/src/tests/config-refs.test.ts +66 -0
- package/src/tests/context-key.test.ts +19 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +20 -0
- package/src/tests/db-task-write-contract.test.ts +10 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/openai-compatible-providers.test.ts +134 -0
- package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
- package/src/tests/opencode-adapter.test.ts +216 -2
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback.test.ts +1 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/rbac-engine.test.ts +11 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/scripts-http.test.ts +201 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +86 -0
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +46 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/swarm-config-export-to-env.test.ts +267 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
- package/src/tests/swarm-diff.test.ts +1 -1
- package/src/tests/task-model-effort-rerun.test.ts +164 -0
- package/src/tests/task-session-prompts.test.ts +132 -0
- package/src/tests/task-tool-manifest.test.ts +14 -0
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tool-annotations.test.ts +6 -6
- package/src/tests/ui-logs-parser.test.ts +43 -0
- package/src/tests/used-by-route.test.ts +6 -0
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/send-task.ts +24 -0
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/tool-config.ts +8 -6
- package/src/types.ts +12 -2
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +71 -2
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +8 -1
- package/templates/skills/apps/content.md +8 -1
- package/templates/skills/chat-sdk/config.json +14 -0
- package/templates/skills/chat-sdk/content.md +84 -0
- package/templates/skills/composio-gmail/content.md +7 -0
- package/templates/skills/composio-google-docs/content.md +1 -1
- package/templates/skills/email-domain-health/config.json +14 -0
- package/templates/skills/email-domain-health/content.md +131 -0
- package/templates/skills/kv-storage/content.md +2 -2
- package/templates/skills/pages/content.md +8 -10
- package/templates/skills/swarm-extensions/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +4 -2
- package/templates/skills/swarm-scripts/config.json +1 -1
- package/templates/skills/swarm-scripts/content.md +4 -2
- package/templates/skills/workflow-iterate/content.md +38 -0
- package/dist/artifact-eczsknc3.js +0 -3843
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/cli-tcc4wd5d.js +0 -56471
- package/dist/index-9ch1n761.js +0 -1102
- package/src/artifact-sdk/index.ts +0 -2
- package/src/artifact-sdk/localtunnel.d.ts +0 -20
- package/src/artifact-sdk/port.ts +0 -12
- package/src/artifact-sdk/server.ts +0 -183
- package/src/artifact-sdk/tunnel.ts +0 -19
- package/src/commands/artifact.ts +0 -249
- package/src/http/ecosystem.ts +0 -77
- package/src/tests/artifact-commands.test.ts +0 -92
- package/src/tests/artifact-sdk.test.ts +0 -793
- package/src/tests/register-service-security.test.ts +0 -194
- package/src/tools/list-services.ts +0 -107
- package/src/tools/register-service.ts +0 -177
- package/src/tools/unregister-service.ts +0 -73
- package/src/tools/update-service-status.ts +0 -96
- package/templates/skills/artifacts/config.json +0 -14
- package/templates/skills/artifacts/content.md +0 -344
- package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
- package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
- package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
- package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
package/src/utils/credentials.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
openAICompatibleProviders,
|
|
4
|
+
providerForKeyVar,
|
|
5
|
+
providerForModel,
|
|
6
|
+
providerKeyVars,
|
|
7
|
+
} from "@desplega/model-catalog";
|
|
2
8
|
import { deriveDefaultRoute, routeCredentialStatus } from "@desplega/model-routing";
|
|
3
9
|
import { type ModelFamily, modelFamilyOf, windowForModelFamily } from "./model-rate-limit-windows";
|
|
4
10
|
|
|
@@ -14,8 +20,27 @@ export const CREDENTIAL_POOL_VARS = [
|
|
|
14
20
|
"AMP_API_KEY",
|
|
15
21
|
"CURSOR_API_KEY",
|
|
16
22
|
"XAI_API_KEY",
|
|
23
|
+
// The canonical key var of every OpenAI-compatible registry provider
|
|
24
|
+
// (`@desplega/model-catalog`). Aliases are normalized into these by the
|
|
25
|
+
// runner (`normalizeProviderKeyAliases`), so they never pool.
|
|
26
|
+
...openAICompatibleProviders().map((p) => p.envVar),
|
|
17
27
|
] as const;
|
|
18
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Copy the first set alias of each registry provider into its canonical key
|
|
31
|
+
* var when the canonical var is empty (`HELMCODE_API_NAN` → `NAN_API_KEY`).
|
|
32
|
+
* The runner runs this once per task, so pooling, credential checks, pi's
|
|
33
|
+
* runtime key and the opencode key splice only read the canonical name. An
|
|
34
|
+
* explicit canonical value wins. Mutates `env`.
|
|
35
|
+
*/
|
|
36
|
+
export function normalizeProviderKeyAliases(env: Record<string, string | undefined>): void {
|
|
37
|
+
for (const provider of openAICompatibleProviders()) {
|
|
38
|
+
if (env[provider.envVar]?.trim()) continue;
|
|
39
|
+
const alias = provider.aliasEnvVars.find((name) => env[name]?.trim());
|
|
40
|
+
if (alias) env[provider.envVar] = env[alias];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
19
44
|
/**
|
|
20
45
|
* Which credential env vars are relevant for each harness provider. The
|
|
21
46
|
* runner uses this map to filter `CREDENTIAL_POOL_VARS` so a codex worker
|
|
@@ -76,6 +101,10 @@ export function getModelAwareCredentialVars(provider: string, model?: string): r
|
|
|
76
101
|
return model.startsWith("openrouter/") ? ["OPENROUTER_API_KEY"] : ["XAI_API_KEY"];
|
|
77
102
|
}
|
|
78
103
|
if (!SLASH_MODEL_PROVIDERS.has(provider) || !model) return base;
|
|
104
|
+
// OpenAI-compatible registry providers own their key: `nan/<id>` and
|
|
105
|
+
// `helmcode/<id>` never pool an OpenRouter or Anthropic key.
|
|
106
|
+
const direct = providerForModel(model);
|
|
107
|
+
if (direct) return [direct.envVar];
|
|
79
108
|
if (model.includes("/")) {
|
|
80
109
|
return base.filter((v) => v !== "OPENAI_API_KEY");
|
|
81
110
|
}
|
|
@@ -91,6 +120,8 @@ export function getModelAwareCredentialVars(provider: string, model?: string): r
|
|
|
91
120
|
* usage with the active worker's HARNESS_PROVIDER.
|
|
92
121
|
*/
|
|
93
122
|
export function deriveProviderFromKeyType(keyType: string): string {
|
|
123
|
+
// OpenAI-compatible registry keys (canonical or alias) default to pi.
|
|
124
|
+
if (providerForKeyVar(keyType)) return "pi";
|
|
94
125
|
switch (keyType) {
|
|
95
126
|
case "CLAUDE_CODE_OAUTH_TOKEN":
|
|
96
127
|
case "ANTHROPIC_API_KEY":
|
|
@@ -210,7 +241,7 @@ function isJsonObject(value: string): boolean {
|
|
|
210
241
|
* Falls back to random selection from all credentials if no available indices match.
|
|
211
242
|
* A JSON object value (the CODEX_OAUTH blob) is always one credential.
|
|
212
243
|
*/
|
|
213
|
-
function splitCredentialPool(value: string): string[] {
|
|
244
|
+
export function splitCredentialPool(value: string): string[] {
|
|
214
245
|
return isJsonObject(value)
|
|
215
246
|
? [value.trim()]
|
|
216
247
|
: value
|
|
@@ -311,21 +342,38 @@ export function validateClaudeCredentials(env: Record<string, string | undefined
|
|
|
311
342
|
return CLAUDE_CREDENTIAL_TYPE_BY_PROVIDER[route.provider] ?? route.provider;
|
|
312
343
|
}
|
|
313
344
|
|
|
345
|
+
/**
|
|
346
|
+
* One label per OpenAI-compatible registry provider for hints and errors:
|
|
347
|
+
* `NAN_API_KEY (or HELMCODE_API_NAN)`, `HELMCODE_API_KEY`.
|
|
348
|
+
*/
|
|
349
|
+
export function openAICompatibleKeyLabels(): string[] {
|
|
350
|
+
return openAICompatibleProviders().map((p) =>
|
|
351
|
+
p.aliasEnvVars.length > 0 ? `${p.envVar} (or ${p.aliasEnvVars.join(" or ")})` : p.envVar,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
314
355
|
/**
|
|
315
356
|
* Validate that at least one opencode credential is available.
|
|
316
|
-
* Priority: OPENROUTER_API_KEY → ANTHROPIC_API_KEY → OPENAI_API_KEY →
|
|
357
|
+
* Priority: OPENROUTER_API_KEY → ANTHROPIC_API_KEY → OPENAI_API_KEY → each
|
|
358
|
+
* OpenAI-compatible registry provider's key (canonical or alias, e.g.
|
|
359
|
+
* NAN_API_KEY or HELMCODE_API_NAN → `nan_api_key`) →
|
|
360
|
+
* ~/.local/share/opencode/auth.json.
|
|
317
361
|
* Returns the credential type found, or throws if none are available.
|
|
318
362
|
*/
|
|
319
363
|
export function validateOpencodeCredentials(
|
|
320
364
|
env: Record<string, string | undefined>,
|
|
321
|
-
): "openrouter_api_key" | "anthropic_api_key" | "openai_api_key" | "auth_file" {
|
|
365
|
+
): "openrouter_api_key" | "anthropic_api_key" | "openai_api_key" | Lowercase<string> | "auth_file" {
|
|
322
366
|
if (env.OPENROUTER_API_KEY) return "openrouter_api_key";
|
|
323
367
|
if (env.ANTHROPIC_API_KEY) return "anthropic_api_key";
|
|
324
368
|
if (env.OPENAI_API_KEY) return "openai_api_key";
|
|
369
|
+
const direct = openAICompatibleProviders().find((p) => providerKeyVars(p).some((n) => env[n]));
|
|
370
|
+
if (direct) return direct.envVar.toLowerCase() as Lowercase<string>;
|
|
325
371
|
const authFile = `${process.env.HOME ?? "/root"}/.local/share/opencode/auth.json`;
|
|
326
372
|
if (existsSync(authFile)) return "auth_file";
|
|
327
373
|
throw new Error(
|
|
328
|
-
|
|
374
|
+
`No opencode credentials found. Set OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, ${openAICompatibleKeyLabels()
|
|
375
|
+
.map((label) => `${label}, `)
|
|
376
|
+
.join("")}or provide ~/.local/share/opencode/auth.json.`,
|
|
329
377
|
);
|
|
330
378
|
}
|
|
331
379
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { openAICompatibleKeyVars } from "@desplega/model-catalog";
|
|
1
2
|
import { type ProviderName, ProviderNameSchema } from "../types";
|
|
2
3
|
|
|
3
4
|
const SUPPORTED_PROVIDERS = ProviderNameSchema.options;
|
|
@@ -10,23 +11,31 @@ function hasEnvValue(
|
|
|
10
11
|
return !!(resolvedEnv[key]?.trim() || fallbackEnv[key]?.trim());
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* LLM keys that make "pi" the default harness when no Claude credential is
|
|
16
|
+
* set: OpenRouter plus every OpenAI-compatible registry key (canonical or alias).
|
|
17
|
+
*/
|
|
18
|
+
const PI_DEFAULT_KEY_VARS = ["OPENROUTER_API_KEY", ...openAICompatibleKeyVars()];
|
|
19
|
+
|
|
13
20
|
/**
|
|
14
21
|
* Credential-aware default for when HARNESS_PROVIDER is unset or invalid.
|
|
15
22
|
* Unconditionally defaulting to "claude" leaves an OpenRouter-only swarm —
|
|
16
23
|
* e.g. every auto-deployed swarm, which is provisioned with
|
|
17
24
|
* OPENROUTER_API_KEY and NEVER an Anthropic credential — unable to
|
|
18
|
-
* authenticate at all. Prefer "pi" when an OpenRouter key
|
|
19
|
-
*
|
|
25
|
+
* authenticate at all. Prefer "pi" when an OpenRouter key or an
|
|
26
|
+
* OpenAI-compatible registry key (NaN, Helmcode; canonical or alias) is
|
|
27
|
+
* present and no Claude credential is; otherwise keep the original "claude"
|
|
28
|
+
* default.
|
|
20
29
|
*/
|
|
21
30
|
function credentialAwareDefault(
|
|
22
31
|
resolvedEnv: Record<string, string | undefined>,
|
|
23
32
|
fallbackEnv: Record<string, string | undefined>,
|
|
24
33
|
): ProviderName {
|
|
25
|
-
const
|
|
34
|
+
const hasPiKey = PI_DEFAULT_KEY_VARS.some((key) => hasEnvValue(key, resolvedEnv, fallbackEnv));
|
|
26
35
|
const hasClaudeCredential =
|
|
27
36
|
hasEnvValue("ANTHROPIC_API_KEY", resolvedEnv, fallbackEnv) ||
|
|
28
37
|
hasEnvValue("CLAUDE_CODE_OAUTH_TOKEN", resolvedEnv, fallbackEnv);
|
|
29
|
-
return
|
|
38
|
+
return hasPiKey && !hasClaudeCredential ? "pi" : "claude";
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
/**
|
|
@@ -36,8 +45,8 @@ function credentialAwareDefault(
|
|
|
36
45
|
* 1. `resolvedEnv.HARNESS_PROVIDER` — value coming from `swarm_config`
|
|
37
46
|
* (overlay produced by `fetchResolvedEnv`, scoped repo > agent > global).
|
|
38
47
|
* 2. `fallbackEnv.HARNESS_PROVIDER` — raw `process.env`.
|
|
39
|
-
* 3. Credential-aware default — "pi" when an OpenRouter
|
|
40
|
-
* no Anthropic credential is (see `credentialAwareDefault`), else
|
|
48
|
+
* 3. Credential-aware default — "pi" when an OpenRouter or registry
|
|
49
|
+
* provider key is present and no Anthropic credential is (see `credentialAwareDefault`), else
|
|
41
50
|
* `"claude"`.
|
|
42
51
|
*
|
|
43
52
|
* Invalid values (anything outside `ProviderNameSchema`) log a warning and
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* NO database imports (worker-safe). Every lookup is synchronous; the HTTP
|
|
18
18
|
* refresh is TTL-cached and never throws, so hot paths stay cheap.
|
|
19
19
|
*/
|
|
20
|
+
import { type CatalogChatRow, withRegistryCatalogSections } from "@desplega/model-catalog";
|
|
20
21
|
import modelsDevSnapshot from "../be/modelsdev-cache.json";
|
|
21
22
|
|
|
22
23
|
export interface RuntimeCatalogModel {
|
|
@@ -38,7 +39,16 @@ export interface RuntimeCatalogProvider {
|
|
|
38
39
|
|
|
39
40
|
export type RuntimeCatalog = Record<string, RuntimeCatalogProvider | undefined>;
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
// Plus the registry-synthesized sections models.dev does not list (`helmcode`).
|
|
43
|
+
const SNAPSHOT = withRegistryCatalogSections(modelsDevSnapshot as RuntimeCatalog);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* One section's full snapshot rows (output limit, modalities, tool calling),
|
|
47
|
+
* which the slim live catalog drops. Harness config blocks read these.
|
|
48
|
+
*/
|
|
49
|
+
export function snapshotCatalogRows(provider: string): Record<string, CatalogChatRow> {
|
|
50
|
+
return (SNAPSHOT[provider]?.models ?? {}) as Record<string, CatalogChatRow>;
|
|
51
|
+
}
|
|
42
52
|
|
|
43
53
|
/** Default freshness window for the worker-side HTTP pull. */
|
|
44
54
|
export const RUNTIME_CATALOG_TTL_MS = 10 * 60_000;
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* API↔worker DB boundary (scripts/check-db-boundary.sh).
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import { openAICompatibleKeyVars } from "@desplega/model-catalog";
|
|
24
25
|
import { GITLEAKS_RULES } from "./secret-rules.generated";
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -35,9 +36,13 @@ import { GITLEAKS_RULES } from "./secret-rules.generated";
|
|
|
35
36
|
* 5, the vendored gitleaks rule set, plus hand-written Resend, Google OAuth,
|
|
36
37
|
* Discord webhook, xAI and bare Telegram shapes in pass 2; v6 = pass 5 resumes
|
|
37
38
|
* after the secret, not the match, so adjacent secrets that share a delimiter
|
|
38
|
-
* both redact, and sourcegraph-access-token drops its bare 40-hex branch
|
|
39
|
+
* both redact, and sourcegraph-access-token drops its bare 40-hex branch;
|
|
40
|
+
* v7 = the OpenAI-compatible registry key vars (`NAN_API_KEY`,
|
|
41
|
+
* `HELMCODE_API_KEY`, and the `HELMCODE_API_NAN` alias no suffix matches).
|
|
42
|
+
* A new registry entry adds key vars, so it is a rule change too
|
|
43
|
+
* (`secret-scrubber.test.ts` pins the list to this number).
|
|
39
44
|
*/
|
|
40
|
-
export const SCRUBBER_RULES_VERSION =
|
|
45
|
+
export const SCRUBBER_RULES_VERSION = 8;
|
|
41
46
|
|
|
42
47
|
/** Env-var names that are always considered secrets, even without suffix hints. */
|
|
43
48
|
const SENSITIVE_KEY_EXACT = new Set<string>([
|
|
@@ -52,6 +57,9 @@ const SENSITIVE_KEY_EXACT = new Set<string>([
|
|
|
52
57
|
"ANTHROPIC_API_KEY",
|
|
53
58
|
"OPENAI_API_KEY",
|
|
54
59
|
"OPENROUTER_API_KEY",
|
|
60
|
+
// OpenAI-compatible registry key vars, canonical and alias. An alias such
|
|
61
|
+
// as `HELMCODE_API_NAN` matches no SENSITIVE_KEY_SUFFIXES entry.
|
|
62
|
+
...openAICompatibleKeyVars(),
|
|
55
63
|
"SLACK_BOT_TOKEN",
|
|
56
64
|
"SLACK_SIGNING_SECRET",
|
|
57
65
|
"SLACK_CLIENT_SECRET",
|
|
@@ -1005,6 +1013,52 @@ export function scrubObject<T>(value: T, seen = new WeakSet<object>()): T {
|
|
|
1005
1013
|
return out as T;
|
|
1006
1014
|
}
|
|
1007
1015
|
|
|
1016
|
+
export type ValueScrubber = {
|
|
1017
|
+
text(text: string): string;
|
|
1018
|
+
object<T>(value: T): T;
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Redact exact occurrences of the given values, at any length, in one output
|
|
1023
|
+
* boundary that knows which secrets it handed out (a script run's secret
|
|
1024
|
+
* config). The global passes above keep their MIN_VALUE_LENGTH floor; this
|
|
1025
|
+
* one never registers anything swarm-wide. All forms match in a single regex
|
|
1026
|
+
* pass, longest first, so one value's marker is never rewritten by another
|
|
1027
|
+
* value's pass. Object keys are redacted too.
|
|
1028
|
+
*/
|
|
1029
|
+
export function createValueScrubber(
|
|
1030
|
+
entries: Array<{ value: string; name: string }>,
|
|
1031
|
+
): ValueScrubber {
|
|
1032
|
+
const names = new Map<string, string>();
|
|
1033
|
+
for (const { value, name } of entries) {
|
|
1034
|
+
if (!value) continue;
|
|
1035
|
+
for (const form of escapedForms(value)) if (!names.has(form)) names.set(form, name);
|
|
1036
|
+
}
|
|
1037
|
+
if (names.size === 0) return { text: (text) => text, object: (value) => value };
|
|
1038
|
+
|
|
1039
|
+
const pattern = [...names.keys()]
|
|
1040
|
+
.sort((a, b) => b.length - a.length)
|
|
1041
|
+
.map(escapeRegExp)
|
|
1042
|
+
.join("|");
|
|
1043
|
+
const re = new RegExp(pattern, "g");
|
|
1044
|
+
const text = (input: string) =>
|
|
1045
|
+
input.replace(re, (match) => `[REDACTED:${names.get(match) ?? "secret"}]`);
|
|
1046
|
+
const object = <T>(value: T, seen = new WeakSet<object>()): T => {
|
|
1047
|
+
if (typeof value === "string") return text(value) as T;
|
|
1048
|
+
if (value === null || typeof value !== "object") return value;
|
|
1049
|
+
if (seen.has(value)) return "[Circular]" as T;
|
|
1050
|
+
seen.add(value);
|
|
1051
|
+
if (Array.isArray(value)) return value.map((item) => object(item, seen)) as T;
|
|
1052
|
+
return Object.fromEntries(
|
|
1053
|
+
Object.entries(value as Record<string, unknown>).map(([key, child]) => [
|
|
1054
|
+
text(key),
|
|
1055
|
+
object(child, seen),
|
|
1056
|
+
]),
|
|
1057
|
+
) as T;
|
|
1058
|
+
};
|
|
1059
|
+
return { text, object: (value) => object(value) };
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1008
1062
|
/**
|
|
1009
1063
|
* Force the env-value cache to rebuild on the next scrub call. Callers should
|
|
1010
1064
|
* invoke this whenever the swarm_config is reloaded (`/internal/reload-config`
|
|
@@ -1031,6 +1085,21 @@ export function registerVolatileSecret(value: string, name: string): void {
|
|
|
1031
1085
|
}
|
|
1032
1086
|
}
|
|
1033
1087
|
|
|
1088
|
+
/**
|
|
1089
|
+
* Register the sensitive values of an env map that is not `process.env`
|
|
1090
|
+
* (a runner's per-task env, an adapter's session env). A key picked from a
|
|
1091
|
+
* credential pool, or a row admitted after boot, lives only there, so the
|
|
1092
|
+
* env pass never sees it. Comma-separated pools register each member too.
|
|
1093
|
+
*/
|
|
1094
|
+
export function registerEnvSecrets(env: Record<string, string | undefined>): void {
|
|
1095
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1096
|
+
if (!value || !isSensitiveKey(key)) continue;
|
|
1097
|
+
registerVolatileSecret(value, key);
|
|
1098
|
+
if (!value.includes(",")) continue;
|
|
1099
|
+
for (const member of value.split(",")) registerVolatileSecret(member.trim(), key);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1034
1103
|
/** Remove a short-lived secret and its encoded forms after credential revocation. */
|
|
1035
1104
|
export function unregisterVolatileSecret(name: string): void {
|
|
1036
1105
|
for (const [value, registeredName] of volatileSecrets) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { chatThreadOfContextKey } from "../tasks/context-key";
|
|
2
3
|
import { ALL_TOOLS } from "../tools/tool-config";
|
|
3
4
|
|
|
4
5
|
const toolNames = z
|
|
@@ -15,9 +16,22 @@ export function parseTaskToolManifest(value: string) {
|
|
|
15
16
|
return taskToolManifestSchema.parse(JSON.parse(value));
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
/** Tools that act on the chat thread of the calling task; useless on any other task. */
|
|
20
|
+
export const CHAT_TASK_ONLY_TOOLS: readonly string[] = ["chat-reply"];
|
|
21
|
+
|
|
22
|
+
/** True when the task came from a Chat SDK thread (contextKey family `chat-sdk`). */
|
|
23
|
+
export function isChatSdkTask(task: { contextKey?: string | null }): boolean {
|
|
24
|
+
return chatThreadOfContextKey(task.contextKey) !== null;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
export function selectTaskTools(
|
|
19
28
|
manifest: z.infer<typeof taskToolManifestSchema>,
|
|
20
|
-
task: {
|
|
29
|
+
task: {
|
|
30
|
+
taskType?: string;
|
|
31
|
+
scheduleId?: string;
|
|
32
|
+
slackChannelId?: string;
|
|
33
|
+
contextKey?: string | null;
|
|
34
|
+
},
|
|
21
35
|
): string[] {
|
|
22
36
|
const fromSchedule =
|
|
23
37
|
task.scheduleId && Object.hasOwn(manifest.schedules ?? {}, task.scheduleId)
|
|
@@ -27,7 +41,10 @@ export function selectTaskTools(
|
|
|
27
41
|
task.taskType && Object.hasOwn(manifest.taskTypes ?? {}, task.taskType)
|
|
28
42
|
? manifest.taskTypes?.[task.taskType]
|
|
29
43
|
: undefined;
|
|
44
|
+
const chatTask = isChatSdkTask(task);
|
|
30
45
|
return [...new Set(fromSchedule ?? fromType ?? [])].filter(
|
|
31
|
-
(name) =>
|
|
46
|
+
(name) =>
|
|
47
|
+
(!name.startsWith("slack-") || Boolean(task.slackChannelId)) &&
|
|
48
|
+
(chatTask || !CHAT_TASK_ONLY_TOOLS.includes(name)),
|
|
32
49
|
);
|
|
33
50
|
}
|
|
@@ -127,6 +127,7 @@ export class SwarmScriptExecutor extends BaseExecutor<
|
|
|
127
127
|
mcpBaseUrl: getMcpBaseUrl(),
|
|
128
128
|
egressSecrets: credentials.egressSecrets,
|
|
129
129
|
failedBindings: credentials.failedBindings,
|
|
130
|
+
userConfig: credentials.userConfig,
|
|
130
131
|
apiConnections: getScriptApiConnectionDescriptors({ agentId: agentId ?? undefined }),
|
|
131
132
|
mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agentId ?? undefined }),
|
|
132
133
|
timeoutMs: config.timeoutMs,
|
|
@@ -84,7 +84,7 @@ agent-fs share-create thoughts/<agent-id>/qa/<topic>-screenshots/<name>.png --js
|
|
|
84
84
|
- In `store-progress`, list the upload in the `attachments` field with `kind: "agent-fs"` and the path, and paste the `share-create` link for people who need to open the file.
|
|
85
85
|
- If `agent-fs auth whoami` fails, report the local path, say the upload was skipped, and continue.
|
|
86
86
|
|
|
87
|
-
The `
|
|
87
|
+
The `agent-fs` skill holds the full agent-fs recipe.
|
|
88
88
|
|
|
89
89
|
## Gotchas
|
|
90
90
|
|
|
@@ -79,7 +79,7 @@ agent-fs share-create thoughts/<agent-id>/qa/<topic>-screenshots/<name>.png --js
|
|
|
79
79
|
- In `store-progress`, list the upload in the `attachments` field with `kind: "agent-fs"` and the path, and paste the `share-create` link for people who need to open the file.
|
|
80
80
|
- If `agent-fs auth whoami` fails, report the local path, say the upload was skipped, and continue.
|
|
81
81
|
|
|
82
|
-
The `
|
|
82
|
+
The `agent-fs` skill holds the full agent-fs recipe.
|
|
83
83
|
|
|
84
84
|
## Gotchas
|
|
85
85
|
|
|
@@ -5,7 +5,7 @@ description: Build and iterate on schema-backed Swarm Apps with live models, nam
|
|
|
5
5
|
|
|
6
6
|
# Swarm Apps
|
|
7
7
|
|
|
8
|
-
Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at `/apps/<id>`. Use an app when people need to view and change live records; use `create_page` for a static snapshot
|
|
8
|
+
Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at `/apps/<id>`. Use an app when people need to view and change live records; use `create_page` for a static snapshot.
|
|
9
9
|
|
|
10
10
|
> **Capability gate**: app tools are available when the server's `CAPABILITIES` includes `pages` (for example `CAPABILITIES=core,task-pool,pages`). There is no separate `apps` capability. If these tools are absent from your MCP list, this is why.
|
|
11
11
|
|
|
@@ -740,3 +740,10 @@ Create this with `app-upsert`:
|
|
|
740
740
|
```
|
|
741
741
|
|
|
742
742
|
The tool returns `{ appId, url: "/apps/<id>" }`. Open that URL to verify the live UI, then continue with the read-patch-validate loop above.
|
|
743
|
+
|
|
744
|
+
## View and screenshot a live app
|
|
745
|
+
|
|
746
|
+
- An app renders at `<dashboard>/apps/<id>`. Two view modes are set by query string: `?mode=full` (full-viewport overlay with a slim header) and `?mode=chromeless` (only the rendered page, the embed surface). `app.navigate` keeps only `?mode`.
|
|
747
|
+
- Logged-in browser: a fresh dashboard has no connection and redirects to /setup. Seed the localStorage key `agent-swarm-connections` with `{connections:[{id,name,apiUrl,apiKey}],activeId}`. Build the value from an env var and pipe it to `agent-browser eval --stdin`, so the key never reaches a command line. Reload, then pick a user in the "Who are you?" dialog.
|
|
748
|
+
- Apps scroll inside an inner canvas, so a full-page screenshot captures only the viewport. Scroll the canvas or raise the viewport height. For mobile, set a 375px-wide viewport before the screenshot.
|
|
749
|
+
- App definitions: `GET /api/apps` and `GET /api/apps/<id>` with the API key as Bearer.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Swarm Apps
|
|
2
2
|
|
|
3
|
-
Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at `/apps/<id>`. Use an app when people need to view and change live records; use `create_page` for a static snapshot
|
|
3
|
+
Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at `/apps/<id>`. Use an app when people need to view and change live records; use `create_page` for a static snapshot.
|
|
4
4
|
|
|
5
5
|
> **Capability gate**: app tools are available when the server's `CAPABILITIES` includes `pages` (for example `CAPABILITIES=core,task-pool,pages`). There is no separate `apps` capability. If these tools are absent from your MCP list, this is why.
|
|
6
6
|
|
|
@@ -735,3 +735,10 @@ Create this with `app-upsert`:
|
|
|
735
735
|
```
|
|
736
736
|
|
|
737
737
|
The tool returns `{ appId, url: "/apps/<id>" }`. Open that URL to verify the live UI, then continue with the read-patch-validate loop above.
|
|
738
|
+
|
|
739
|
+
## View and screenshot a live app
|
|
740
|
+
|
|
741
|
+
- An app renders at `<dashboard>/apps/<id>`. Two view modes are set by query string: `?mode=full` (full-viewport overlay with a slim header) and `?mode=chromeless` (only the rendered page, the embed surface). `app.navigate` keeps only `?mode`.
|
|
742
|
+
- Logged-in browser: a fresh dashboard has no connection and redirects to /setup. Seed the localStorage key `agent-swarm-connections` with `{connections:[{id,name,apiUrl,apiKey}],activeId}`. Build the value from an env var and pipe it to `agent-browser eval --stdin`, so the key never reaches a command line. Reload, then pick a user in the "Who are you?" dialog.
|
|
743
|
+
- Apps scroll inside an inner canvas, so a full-page screenshot captures only the viewport. Scroll the canvas or raise the viewport height. For mobile, set a 375px-wide viewport before the screenshot.
|
|
744
|
+
- App definitions: `GET /api/apps` and `GET /api/apps/<id>` with the API key as Bearer.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "skill",
|
|
3
|
+
"name": "chat-sdk",
|
|
4
|
+
"displayName": "Chat SDK",
|
|
5
|
+
"slug": "chat-sdk",
|
|
6
|
+
"title": "Chat SDK",
|
|
7
|
+
"description": "Chat threads from Telegram, Slack (Chat SDK), Discord, and Teams. Use when a task carries a task:chat-sdk contextKey or a user asked through Telegram, Slack, Discord, or Teams. Covers what the swarm posts for you, `chat-reply`, markdown and length per platform, attachments, `chat-read-thread` limits, and channel routes with `register-chat-channel`.",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"category": "skills",
|
|
10
|
+
"placeholders": [],
|
|
11
|
+
"runAllSeedersCandidate": true,
|
|
12
|
+
"systemDefault": false,
|
|
13
|
+
"tags": ["chat", "telegram", "discord", "teams", "slack", "communication"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Chat SDK
|
|
2
|
+
|
|
3
|
+
The swarm receives messages from Telegram, Slack, Discord, and Microsoft Teams through the Chat SDK channel. A person mentions the bot or sends it a direct message, and the swarm creates a task for it. This skill tells you how to work on such a task.
|
|
4
|
+
|
|
5
|
+
## How you know you are on a chat task
|
|
6
|
+
|
|
7
|
+
- The task has `source: "chat"`, or it is a lead follow-up of one.
|
|
8
|
+
- The task `contextKey` starts with `task:chat-sdk:`. The full form is `task:chat-sdk:<adapter>:<encoded thread id>`, for example `task:chat-sdk:telegram:555001`.
|
|
9
|
+
- The task text holds the user's message. Later replies in the same thread arrive as follow-up tasks with the same `contextKey`, or as steering messages on your running task.
|
|
10
|
+
- The user has four slash commands that never reach you as a task: `/help`, `/status`, `/cancel` and `/reset`. `/cancel` ends the thread's active tasks, yours included, with status `cancelled`. After `/reset` the next task has no parent task and no earlier thread context, so treat it as a new conversation.
|
|
11
|
+
|
|
12
|
+
Use `get-task-details` to read the `contextKey` when you are not sure.
|
|
13
|
+
|
|
14
|
+
## Reply with `chat-reply`
|
|
15
|
+
|
|
16
|
+
`chat-reply { text }` posts to the thread your current task came from. It finds the thread from your task, so you do not pass ids.
|
|
17
|
+
|
|
18
|
+
- Use it to ask the user a question you cannot answer yourself. Ask once, with all your questions together.
|
|
19
|
+
- Use it for one short progress note when work takes more than a few minutes, such as "Started. This takes about 10 minutes."
|
|
20
|
+
- Do not use it for receipts ("Got it", "On it"), and do not send many progress notes.
|
|
21
|
+
- It works only on a task with a `task:chat-sdk:` contextKey. A session bound to a non-chat task does not see `chat-reply` at all, because the tool is left out of its tool list.
|
|
22
|
+
|
|
23
|
+
## The final summary is posted automatically
|
|
24
|
+
|
|
25
|
+
When your task ends, the swarm posts the task output to the thread:
|
|
26
|
+
|
|
27
|
+
- Completed: your `store-progress` output.
|
|
28
|
+
- Failed: a "Task failed." line, then the failure reason.
|
|
29
|
+
- Cancelled: a "Task cancelled." line, then the reason when there is one.
|
|
30
|
+
|
|
31
|
+
Do not post your final answer with `chat-reply` as well. The user would get it twice. Put the answer in the `store-progress` output and stop.
|
|
32
|
+
|
|
33
|
+
The post is durable. If the platform is down, the swarm retries with backoff for up to 24 hours.
|
|
34
|
+
|
|
35
|
+
## Markdown per platform
|
|
36
|
+
|
|
37
|
+
Write plain, short markdown: bold, italic, inline code, code blocks, bullet lists, and links. Avoid tables, nested lists, and HTML.
|
|
38
|
+
|
|
39
|
+
| Platform | What renders | Limit per message |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Telegram | Bold, italic, code, code blocks, links, lists. No tables. | 4096 characters |
|
|
42
|
+
| Discord | Bold, italic, code, code blocks, lists, headings, links. No tables. | 2000 characters |
|
|
43
|
+
| Slack | Converted to Slack mrkdwn: bold, italic, code, lists, links. Tables do not render well. | 4000 characters |
|
|
44
|
+
| Teams | Basic markdown: bold, italic, code, lists, links. | 20000 characters |
|
|
45
|
+
|
|
46
|
+
The swarm splits longer text into several messages at line breaks. Long answers are hard to read in chat, so keep the answer short and put long material in an artifact or page, then post the link.
|
|
47
|
+
|
|
48
|
+
## Attachments arrive as task attachments
|
|
49
|
+
|
|
50
|
+
Files and images the user sends in the thread are stored as attachments of the task. The task text names them. Download them with the `download-task-attachment` skill. You cannot send files back to the thread in this version. Share a link instead.
|
|
51
|
+
|
|
52
|
+
## `chat-read-thread` limits per platform
|
|
53
|
+
|
|
54
|
+
`chat-read-thread { threadId, limit? }` returns the most recent messages of a thread, oldest first. The thread id is the adapter name plus the URI-decoded remainder of the contextKey. Telegram ids have no escaping, so `telegram:555001` stays as it is. Slack, Discord, and Teams ids contain `%3A` in the contextKey, so do not build the id by hand. `chat-reply` resolves the thread itself. For `chat-read-thread`, use the thread id returned by `chat-reply`, `chat-post`, or `chat-open-dm`. You can read the thread of your current task. Any other thread needs lead privileges.
|
|
55
|
+
|
|
56
|
+
- Telegram has no history API. The SDK keeps the Telegram thread history it saw in the swarm database, capped at 100 messages and 7 days.
|
|
57
|
+
- Teams needs the `Chat.Read.*` Graph permission on the bot. Without it, the call fails.
|
|
58
|
+
- Slack and Discord return the platform history.
|
|
59
|
+
|
|
60
|
+
The task text already holds the earlier swarm messages of the thread, so read the thread only when you need more.
|
|
61
|
+
|
|
62
|
+
## Other tools
|
|
63
|
+
|
|
64
|
+
- `chat-post { adapter, threadId | channelId, text }` posts outside your task's thread. Lead only. Use it only when the task asks you to message another thread or channel.
|
|
65
|
+
- `chat-open-dm { adapter, userId }` returns the direct-message thread id for a user. Lead only. Then post to it with `chat-post`.
|
|
66
|
+
|
|
67
|
+
## Leads: `register-chat-channel`
|
|
68
|
+
|
|
69
|
+
By default every message addressed to the swarm goes to the lead. A lead can route one channel to a fixed agent or workflow:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
register-chat-channel { adapter: "telegram", channelId: "telegram:-100123", agentId: "<agent id>" }
|
|
73
|
+
register-chat-channel { adapter: "slack", channelId: "slack:C123", workflowId: "<workflow id>" }
|
|
74
|
+
unregister-chat-channel { adapter: "telegram", channelId: "telegram:-100123" }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Give exactly one of `agentId` or `workflowId`. A `swarm#<agentId>` token in a message still wins over the route. A workflow mapping fires only for a bot mention or a DM; a plain reply in the subscribed thread does not start another run. Only the lead can call these tools.
|
|
78
|
+
|
|
79
|
+
## Etiquette
|
|
80
|
+
|
|
81
|
+
- Keep messages short. Chat is read on phones.
|
|
82
|
+
- Never post secrets, tokens, internal URLs with credentials, or private data from other users. The swarm scrubs known secrets, but do not depend on it.
|
|
83
|
+
- When you ask a question in a group, start with the user's name so they see it.
|
|
84
|
+
- One question message is better than several.
|
|
@@ -62,6 +62,13 @@ agent-swarm x composio POST /tools/execute/GMAIL_SEND_EMAIL \
|
|
|
62
62
|
- `recipient_email` is the primary; add more via `extra_recipients` / `cc` / `bcc`.
|
|
63
63
|
- **Sending is a write action** — only do it when the task explicitly asks.
|
|
64
64
|
|
|
65
|
+
## Sender (From) and footer
|
|
66
|
+
|
|
67
|
+
- A draft defaults to the connected account's primary address. Before you create drafts, list the account's verified send-as aliases and set From to the alias the requester prefers (requester profile or brief). Take the footer and signature domain from the same source.
|
|
68
|
+
- Never set From to an alias that is not verified. Gmail rewrites it to the primary address.
|
|
69
|
+
- After you create the drafts, check From as well as To and Subject on each one.
|
|
70
|
+
- To fix existing drafts, update each draft in place with the alias. Draft ids stay the same, so there is no need to delete and recreate.
|
|
71
|
+
|
|
65
72
|
## Reply in a thread
|
|
66
73
|
|
|
67
74
|
```bash
|
|
@@ -67,7 +67,7 @@ agent-swarm x composio POST /tools/execute/GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN \
|
|
|
67
67
|
2. Write the converted content to your own agent-fs namespace with a short
|
|
68
68
|
provenance header naming the source document and import method.
|
|
69
69
|
3. Resolve the destination org and drive from the environment or
|
|
70
|
-
`agent-fs stat <path> --json`; the `
|
|
70
|
+
`agent-fs stat <path> --json`; the `agent-fs` skill documents the canonical
|
|
71
71
|
storage and share-link workflow. Never copy org or drive IDs from an example.
|
|
72
72
|
4. Read the stored file back and assert several markers: the title, a middle
|
|
73
73
|
phrase, and the final sentence. A successful byte-count response alone does
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "skill",
|
|
3
|
+
"name": "email-domain-health",
|
|
4
|
+
"displayName": "Email Domain Health",
|
|
5
|
+
"slug": "email-domain-health",
|
|
6
|
+
"title": "Email Domain Health",
|
|
7
|
+
"description": "Build a scheduled email domain health monitor: ask the user for domains, providers, test inbox, schedule and alert rules, then build a checks script (SPF, DKIM, DMARC, MX, provider records, blocklists with positive controls), a daily test send with score capture, bounce rate, run history in a Swarm App, and alerts only on a new red or a score drop. Orchestrates the swarm-scripts, apps, scheduling and composio-gmail skills.",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"category": "skills",
|
|
10
|
+
"placeholders": [],
|
|
11
|
+
"runAllSeedersCandidate": true,
|
|
12
|
+
"systemDefault": true,
|
|
13
|
+
"tags": ["email", "deliverability", "dns", "monitoring", "apps"]
|
|
14
|
+
}
|