@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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retiring a seeded skill whose template was removed.
|
|
3
|
+
*
|
|
4
|
+
* Dropping `templates/skills/<name>/` alone leaves the last seeded row live on
|
|
5
|
+
* every existing install. `retireSeedSkills` deletes it, but only while the
|
|
6
|
+
* seeder still owns it: a recorded `seed_state` hash that the live copy
|
|
7
|
+
* matches and every metadata column still holds what the seeder wrote.
|
|
8
|
+
* Unrecorded and user-edited skills stay, including metadata-only edits.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
12
|
+
import { unlink } from "node:fs/promises";
|
|
13
|
+
import {
|
|
14
|
+
closeDb,
|
|
15
|
+
computeContentHash,
|
|
16
|
+
createAgent,
|
|
17
|
+
createSkill,
|
|
18
|
+
getAgentSkills,
|
|
19
|
+
getSkillByName,
|
|
20
|
+
getSkillFiles,
|
|
21
|
+
initDb,
|
|
22
|
+
installSkill,
|
|
23
|
+
updateSkill,
|
|
24
|
+
upsertSkillFiles,
|
|
25
|
+
} from "../be/db";
|
|
26
|
+
import { getSeedState, recordSeedState, runSeeder } from "../be/seed";
|
|
27
|
+
import { RETIRED_SEED_SKILLS, retireSeedSkills, skillsSeeder } from "../be/seed-skills";
|
|
28
|
+
|
|
29
|
+
const TEST_DB_PATH = `./test-seed-skills-retire-${process.pid}.sqlite`;
|
|
30
|
+
|
|
31
|
+
async function removeDbFiles(path: string): Promise<void> {
|
|
32
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
33
|
+
await unlink(path + suffix).catch(() => {});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const BUNDLED_FILE = { path: "examples/serve.ts", content: "pm2 start serve.ts\n" };
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Seed a swarm skill the way the seeder does (frontmatter description equal to
|
|
41
|
+
* the description column, isComplex set from the file count) and record the
|
|
42
|
+
* hash it would have written.
|
|
43
|
+
*/
|
|
44
|
+
async function seedLegacySkill(name: string, withFile = false): Promise<string> {
|
|
45
|
+
const content = `---\nname: ${name}\ndescription: old\n---\n\nRun pm2 start.\n`;
|
|
46
|
+
const skill = await createSkill({
|
|
47
|
+
name,
|
|
48
|
+
description: "old",
|
|
49
|
+
content,
|
|
50
|
+
type: "personal",
|
|
51
|
+
scope: "swarm",
|
|
52
|
+
ownerAgentId: undefined,
|
|
53
|
+
systemDefault: false,
|
|
54
|
+
isComplex: withFile,
|
|
55
|
+
});
|
|
56
|
+
let hashed = `${content}\n\n# seed:systemDefault=0\n`;
|
|
57
|
+
if (withFile) {
|
|
58
|
+
await upsertSkillFiles(skill.id, [BUNDLED_FILE]);
|
|
59
|
+
hashed += `# seed:files\n${BUNDLED_FILE.path}\n${BUNDLED_FILE.content}\n`;
|
|
60
|
+
}
|
|
61
|
+
await recordSeedState("skill", name, computeContentHash(hashed));
|
|
62
|
+
return skill.id;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe("retiring seeded skills", () => {
|
|
66
|
+
let agentId: string;
|
|
67
|
+
|
|
68
|
+
beforeAll(async () => {
|
|
69
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
70
|
+
initDb(TEST_DB_PATH);
|
|
71
|
+
agentId = (
|
|
72
|
+
await createAgent({ name: "retire-test", isLead: false, status: "idle", capabilities: [] })
|
|
73
|
+
).id;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterAll(async () => {
|
|
77
|
+
closeDb();
|
|
78
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("artifacts is retired and no longer seeded", async () => {
|
|
82
|
+
expect(RETIRED_SEED_SKILLS).toContain("artifacts");
|
|
83
|
+
const keys = (await skillsSeeder.items()).map((item) => item.key);
|
|
84
|
+
expect(keys).not.toContain("artifacts");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("the seeder run deletes a pristine retired skill and its seed state", async () => {
|
|
88
|
+
const id = await seedLegacySkill("artifacts", true);
|
|
89
|
+
await installSkill(agentId, id);
|
|
90
|
+
|
|
91
|
+
const result = await runSeeder(skillsSeeder, { quiet: true });
|
|
92
|
+
expect(result.failed).toEqual([]);
|
|
93
|
+
expect(result.retired).toBe(1);
|
|
94
|
+
expect(await getSkillByName("artifacts", "swarm")).toBeNull();
|
|
95
|
+
expect(await getSkillFiles(id)).toEqual([]);
|
|
96
|
+
expect((await getAgentSkills(agentId, false)).map((s) => s.id)).not.toContain(id);
|
|
97
|
+
expect(await getSeedState("skill", "artifacts")).toBeNull();
|
|
98
|
+
|
|
99
|
+
// Settled: nothing left to retire on the next boot.
|
|
100
|
+
expect((await runSeeder(skillsSeeder, { quiet: true })).retired).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("the seeder run keeps a retired skill with metadata-only edits, with its installs and files", async () => {
|
|
104
|
+
const id = await seedLegacySkill("artifacts", true);
|
|
105
|
+
await installSkill(agentId, id);
|
|
106
|
+
await updateSkill(id, {
|
|
107
|
+
allowedTools: "Read",
|
|
108
|
+
model: "user-selected-model",
|
|
109
|
+
description: "my edited description",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const result = await runSeeder(skillsSeeder, { quiet: true });
|
|
113
|
+
expect(result.failed).toEqual([]);
|
|
114
|
+
expect(result.retired).toBe(0);
|
|
115
|
+
|
|
116
|
+
const kept = await getSkillByName("artifacts", "swarm");
|
|
117
|
+
expect(kept?.id).toBe(id);
|
|
118
|
+
expect(kept?.model).toBe("user-selected-model");
|
|
119
|
+
expect((await getSkillFiles(id)).map((file) => file.path)).toEqual([BUNDLED_FILE.path]);
|
|
120
|
+
expect((await getAgentSkills(agentId, false)).map((s) => s.id)).toContain(id);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Every column updateSkill can write outside the seed hash. Each one alone
|
|
124
|
+
// marks the row as user-edited.
|
|
125
|
+
const METADATA_EDITS: [string, Parameters<typeof updateSkill>[1]][] = [
|
|
126
|
+
["description", { description: "mine" }],
|
|
127
|
+
["allowedTools", { allowedTools: "Read" }],
|
|
128
|
+
["model", { model: "opus" }],
|
|
129
|
+
["effort", { effort: "high" }],
|
|
130
|
+
["context", { context: "fork" }],
|
|
131
|
+
["agent", { agent: "reviewer" }],
|
|
132
|
+
["disableModelInvocation", { disableModelInvocation: true }],
|
|
133
|
+
["isEnabled", { isEnabled: false }],
|
|
134
|
+
["isComplex", { isComplex: true }],
|
|
135
|
+
["sourceUrl", { sourceUrl: "https://example.com/SKILL.md" }],
|
|
136
|
+
["sourceRepo", { sourceRepo: "me/skills" }],
|
|
137
|
+
["sourcePath", { sourcePath: "skills/artifacts" }],
|
|
138
|
+
["sourceBranch", { sourceBranch: "dev" }],
|
|
139
|
+
["sourceHash", { sourceHash: "abc123" }],
|
|
140
|
+
["lastFetchedAt", { lastFetchedAt: "2026-01-01T00:00:00.000Z" }],
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
test.each(METADATA_EDITS)("a retired skill with an edited %s is kept", async (field, edit) => {
|
|
144
|
+
const name = `zz-retired-meta-${field.toLowerCase()}`;
|
|
145
|
+
const id = await seedLegacySkill(name);
|
|
146
|
+
await updateSkill(id, edit);
|
|
147
|
+
|
|
148
|
+
expect(await retireSeedSkills([name])).toEqual([]);
|
|
149
|
+
expect(await getSkillByName(name, "swarm")).not.toBeNull();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("a pristine retired skill touched by a no-op update is still retired", async () => {
|
|
153
|
+
const id = await seedLegacySkill("zz-retired-noop");
|
|
154
|
+
await updateSkill(id, {});
|
|
155
|
+
|
|
156
|
+
expect(await retireSeedSkills(["zz-retired-noop"])).toEqual(["zz-retired-noop"]);
|
|
157
|
+
expect(await getSkillByName("zz-retired-noop", "swarm")).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("a retired skill edited since it was seeded is kept", async () => {
|
|
161
|
+
const id = await seedLegacySkill("zz-retired-edited");
|
|
162
|
+
await updateSkill(id, { content: "---\nname: zz-retired-edited\n---\n\nmine\n" });
|
|
163
|
+
|
|
164
|
+
expect(await retireSeedSkills(["zz-retired-edited"])).toEqual([]);
|
|
165
|
+
expect(await getSkillByName("zz-retired-edited", "swarm")).not.toBeNull();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("a same-name skill the seeder never recorded is kept", async () => {
|
|
169
|
+
await createSkill({
|
|
170
|
+
name: "zz-retired-unrecorded",
|
|
171
|
+
description: "user authored",
|
|
172
|
+
content: "---\nname: zz-retired-unrecorded\n---\n\nmine\n",
|
|
173
|
+
type: "personal",
|
|
174
|
+
scope: "swarm",
|
|
175
|
+
ownerAgentId: undefined,
|
|
176
|
+
systemDefault: false,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(await retireSeedSkills(["zz-retired-unrecorded"])).toEqual([]);
|
|
180
|
+
expect(await getSkillByName("zz-retired-unrecorded", "swarm")).not.toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* A task's requester drives attribution, cost reporting and admission. A non-lead agent may omit
|
|
3
3
|
* `requestedByUserId` or pass the requester of its own current task. Only a lead, the operator or
|
|
4
4
|
* a user may name any other registered user. Covers the send-task tool and POST /api/tasks.
|
|
5
|
+
* POST /api/tasks also refuses an agent a foreign parent or a chat thread it does not own.
|
|
5
6
|
*/
|
|
6
7
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
7
8
|
import { unlink } from "node:fs/promises";
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
initDb,
|
|
19
20
|
} from "../be/db";
|
|
20
21
|
import { handleTasks } from "../http/tasks";
|
|
22
|
+
import { chatSdkContextKey } from "../tasks/context-key";
|
|
21
23
|
import { registerSendTaskTool } from "../tools/send-task";
|
|
22
24
|
import { type HttpRequestAuth, setRequestAuth } from "../utils/request-auth-context";
|
|
23
25
|
|
|
@@ -258,3 +260,59 @@ describe("POST /api/tasks with an X-Agent-ID on the swarm key", () => {
|
|
|
258
260
|
expect(lead.body.requestedByUserId).toBe(userBId);
|
|
259
261
|
});
|
|
260
262
|
});
|
|
263
|
+
|
|
264
|
+
describe("POST /api/tasks parent and chat-thread binding for an agent caller", () => {
|
|
265
|
+
test("a worker cannot attach a child to a task it neither owns nor created", async () => {
|
|
266
|
+
const foreign = await createTaskExtended("lead chat task (http)", {
|
|
267
|
+
agentId: LEAD_ID,
|
|
268
|
+
source: "chat",
|
|
269
|
+
contextKey: chatSdkContextKey("telegram:552222"),
|
|
270
|
+
});
|
|
271
|
+
const before = await taskCount();
|
|
272
|
+
const result = await postTask(
|
|
273
|
+
{ agentId: WORKER_ID },
|
|
274
|
+
{ parentTaskId: foreign.id, agentId: WORKER_ID, routingReason: "skill" },
|
|
275
|
+
);
|
|
276
|
+
expect(result.status).toBe(403);
|
|
277
|
+
expect(await taskCount()).toBe(before);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("a worker cannot bind a new task to a chat thread by contextKey", async () => {
|
|
281
|
+
const before = await taskCount();
|
|
282
|
+
const result = await postTask(
|
|
283
|
+
{ agentId: WORKER_ID },
|
|
284
|
+
{
|
|
285
|
+
contextKey: chatSdkContextKey("telegram:552222"),
|
|
286
|
+
agentId: WORKER_ID,
|
|
287
|
+
routingReason: "skill",
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
expect(result.status).toBe(403);
|
|
291
|
+
expect(await taskCount()).toBe(before);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("a worker may attach under its own task, and the operator and a lead keep access", async () => {
|
|
295
|
+
const own = await createTaskExtended("worker chat task (http)", {
|
|
296
|
+
agentId: WORKER_ID,
|
|
297
|
+
source: "chat",
|
|
298
|
+
contextKey: chatSdkContextKey("telegram:551111"),
|
|
299
|
+
});
|
|
300
|
+
const child = await postTask(
|
|
301
|
+
{ agentId: WORKER_ID },
|
|
302
|
+
{ parentTaskId: own.id, agentId: OTHER_WORKER_ID, routingReason: "skill" },
|
|
303
|
+
);
|
|
304
|
+
expect(child.status).toBe(201);
|
|
305
|
+
|
|
306
|
+
const foreign = await createTaskExtended("lead chat task (http, open)", {
|
|
307
|
+
agentId: LEAD_ID,
|
|
308
|
+
source: "chat",
|
|
309
|
+
contextKey: chatSdkContextKey("telegram:553333"),
|
|
310
|
+
});
|
|
311
|
+
expect((await postTask({}, { parentTaskId: foreign.id })).status).toBe(201);
|
|
312
|
+
expect((await postTask({ agentId: LEAD_ID }, { parentTaskId: foreign.id })).status).toBe(201);
|
|
313
|
+
expect(
|
|
314
|
+
(await postTask({ agentId: LEAD_ID }, { contextKey: chatSdkContextKey("telegram:554444") }))
|
|
315
|
+
.status,
|
|
316
|
+
).toBe(201);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// behavior that is uneconomical to express through a server round-trip.
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { loadModelsDevCache } from "../be/modelsdev-cache";
|
|
7
|
+
import { buildModelsDevSeedRows } from "../be/seed-pricing";
|
|
6
8
|
import { recomputeSessionCost } from "../http/session-cost-recompute";
|
|
7
9
|
import type { PricingProvider, PricingTokenClass } from "../types";
|
|
8
10
|
|
|
@@ -166,4 +168,96 @@ describe("recomputeSessionCost — pure branch behavior", () => {
|
|
|
166
168
|
expect(result.costSource).toBe("unpriced");
|
|
167
169
|
expect(result.totalCostUsd).toBe(0);
|
|
168
170
|
});
|
|
171
|
+
|
|
172
|
+
// Task 06c031dd (opencode 2.0.28): the session's tokens at the live
|
|
173
|
+
// OpenRouter list rates, vs the billed `usage.cost` sum the adapter flags.
|
|
174
|
+
const OPENCODE_DEEPSEEK: RateBook = {
|
|
175
|
+
"opencode:deepseek/deepseek-v4.1-flash:input": 0.0441,
|
|
176
|
+
"opencode:deepseek/deepseek-v4.1-flash:output": 0.765,
|
|
177
|
+
"opencode:deepseek/deepseek-v4.1-flash:cached_input": 0.0054,
|
|
178
|
+
};
|
|
179
|
+
const OPENCODE_ROW = {
|
|
180
|
+
...BASE,
|
|
181
|
+
provider: "opencode",
|
|
182
|
+
model: "openrouter/deepseek/deepseek-v4.1-flash",
|
|
183
|
+
inputTokens: 65_917,
|
|
184
|
+
outputTokens: 212,
|
|
185
|
+
cacheReadTokens: 116_736,
|
|
186
|
+
} as const;
|
|
187
|
+
|
|
188
|
+
test("opencode without a billed cost prices its tokens from the table", async () => {
|
|
189
|
+
const result = await recomputeSessionCost(
|
|
190
|
+
{ ...OPENCODE_ROW, harnessCostUsd: 0.0014022106 },
|
|
191
|
+
lookupFrom(OPENCODE_DEEPSEEK),
|
|
192
|
+
);
|
|
193
|
+
expect(result.costSource).toBe("pricing-table");
|
|
194
|
+
expect(result.totalCostUsd).toBeCloseTo(
|
|
195
|
+
(65_917 * 0.0441 + 116_736 * 0.0054 + 212 * 0.765) / 1_000_000,
|
|
196
|
+
12,
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("a billed harness cost wins over the table", async () => {
|
|
201
|
+
const result = await recomputeSessionCost(
|
|
202
|
+
{ ...OPENCODE_ROW, harnessCostUsd: 0.0207, harnessCostBilled: true },
|
|
203
|
+
lookupFrom(OPENCODE_DEEPSEEK),
|
|
204
|
+
);
|
|
205
|
+
expect(result).toEqual({
|
|
206
|
+
totalCostUsd: 0.0207,
|
|
207
|
+
costSource: "harness",
|
|
208
|
+
modelBreakdown: undefined,
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("a billed flag with no cost still prices from the table", async () => {
|
|
213
|
+
const result = await recomputeSessionCost(
|
|
214
|
+
{ ...OPENCODE_ROW, harnessCostUsd: 0, harnessCostBilled: true },
|
|
215
|
+
lookupFrom(OPENCODE_DEEPSEEK),
|
|
216
|
+
);
|
|
217
|
+
expect(result.costSource).toBe("pricing-table");
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("nan and helmcode sessions price at $0 from the seeded table", () => {
|
|
222
|
+
// Rows projected from the vendored snapshot, exactly as the boot seeder and
|
|
223
|
+
// the runtime refresh insert them.
|
|
224
|
+
const cache = loadModelsDevCache();
|
|
225
|
+
const rows = cache ? buildModelsDevSeedRows(cache) : [];
|
|
226
|
+
const book: RateBook = Object.fromEntries(
|
|
227
|
+
rows.map((row) => [`${row.provider}:${row.model}:${row.tokenClass}`, row.pricePerMillionUsd]),
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
test("pi on nan/glm5.3-flash and opencode on helmcode/qwen3.6 record pricing-table, total 0", async () => {
|
|
231
|
+
for (const [provider, model] of [
|
|
232
|
+
["pi", "nan/glm5.3-flash"],
|
|
233
|
+
["opencode", "helmcode/qwen3.6"],
|
|
234
|
+
] as const) {
|
|
235
|
+
const result = await recomputeSessionCost(
|
|
236
|
+
{
|
|
237
|
+
...BASE,
|
|
238
|
+
provider,
|
|
239
|
+
model,
|
|
240
|
+
harnessCostUsd: 0,
|
|
241
|
+
inputTokens: 50_000,
|
|
242
|
+
outputTokens: 2_000,
|
|
243
|
+
cacheReadTokens: 10_000,
|
|
244
|
+
},
|
|
245
|
+
lookupFrom(book),
|
|
246
|
+
);
|
|
247
|
+
expect(result.costSource).toBe("pricing-table");
|
|
248
|
+
expect(result.totalCostUsd).toBe(0);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("nan/qwen3.6 and helmcode/qwen3.6 are separate pricing keys", () => {
|
|
253
|
+
for (const harness of ["pi", "opencode"]) {
|
|
254
|
+
const keys = new Set(
|
|
255
|
+
rows
|
|
256
|
+
.filter((row) => row.provider === harness && row.model.endsWith("/qwen3.6"))
|
|
257
|
+
.map((row) => row.model),
|
|
258
|
+
);
|
|
259
|
+
expect(keys.has("nan/qwen3.6")).toBe(true);
|
|
260
|
+
expect(keys.has("helmcode/qwen3.6")).toBe(true);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
169
263
|
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { closeDb, createAgent, createSkill, initDb } from "../be/db";
|
|
5
|
+
import { skillsSeeder } from "../be/seed-skills";
|
|
6
|
+
import { registerSkillGetTool } from "../tools/skills/skill-get";
|
|
7
|
+
|
|
8
|
+
const TEST_DB_PATH = "./test-skill-get-name-resolution.sqlite";
|
|
9
|
+
|
|
10
|
+
const CALLER_ID = "aaaa0000-0000-4000-8000-000000000031";
|
|
11
|
+
const OTHER_ID = "bbbb0000-0000-4000-8000-000000000032";
|
|
12
|
+
const THIRD_ID = "cccc0000-0000-4000-8000-000000000033";
|
|
13
|
+
|
|
14
|
+
type StructuredContent = {
|
|
15
|
+
success: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
skill?: { id: string; ownerAgentId: string | null };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async function callSkillGet(
|
|
21
|
+
server: McpServer,
|
|
22
|
+
callerAgentId: string | undefined,
|
|
23
|
+
args: Record<string, unknown>,
|
|
24
|
+
): Promise<StructuredContent> {
|
|
25
|
+
// biome-ignore lint/complexity/noBannedTypes: accessing internal MCP SDK type for test
|
|
26
|
+
const tools = (server as unknown as { _registeredTools: Record<string, { handler: Function }> })
|
|
27
|
+
._registeredTools;
|
|
28
|
+
const result = await tools["skill-get"].handler(args, {
|
|
29
|
+
sessionId: "test-session",
|
|
30
|
+
requestInfo: { headers: { "x-agent-id": callerAgentId ?? "" } },
|
|
31
|
+
});
|
|
32
|
+
return (result as { structuredContent: StructuredContent }).structuredContent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function swarmSkill(name: string, ownerAgentId: string | null) {
|
|
36
|
+
return createSkill({
|
|
37
|
+
name,
|
|
38
|
+
description: `${name} description`,
|
|
39
|
+
content: `---\nname: ${name}\ndescription: ${name} description\n---\n\nBody.`,
|
|
40
|
+
type: "personal",
|
|
41
|
+
scope: "swarm",
|
|
42
|
+
ownerAgentId: ownerAgentId ?? undefined,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("skill-get swarm name resolution", () => {
|
|
47
|
+
let server: McpServer;
|
|
48
|
+
|
|
49
|
+
beforeAll(async () => {
|
|
50
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
51
|
+
try {
|
|
52
|
+
await unlink(TEST_DB_PATH + suffix);
|
|
53
|
+
} catch {
|
|
54
|
+
// File doesn't exist
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
closeDb();
|
|
58
|
+
initDb(TEST_DB_PATH);
|
|
59
|
+
for (const [id, name] of [
|
|
60
|
+
[CALLER_ID, "Caller"],
|
|
61
|
+
[OTHER_ID, "Other"],
|
|
62
|
+
[THIRD_ID, "Third"],
|
|
63
|
+
] as const) {
|
|
64
|
+
await createAgent({ id, name, isLead: false, status: "idle" });
|
|
65
|
+
}
|
|
66
|
+
server = new McpServer({ name: "test-skill-get-name-resolution", version: "1.0.0" });
|
|
67
|
+
registerSkillGetTool(server);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
afterAll(async () => {
|
|
71
|
+
closeDb();
|
|
72
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
73
|
+
try {
|
|
74
|
+
await unlink(TEST_DB_PATH + suffix);
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("an owned-only swarm skill resolves by name", async () => {
|
|
82
|
+
const owned = await swarmSkill("owned-only-skill", OTHER_ID);
|
|
83
|
+
|
|
84
|
+
const withCaller = await callSkillGet(server, CALLER_ID, { name: "owned-only-skill" });
|
|
85
|
+
expect(withCaller.success).toBe(true);
|
|
86
|
+
expect(withCaller.skill?.id).toBe(owned.id);
|
|
87
|
+
|
|
88
|
+
const anonymous = await callSkillGet(server, undefined, { name: "owned-only-skill" });
|
|
89
|
+
expect(anonymous.success).toBe(true);
|
|
90
|
+
expect(anonymous.skill?.id).toBe(owned.id);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("the ownerless row beats owned rows, including the caller's own", async () => {
|
|
94
|
+
// Live shape: qa-loom has an ownerless row and an agent-owned twin.
|
|
95
|
+
const owned = await swarmSkill("shadowed-skill", CALLER_ID);
|
|
96
|
+
const ownerless = await swarmSkill("shadowed-skill", null);
|
|
97
|
+
expect(owned.createdAt <= ownerless.createdAt).toBe(true);
|
|
98
|
+
|
|
99
|
+
const result = await callSkillGet(server, CALLER_ID, { name: "shadowed-skill" });
|
|
100
|
+
expect(result.success).toBe(true);
|
|
101
|
+
expect(result.skill?.id).toBe(ownerless.id);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("the caller's own row beats another agent's row", async () => {
|
|
105
|
+
await swarmSkill("two-owner-skill", OTHER_ID);
|
|
106
|
+
const mine = await swarmSkill("two-owner-skill", CALLER_ID);
|
|
107
|
+
|
|
108
|
+
const result = await callSkillGet(server, CALLER_ID, { name: "two-owner-skill" });
|
|
109
|
+
expect(result.success).toBe(true);
|
|
110
|
+
expect(result.skill?.id).toBe(mine.id);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("two foreign-owned rows return an error naming both ids", async () => {
|
|
114
|
+
const first = await swarmSkill("foreign-pair-skill", OTHER_ID);
|
|
115
|
+
const second = await swarmSkill("foreign-pair-skill", THIRD_ID);
|
|
116
|
+
|
|
117
|
+
const result = await callSkillGet(server, CALLER_ID, { name: "foreign-pair-skill" });
|
|
118
|
+
expect(result.success).toBe(false);
|
|
119
|
+
expect(result.message).toContain(first.id);
|
|
120
|
+
expect(result.message).toContain(second.id);
|
|
121
|
+
expect(result.message).toContain("skillId");
|
|
122
|
+
|
|
123
|
+
const byId = await callSkillGet(server, CALLER_ID, { skillId: second.id });
|
|
124
|
+
expect(byId.success).toBe(true);
|
|
125
|
+
expect(byId.skill?.id).toBe(second.id);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("the seed path still matches ownerless rows only", async () => {
|
|
129
|
+
await swarmSkill("seed-owned-only", OTHER_ID);
|
|
130
|
+
|
|
131
|
+
const item = { key: "seed-owned-only", contentHash: "" } as never;
|
|
132
|
+
expect(await skillsSeeder.upstreamHash(item)).toBeNull();
|
|
133
|
+
|
|
134
|
+
await swarmSkill("seed-owned-only", null);
|
|
135
|
+
expect(await skillsSeeder.upstreamHash(item)).not.toBeNull();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import type { WebClient } from "@slack/web-api";
|
|
3
3
|
import { withAutoJoin } from "../slack/channel-join";
|
|
4
|
+
import { resetExternalChannelStateForTesting } from "../slack/external-channel";
|
|
4
5
|
|
|
5
6
|
// Mirrors the shape @slack/web-api's platformErrorFromResult produces:
|
|
6
7
|
// message = "An API error occurred: <code>", data.error = "<code>"
|
|
@@ -37,6 +38,8 @@ function makeClient(opts: {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
describe("withAutoJoin", () => {
|
|
41
|
+
beforeEach(() => resetExternalChannelStateForTesting());
|
|
42
|
+
|
|
40
43
|
test("success: fn called once, join and info not called", async () => {
|
|
41
44
|
const { client, infoFn, joinFn } = makeClient({});
|
|
42
45
|
const fn = mock(() => Promise.resolve("ok"));
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { createServer, type Server } from "node:http";
|
|
3
|
+
import { closeDb, getSwarmConfigs, initDb } from "../be/db";
|
|
4
|
+
import { handleConfig } from "../http/config";
|
|
5
|
+
import { handleCore } from "../http/core";
|
|
6
|
+
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
7
|
+
import { listenOnFreePort } from "./test-net";
|
|
8
|
+
|
|
9
|
+
// A typo such as 'treu' must not reach the DB: parseEnvFlag would read it as
|
|
10
|
+
// false and silently turn the external-channel block off.
|
|
11
|
+
|
|
12
|
+
const API_KEY = "example-slack-external-config-test-key";
|
|
13
|
+
const KEY = "SLACK_BLOCK_EXTERNAL_CHANNELS";
|
|
14
|
+
|
|
15
|
+
let server: Server;
|
|
16
|
+
let baseUrl: string;
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
initDb(":memory:");
|
|
20
|
+
server = createServer(async (req, res) => {
|
|
21
|
+
if (await handleCore(req, res, req.headers["x-agent-id"] as string | undefined, API_KEY)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const segments = getPathSegments(req.url || "");
|
|
25
|
+
const query = parseQueryParams(req.url || "");
|
|
26
|
+
if (await handleConfig(req, res, segments, query)) return;
|
|
27
|
+
res.writeHead(404).end();
|
|
28
|
+
});
|
|
29
|
+
baseUrl = `http://127.0.0.1:${await listenOnFreePort(server)}`;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterAll(async () => {
|
|
33
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
34
|
+
closeDb();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function putConfig(value: string): Promise<Response> {
|
|
38
|
+
return fetch(`${baseUrl}/api/config`, {
|
|
39
|
+
method: "PUT",
|
|
40
|
+
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
|
|
41
|
+
body: JSON.stringify({ scope: "global", key: KEY, value }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function storedValue(): Promise<string | undefined> {
|
|
46
|
+
const rows = await getSwarmConfigs({ scope: "global", key: KEY });
|
|
47
|
+
return rows[0]?.value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("SLACK_BLOCK_EXTERNAL_CHANNELS config write", () => {
|
|
51
|
+
test("PUT /api/config rejects a non-boolean value and stores nothing", async () => {
|
|
52
|
+
const response = await putConfig("treu");
|
|
53
|
+
expect(response.status).toBe(400);
|
|
54
|
+
expect(await response.text()).toContain(`Invalid ${KEY}`);
|
|
55
|
+
expect(await storedValue()).toBeUndefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("PUT /api/config accepts boolean literals", async () => {
|
|
59
|
+
for (const value of ["true", "false", "1", "0"]) {
|
|
60
|
+
const response = await putConfig(value);
|
|
61
|
+
expect(response.status).toBe(200);
|
|
62
|
+
expect(await storedValue()).toBe(value);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|