@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
package/src/types.ts
CHANGED
|
@@ -391,6 +391,7 @@ export const AgentTaskSourceSchema = z.enum([
|
|
|
391
391
|
"linear",
|
|
392
392
|
"jira",
|
|
393
393
|
"comb",
|
|
394
|
+
"chat",
|
|
394
395
|
]);
|
|
395
396
|
export type AgentTaskSource = z.infer<typeof AgentTaskSourceSchema>;
|
|
396
397
|
|
|
@@ -446,7 +447,7 @@ export const SteeringStatusSchema = z.enum([
|
|
|
446
447
|
]);
|
|
447
448
|
export type SteeringStatus = z.infer<typeof SteeringStatusSchema>;
|
|
448
449
|
|
|
449
|
-
export const SteeringSourceSchema = z.enum(["ui", "mcp", "script", "slack", "api"]);
|
|
450
|
+
export const SteeringSourceSchema = z.enum(["ui", "mcp", "script", "slack", "api", "chat"]);
|
|
450
451
|
export type SteeringSource = z.infer<typeof SteeringSourceSchema>;
|
|
451
452
|
|
|
452
453
|
export const SteeringMessageSchema = z
|
|
@@ -681,6 +682,10 @@ export const AgentTaskSchema = z
|
|
|
681
682
|
resolvedModel: z.string().optional(),
|
|
682
683
|
modelSource: z.string().optional(),
|
|
683
684
|
modelAlias: z.string().optional(),
|
|
685
|
+
// The effort the session ran at (task effort, else the agent's
|
|
686
|
+
// REASONING_EFFORT_OVERRIDE, dropped when the harness/model cannot take
|
|
687
|
+
// it). Reported at session start; unset when the harness default applied.
|
|
688
|
+
resolvedEffort: ReasoningEffortSchema.optional(),
|
|
684
689
|
|
|
685
690
|
// Schedule linking (optional — set when task was created by a schedule).
|
|
686
691
|
// z.string(): also reachable verbatim from the create-task query param.
|
|
@@ -1458,7 +1463,7 @@ export type ChannelType = z.infer<typeof ChannelTypeSchema>;
|
|
|
1458
1463
|
export type Channel = z.infer<typeof ChannelSchema>;
|
|
1459
1464
|
export type ChannelMessage = z.infer<typeof ChannelMessageSchema>;
|
|
1460
1465
|
|
|
1461
|
-
// Service Types (
|
|
1466
|
+
// Service Types (removed PM2 service registry; kept with the services table)
|
|
1462
1467
|
export const ServiceStatusSchema = z.enum(["starting", "healthy", "unhealthy", "stopped"]);
|
|
1463
1468
|
|
|
1464
1469
|
export const ServiceSchema = z
|
|
@@ -1834,6 +1839,10 @@ export const SwarmConfigSchema = z
|
|
|
1834
1839
|
// auto-encrypted during initDb; if that fails, boot aborts before normal API
|
|
1835
1840
|
// reads occur.
|
|
1836
1841
|
encrypted: z.boolean(),
|
|
1842
|
+
// Whether workers export this row into agent process env. False keeps it
|
|
1843
|
+
// server-side (connections, script credential bindings). Always false for
|
|
1844
|
+
// keys only the API server reads (src/utils/config-env-export.ts).
|
|
1845
|
+
exportToEnv: z.boolean(),
|
|
1837
1846
|
})
|
|
1838
1847
|
.openapi("SwarmConfig");
|
|
1839
1848
|
|
|
@@ -2625,6 +2634,7 @@ export type AgentTaskSummary = Pick<
|
|
|
2625
2634
|
| "modelSource"
|
|
2626
2635
|
| "modelAlias"
|
|
2627
2636
|
| "effort"
|
|
2637
|
+
| "resolvedEffort"
|
|
2628
2638
|
| "provider"
|
|
2629
2639
|
| "requestedByUserId"
|
|
2630
2640
|
| "progress"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a swarm_config row reaches an agent's process environment.
|
|
3
|
+
*
|
|
4
|
+
* Each row carries an `exportToEnv` flag (migration 215). Workers export a row
|
|
5
|
+
* into the harness env, and so into `env`, `printenv` and `/proc/<pid>/environ`
|
|
6
|
+
* of every agent shell, only when the flag is true. Rows with the flag off stay
|
|
7
|
+
* server-side: agents use them through a connection or a script credential
|
|
8
|
+
* binding instead.
|
|
9
|
+
*
|
|
10
|
+
* Keys below are read only by the API server (integration signing secrets, app
|
|
11
|
+
* private keys, OAuth client secrets, the API's own agent-fs key). They can
|
|
12
|
+
* never be exported: the config API rejects `exportToEnv: true` for them and
|
|
13
|
+
* every reader treats a stored true as false.
|
|
14
|
+
*/
|
|
15
|
+
export const ENV_EXPORT_RESTRICTED_KEYS: ReadonlySet<string> = new Set([
|
|
16
|
+
"SLACK_APP_TOKEN",
|
|
17
|
+
"SLACK_SIGNING_SECRET",
|
|
18
|
+
"GITHUB_APP_PRIVATE_KEY",
|
|
19
|
+
"GITHUB_WEBHOOK_SECRET",
|
|
20
|
+
"GITLAB_WEBHOOK_SECRET",
|
|
21
|
+
"LINEAR_CLIENT_SECRET",
|
|
22
|
+
"LINEAR_SIGNING_SECRET",
|
|
23
|
+
"JIRA_CLIENT_SECRET",
|
|
24
|
+
"JIRA_WEBHOOK_TOKEN",
|
|
25
|
+
"AZURE_DEVOPS_WEBHOOK_SECRET",
|
|
26
|
+
"AGENTMAIL_WEBHOOK_SECRET",
|
|
27
|
+
"API_AGENT_FS_API_KEY",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Server-side secrets an operator may still export per row. A new row for one
|
|
32
|
+
* of these keys starts with the flag off unless the caller sends
|
|
33
|
+
* `exportToEnv: true`. Migration 215 applies the same default to rows that
|
|
34
|
+
* already existed.
|
|
35
|
+
*/
|
|
36
|
+
export const ENV_EXPORT_DEFAULT_OFF_KEYS: ReadonlySet<string> = new Set([
|
|
37
|
+
"KAPSO_WEBHOOK_HMAC_SECRET",
|
|
38
|
+
"SENTRY_TEST_APP_WEBHOOK_SECRET",
|
|
39
|
+
"TELEMETRY_CH_AUTH",
|
|
40
|
+
"HACK_AGENT_FS_ADMIN_KEY",
|
|
41
|
+
"X_CLIENT_SECRET",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
/** Script connection secrets (`connection.<slug>.secret`) are brokered server-side. */
|
|
45
|
+
const CONNECTION_SECRET_KEY = /^connection\..+\.secret$/;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Key prefixes whose rows start with the flag off: the API server reads them
|
|
49
|
+
* (Chat SDK bot tokens and adapter JSON), agents never need them in a shell.
|
|
50
|
+
*/
|
|
51
|
+
const ENV_EXPORT_DEFAULT_OFF_PREFIXES = ["CHAT_SDK_"];
|
|
52
|
+
|
|
53
|
+
export function isEnvExportRestrictedKey(key: string): boolean {
|
|
54
|
+
return ENV_EXPORT_RESTRICTED_KEYS.has(key.toUpperCase()) || CONNECTION_SECRET_KEY.test(key);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The flag a new row gets when the caller does not send one. */
|
|
58
|
+
export function defaultExportToEnv(key: string): boolean {
|
|
59
|
+
const upper = key.toUpperCase();
|
|
60
|
+
return (
|
|
61
|
+
!isEnvExportRestrictedKey(key) &&
|
|
62
|
+
!ENV_EXPORT_DEFAULT_OFF_KEYS.has(upper) &&
|
|
63
|
+
!ENV_EXPORT_DEFAULT_OFF_PREFIXES.some((prefix) => upper.startsWith(prefix))
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function envExportRestrictedError(key: string): string {
|
|
68
|
+
return `Key '${key}' is read only by the API server and can never be exported to agent env; store it with exportToEnv: false.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The flag readers act on: a stored true never wins for a restricted key. */
|
|
72
|
+
export function effectiveExportToEnv(key: string, stored: boolean): boolean {
|
|
73
|
+
return stored && !isEnvExportRestrictedKey(key);
|
|
74
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { OPENAI_COMPATIBLE_PROVIDERS } from "@desplega/model-catalog";
|
|
2
3
|
import {
|
|
3
4
|
CREDENTIAL_POOL_VARS,
|
|
5
|
+
deriveProviderFromKeyType,
|
|
4
6
|
getModelAwareCredentialVars,
|
|
5
7
|
resolveCredentialPools,
|
|
6
8
|
selectRandomCredential,
|
|
@@ -123,6 +125,35 @@ describe("resolveCredentialPools", () => {
|
|
|
123
125
|
expect(["example-oauth1", "example-oauth2"]).toContain(env.CLAUDE_CODE_OAUTH_TOKEN!);
|
|
124
126
|
expect(["example-apikey1", "example-apikey2"]).toContain(env.ANTHROPIC_API_KEY!);
|
|
125
127
|
});
|
|
128
|
+
|
|
129
|
+
it("pools NAN_API_KEY alone for an opencode nan/* model", async () => {
|
|
130
|
+
const env: Record<string, string | undefined> = {
|
|
131
|
+
NAN_API_KEY: "example-nan-a,example-nan-b,example-nan-c",
|
|
132
|
+
OPENROUTER_API_KEY: "example-or-a,example-or-b",
|
|
133
|
+
};
|
|
134
|
+
const selections = await resolveCredentialPools(env, {
|
|
135
|
+
provider: "opencode",
|
|
136
|
+
model: "nan/glm5.3-flash",
|
|
137
|
+
});
|
|
138
|
+
expect(["example-nan-a", "example-nan-b", "example-nan-c"]).toContain(env.NAN_API_KEY!);
|
|
139
|
+
expect(selections).toHaveLength(1);
|
|
140
|
+
expect(selections[0]?.keyType).toBe("NAN_API_KEY");
|
|
141
|
+
expect(selections[0]?.total).toBe(3);
|
|
142
|
+
// OpenRouter is not relevant to a nan/* model and stays untouched.
|
|
143
|
+
expect(env.OPENROUTER_API_KEY).toBe("example-or-a,example-or-b");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("pools HELMCODE_API_KEY alone for a pi helmcode/* model", async () => {
|
|
147
|
+
const env: Record<string, string | undefined> = {
|
|
148
|
+
HELMCODE_API_KEY: "example-helm-a,example-helm-b",
|
|
149
|
+
};
|
|
150
|
+
const selections = await resolveCredentialPools(env, {
|
|
151
|
+
provider: "pi",
|
|
152
|
+
model: "helmcode/glm5.3",
|
|
153
|
+
});
|
|
154
|
+
expect(["example-helm-a", "example-helm-b"]).toContain(env.HELMCODE_API_KEY!);
|
|
155
|
+
expect(selections.map((s) => s.keyType)).toEqual(["HELMCODE_API_KEY"]);
|
|
156
|
+
});
|
|
126
157
|
});
|
|
127
158
|
|
|
128
159
|
describe("validateClaudeCredentials", () => {
|
|
@@ -198,6 +229,23 @@ describe("CREDENTIAL_POOL_VARS", () => {
|
|
|
198
229
|
it("contains expected env var names", () => {
|
|
199
230
|
expect(CREDENTIAL_POOL_VARS).toContain("CLAUDE_CODE_OAUTH_TOKEN");
|
|
200
231
|
expect(CREDENTIAL_POOL_VARS).toContain("ANTHROPIC_API_KEY");
|
|
232
|
+
expect(CREDENTIAL_POOL_VARS).toContain("NAN_API_KEY");
|
|
233
|
+
expect(CREDENTIAL_POOL_VARS).toContain("HELMCODE_API_KEY");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("pools only the canonical NaN var; the alias is normalized by the runner", () => {
|
|
237
|
+
expect(OPENAI_COMPATIBLE_PROVIDERS.nan.aliasEnvVars).toEqual(["HELMCODE_API_NAN"]);
|
|
238
|
+
expect(CREDENTIAL_POOL_VARS as readonly string[]).not.toContain("HELMCODE_API_NAN");
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("deriveProviderFromKeyType", () => {
|
|
243
|
+
it("tags NaN and Helmcode keys as pi, like OpenRouter", () => {
|
|
244
|
+
for (const keyType of ["NAN_API_KEY", "HELMCODE_API_NAN", "HELMCODE_API_KEY"]) {
|
|
245
|
+
expect(deriveProviderFromKeyType(keyType)).toBe("pi");
|
|
246
|
+
}
|
|
247
|
+
expect(deriveProviderFromKeyType("OPENROUTER_API_KEY")).toBe("pi");
|
|
248
|
+
expect(deriveProviderFromKeyType("SOMETHING_ELSE")).toBe("claude");
|
|
201
249
|
});
|
|
202
250
|
});
|
|
203
251
|
|
|
@@ -266,4 +314,48 @@ describe("getModelAwareCredentialVars", () => {
|
|
|
266
314
|
const vars = getModelAwareCredentialVars("opencode", "anthropic/claude-sonnet-4-20250514");
|
|
267
315
|
expect(vars).not.toContain("OPENAI_API_KEY");
|
|
268
316
|
});
|
|
317
|
+
|
|
318
|
+
it("routes nan/* and helmcode/* to their own key on pi and opencode", () => {
|
|
319
|
+
for (const provider of ["pi", "opencode"]) {
|
|
320
|
+
expect(getModelAwareCredentialVars(provider, "nan/glm5.3-flash")).toEqual(["NAN_API_KEY"]);
|
|
321
|
+
expect(getModelAwareCredentialVars(provider, "helmcode/glm5.3")).toEqual([
|
|
322
|
+
"HELMCODE_API_KEY",
|
|
323
|
+
]);
|
|
324
|
+
// Only the exact prefix routes: a look-alike is any other provider/model.
|
|
325
|
+
const other = getModelAwareCredentialVars(provider, "other/x");
|
|
326
|
+
expect(getModelAwareCredentialVars(provider, "helmcodex/x")).toEqual(other);
|
|
327
|
+
expect(getModelAwareCredentialVars(provider, "nanx/y")).toEqual(other);
|
|
328
|
+
expect(other).not.toContain("NAN_API_KEY");
|
|
329
|
+
expect(other).not.toContain("HELMCODE_API_KEY");
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("keeps OpenRouter and bare-id routing byte-identical", () => {
|
|
334
|
+
for (const provider of ["pi", "opencode"]) {
|
|
335
|
+
expect(
|
|
336
|
+
getModelAwareCredentialVars(provider, "openrouter/deepseek/deepseek-v4-flash"),
|
|
337
|
+
).toEqual(["OPENROUTER_API_KEY", "ANTHROPIC_API_KEY"]);
|
|
338
|
+
expect(getModelAwareCredentialVars(provider, "openai/gpt-5")).toEqual([
|
|
339
|
+
"OPENROUTER_API_KEY",
|
|
340
|
+
"ANTHROPIC_API_KEY",
|
|
341
|
+
]);
|
|
342
|
+
expect(getModelAwareCredentialVars(provider, "gpt-4o")).toEqual([
|
|
343
|
+
"OPENROUTER_API_KEY",
|
|
344
|
+
"ANTHROPIC_API_KEY",
|
|
345
|
+
"OPENAI_API_KEY",
|
|
346
|
+
]);
|
|
347
|
+
// A NaN model routed through OpenRouter is still an OpenRouter model.
|
|
348
|
+
expect(getModelAwareCredentialVars(provider, "openrouter/nan/glm5.3")).toEqual([
|
|
349
|
+
"OPENROUTER_API_KEY",
|
|
350
|
+
"ANTHROPIC_API_KEY",
|
|
351
|
+
]);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("does not apply the nan/helmcode prefix routing to other harnesses", () => {
|
|
356
|
+
expect(getModelAwareCredentialVars("claude", "nan/glm5.3")).toEqual([
|
|
357
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
358
|
+
"ANTHROPIC_API_KEY",
|
|
359
|
+
]);
|
|
360
|
+
});
|
|
269
361
|
});
|
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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime read of `NODE_ENV`.
|
|
3
|
+
*
|
|
4
|
+
* `bun build --compile` replaces every dot read of `NODE_ENV` on `process.env`
|
|
5
|
+
* with the build-time constant "development", so the compiled API and worker
|
|
6
|
+
* binaries never see the deployment's value. A computed key is not inlined.
|
|
7
|
+
* Read `NODE_ENV` through this helper only. `src/tests/node-env-runtime.test.ts`
|
|
8
|
+
* compiles a binary to prove it; `scripts/check-node-env-access.sh` rejects new
|
|
9
|
+
* dot reads in `src/`.
|
|
10
|
+
*/
|
|
11
|
+
const NODE_ENV_KEY = "NODE_ENV";
|
|
12
|
+
|
|
13
|
+
export function getNodeEnv(): string | undefined {
|
|
14
|
+
return process.env[NODE_ENV_KEY];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* True only for an explicit development or test environment. Deployed images
|
|
19
|
+
* leave NODE_ENV unset, so unset (and any other value) fails closed.
|
|
20
|
+
*/
|
|
21
|
+
export function isDevOrTestNodeEnv(): boolean {
|
|
22
|
+
const env = getNodeEnv();
|
|
23
|
+
return env === "development" || env === "test";
|
|
24
|
+
}
|
|
@@ -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
|
|