@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,370 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeAll,
|
|
5
|
+
beforeEach,
|
|
6
|
+
describe,
|
|
7
|
+
expect,
|
|
8
|
+
mock,
|
|
9
|
+
spyOn,
|
|
10
|
+
test,
|
|
11
|
+
} from "bun:test";
|
|
12
|
+
import { unlinkSync } from "node:fs";
|
|
13
|
+
import type { WebClient } from "@slack/web-api";
|
|
14
|
+
import { closeDb, getDbClient, getKv, initDb, upsertKv } from "../be/db";
|
|
15
|
+
import {
|
|
16
|
+
EXTERNAL_ALERT_KV_NAMESPACE,
|
|
17
|
+
getChannelBlockReason,
|
|
18
|
+
getChannelExternality,
|
|
19
|
+
invalidateChannelExternality,
|
|
20
|
+
leaveExternalChannel,
|
|
21
|
+
resetExternalChannelStateForTesting,
|
|
22
|
+
scanExternalChannelsOnBoot,
|
|
23
|
+
shouldBlockChannel,
|
|
24
|
+
} from "../slack/external-channel";
|
|
25
|
+
|
|
26
|
+
const originalFlag = process.env.SLACK_BLOCK_EXTERNAL_CHANNELS;
|
|
27
|
+
const originalAlerts = process.env.SLACK_ALERTS_CHANNEL;
|
|
28
|
+
const TEST_DB_PATH = "./test-slack-external-channel.sqlite";
|
|
29
|
+
|
|
30
|
+
function removeDbFiles(): void {
|
|
31
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
32
|
+
try {
|
|
33
|
+
unlinkSync(`${TEST_DB_PATH}${suffix}`);
|
|
34
|
+
} catch {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
beforeAll(() => {
|
|
41
|
+
removeDbFiles();
|
|
42
|
+
initDb(TEST_DB_PATH);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterAll(() => {
|
|
46
|
+
closeDb();
|
|
47
|
+
removeDbFiles();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function makePlatformError(code: string, extra: Record<string, unknown> = {}): Error {
|
|
51
|
+
const err = new Error(`An API error occurred: ${code}`);
|
|
52
|
+
(err as unknown as { data: Record<string, unknown> }).data = { ok: false, error: code, ...extra };
|
|
53
|
+
return err;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function makeClient(opts: {
|
|
57
|
+
channel?: Record<string, unknown>;
|
|
58
|
+
info?: () => Promise<unknown>;
|
|
59
|
+
leave?: () => Promise<unknown>;
|
|
60
|
+
channels?: Array<Record<string, unknown>>;
|
|
61
|
+
}) {
|
|
62
|
+
const info = mock(opts.info ?? (() => Promise.resolve({ channel: opts.channel ?? {} })));
|
|
63
|
+
const leave = mock(opts.leave ?? (() => Promise.resolve({ ok: true })));
|
|
64
|
+
const postMessage = mock(() => Promise.resolve({ ok: true }));
|
|
65
|
+
const usersConversations = mock(() =>
|
|
66
|
+
Promise.resolve({ channels: opts.channels ?? [], response_metadata: { next_cursor: "" } }),
|
|
67
|
+
);
|
|
68
|
+
const client = {
|
|
69
|
+
conversations: { info, leave },
|
|
70
|
+
chat: { postMessage },
|
|
71
|
+
users: { conversations: usersConversations },
|
|
72
|
+
} as unknown as WebClient;
|
|
73
|
+
return { client, info, leave, postMessage, usersConversations };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function postedText(postMessage: ReturnType<typeof mock>): string {
|
|
77
|
+
return (postMessage.mock.calls[0]?.[0] as { text: string }).text;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
beforeEach(async () => {
|
|
81
|
+
resetExternalChannelStateForTesting();
|
|
82
|
+
await getDbClient().run("DELETE FROM kv_entries WHERE namespace = ?", [
|
|
83
|
+
EXTERNAL_ALERT_KV_NAMESPACE,
|
|
84
|
+
]);
|
|
85
|
+
process.env.SLACK_BLOCK_EXTERNAL_CHANNELS = "true";
|
|
86
|
+
process.env.SLACK_ALERTS_CHANNEL = "C_ALERTS";
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
afterEach(() => {
|
|
90
|
+
if (originalFlag === undefined) delete process.env.SLACK_BLOCK_EXTERNAL_CHANNELS;
|
|
91
|
+
else process.env.SLACK_BLOCK_EXTERNAL_CHANNELS = originalFlag;
|
|
92
|
+
if (originalAlerts === undefined) delete process.env.SLACK_ALERTS_CHANNEL;
|
|
93
|
+
else process.env.SLACK_ALERTS_CHANNEL = originalAlerts;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("getChannelExternality", () => {
|
|
97
|
+
test("is_ext_shared is external", async () => {
|
|
98
|
+
const { client } = makeClient({ channel: { is_ext_shared: true } });
|
|
99
|
+
expect(await getChannelExternality(client, "C1")).toBe("external");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("is_ext_shared with a foreign conversation_host_id is external", async () => {
|
|
103
|
+
const { client } = makeClient({
|
|
104
|
+
channel: { is_ext_shared: true, conversation_host_id: "T0FOREIGN1" },
|
|
105
|
+
});
|
|
106
|
+
expect(await getChannelExternality(client, "C1")).toBe("external");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("is_pending_ext_shared is external", async () => {
|
|
110
|
+
const { client } = makeClient({ channel: { is_pending_ext_shared: true } });
|
|
111
|
+
expect(await getChannelExternality(client, "C1")).toBe("external");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("is_org_shared with both ext flags false is internal", async () => {
|
|
115
|
+
const { client } = makeClient({
|
|
116
|
+
channel: {
|
|
117
|
+
is_shared: true,
|
|
118
|
+
is_org_shared: true,
|
|
119
|
+
is_ext_shared: false,
|
|
120
|
+
is_pending_ext_shared: false,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
expect(await getChannelExternality(client, "C1")).toBe("internal");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("conversations.info error is unknown and not cached", async () => {
|
|
127
|
+
const { client, info } = makeClient({ info: () => Promise.reject(new Error("boom")) });
|
|
128
|
+
expect(await getChannelExternality(client, "C1")).toBe("unknown");
|
|
129
|
+
expect(await getChannelExternality(client, "C1")).toBe("unknown");
|
|
130
|
+
expect(info).toHaveBeenCalledTimes(2);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("2 lookups inside the TTL make 1 API call; invalidate forces a fresh call", async () => {
|
|
134
|
+
const { client, info } = makeClient({ channel: { is_ext_shared: false } });
|
|
135
|
+
await getChannelExternality(client, "C1");
|
|
136
|
+
await getChannelExternality(client, "C1");
|
|
137
|
+
expect(info).toHaveBeenCalledTimes(1);
|
|
138
|
+
invalidateChannelExternality("C1");
|
|
139
|
+
await getChannelExternality(client, "C1");
|
|
140
|
+
expect(info).toHaveBeenCalledTimes(2);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("shouldBlockChannel", () => {
|
|
145
|
+
test("flag off never blocks and makes no API call", async () => {
|
|
146
|
+
process.env.SLACK_BLOCK_EXTERNAL_CHANNELS = "false";
|
|
147
|
+
const { client, info } = makeClient({ channel: { is_ext_shared: true } });
|
|
148
|
+
expect(await shouldBlockChannel(client, "C1", true)).toBe(false);
|
|
149
|
+
expect(info).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("envelope flag blocks without an API call", async () => {
|
|
153
|
+
const { client, info } = makeClient({});
|
|
154
|
+
expect(await shouldBlockChannel(client, "C1", true)).toBe(true);
|
|
155
|
+
expect(info).not.toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("unknown blocks as unverified (fail closed)", async () => {
|
|
159
|
+
const { client } = makeClient({ info: () => Promise.reject(new Error("boom")) });
|
|
160
|
+
expect(await shouldBlockChannel(client, "C1")).toBe(true);
|
|
161
|
+
expect(await getChannelBlockReason(client, "C1")).toBe("unverified");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("a verified internal channel is not blocked", async () => {
|
|
165
|
+
const { client } = makeClient({ channel: { is_ext_shared: false } });
|
|
166
|
+
expect(await getChannelBlockReason(client, "C1")).toBeNull();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("envelope evidence replaces a cached internal value", async () => {
|
|
170
|
+
let channel: Record<string, unknown> = { is_ext_shared: false };
|
|
171
|
+
const { client, info } = makeClient({ info: () => Promise.resolve({ channel }) });
|
|
172
|
+
expect(await getChannelExternality(client, "C1")).toBe("internal");
|
|
173
|
+
channel = { is_ext_shared: true };
|
|
174
|
+
expect(await shouldBlockChannel(client, "C1", true)).toBe(true);
|
|
175
|
+
// Reviewer repro: the next outbound check without an envelope must block too.
|
|
176
|
+
expect(await shouldBlockChannel(client, "C1")).toBe(true);
|
|
177
|
+
expect(info).toHaveBeenCalledTimes(1);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("an in-flight internal lookup does not overwrite envelope evidence", async () => {
|
|
181
|
+
let resolveInfo: (value: unknown) => void = () => {};
|
|
182
|
+
const { client } = makeClient({
|
|
183
|
+
info: () =>
|
|
184
|
+
new Promise((resolve) => {
|
|
185
|
+
resolveInfo = resolve;
|
|
186
|
+
}),
|
|
187
|
+
});
|
|
188
|
+
const inFlight = getChannelExternality(client, "C1");
|
|
189
|
+
expect(await shouldBlockChannel(client, "C1", true)).toBe(true);
|
|
190
|
+
resolveInfo({ channel: { is_ext_shared: false } });
|
|
191
|
+
expect(await inFlight).toBe("external");
|
|
192
|
+
expect(await shouldBlockChannel(client, "C1")).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("an in-flight lookup after invalidation is not cached", async () => {
|
|
196
|
+
let resolveInfo: (value: unknown) => void = () => {};
|
|
197
|
+
const { client, info } = makeClient({
|
|
198
|
+
info: () =>
|
|
199
|
+
new Promise((resolve) => {
|
|
200
|
+
resolveInfo = resolve;
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
const inFlight = getChannelExternality(client, "C1");
|
|
204
|
+
invalidateChannelExternality("C1");
|
|
205
|
+
resolveInfo({ channel: { is_ext_shared: false } });
|
|
206
|
+
expect(await inFlight).toBe("unknown");
|
|
207
|
+
const next = getChannelExternality(client, "C1");
|
|
208
|
+
resolveInfo({ channel: { is_ext_shared: false } });
|
|
209
|
+
expect(await next).toBe("internal");
|
|
210
|
+
expect(info).toHaveBeenCalledTimes(2);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("leaveExternalChannel", () => {
|
|
215
|
+
test("success: 1 leave and 1 alert with Leave: left", async () => {
|
|
216
|
+
const { client, leave, postMessage } = makeClient({});
|
|
217
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
218
|
+
expect(leave).toHaveBeenCalledTimes(1);
|
|
219
|
+
expect(postMessage).toHaveBeenCalledTimes(1);
|
|
220
|
+
expect((postMessage.mock.calls[0]?.[0] as { channel: string }).channel).toBe("C_ALERTS");
|
|
221
|
+
expect(postedText(postMessage)).toContain("Trigger: joined");
|
|
222
|
+
expect(postedText(postMessage)).toContain("Leave: left");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("missing_scope: failed line and scope hint", async () => {
|
|
226
|
+
const { client, postMessage } = makeClient({
|
|
227
|
+
leave: () =>
|
|
228
|
+
Promise.reject(makePlatformError("missing_scope", { needed: "channels:manage" })),
|
|
229
|
+
});
|
|
230
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
231
|
+
expect(postedText(postMessage)).toContain("Leave: failed (missing_scope)");
|
|
232
|
+
expect(postedText(postMessage)).toContain("Slack requires the `channels:manage` scope");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test.each([
|
|
236
|
+
"cant_leave_general",
|
|
237
|
+
"last_member",
|
|
238
|
+
"method_not_supported_for_channel_type",
|
|
239
|
+
])("%s maps to failed (<code>)", async (code) => {
|
|
240
|
+
const { client, postMessage } = makeClient({
|
|
241
|
+
leave: () => Promise.reject(makePlatformError(code)),
|
|
242
|
+
});
|
|
243
|
+
await leaveExternalChannel(client, "C1", "shared");
|
|
244
|
+
expect(postedText(postMessage)).toContain(`Leave: failed (${code})`);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("not_in_channel response counts as left", async () => {
|
|
248
|
+
const { client, postMessage } = makeClient({
|
|
249
|
+
leave: () => Promise.reject(makePlatformError("not_in_channel", { not_in_channel: true })),
|
|
250
|
+
});
|
|
251
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
252
|
+
expect(postedText(postMessage)).toContain("Leave: left");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("second call within 1 hour posts no second alert", async () => {
|
|
256
|
+
const { client, leave, postMessage } = makeClient({});
|
|
257
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
258
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
259
|
+
expect(leave).toHaveBeenCalledTimes(2);
|
|
260
|
+
expect(postMessage).toHaveBeenCalledTimes(1);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("alert dedupe survives a restart", async () => {
|
|
264
|
+
const first = makeClient({});
|
|
265
|
+
await leaveExternalChannel(first.client, "C1", "joined");
|
|
266
|
+
expect(first.postMessage).toHaveBeenCalledTimes(1);
|
|
267
|
+
|
|
268
|
+
// Restart: all process memory is gone; only kv_entries survives. (The test
|
|
269
|
+
// DB restores from an in-memory template, so it cannot be reopened here.)
|
|
270
|
+
resetExternalChannelStateForTesting();
|
|
271
|
+
|
|
272
|
+
const second = makeClient({});
|
|
273
|
+
await leaveExternalChannel(second.client, "C1", "joined");
|
|
274
|
+
expect(second.postMessage).not.toHaveBeenCalled();
|
|
275
|
+
const entry = await getKv(EXTERNAL_ALERT_KV_NAMESPACE, "C1:joined");
|
|
276
|
+
expect(entry?.value).toBe("sent");
|
|
277
|
+
expect(entry?.expiresAt).toBeGreaterThan(Date.now() + 59 * 60 * 1000);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("a failed alert post releases the key so the next attempt sends", async () => {
|
|
281
|
+
const error = spyOn(console, "error").mockImplementation(() => {});
|
|
282
|
+
try {
|
|
283
|
+
const { client, postMessage } = makeClient({});
|
|
284
|
+
postMessage.mockImplementationOnce(() => Promise.reject(new Error("slack down")));
|
|
285
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
286
|
+
expect(await getKv(EXTERNAL_ALERT_KV_NAMESPACE, "C1:joined")).toBeNull();
|
|
287
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
288
|
+
expect(postMessage).toHaveBeenCalledTimes(2);
|
|
289
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
290
|
+
expect(postMessage).toHaveBeenCalledTimes(2);
|
|
291
|
+
} finally {
|
|
292
|
+
error.mockRestore();
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("a stale sending reservation (crash mid-post) does not suppress the alert", async () => {
|
|
297
|
+
await upsertKv({
|
|
298
|
+
namespace: EXTERNAL_ALERT_KV_NAMESPACE,
|
|
299
|
+
key: "C1:joined",
|
|
300
|
+
value: "sending",
|
|
301
|
+
valueType: "string",
|
|
302
|
+
expiresAt: Date.now() - 1,
|
|
303
|
+
});
|
|
304
|
+
const { client, postMessage } = makeClient({});
|
|
305
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
306
|
+
expect(postMessage).toHaveBeenCalledTimes(1);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("an in-flight sending reservation suppresses a concurrent duplicate", async () => {
|
|
310
|
+
const { client, postMessage } = makeClient({});
|
|
311
|
+
await Promise.all([
|
|
312
|
+
leaveExternalChannel(client, "C1", "joined"),
|
|
313
|
+
leaveExternalChannel(client, "C1", "joined"),
|
|
314
|
+
]);
|
|
315
|
+
expect(postMessage).toHaveBeenCalledTimes(1);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("unset SLACK_ALERTS_CHANNEL: 0 posts and 1 warning", async () => {
|
|
319
|
+
delete process.env.SLACK_ALERTS_CHANNEL;
|
|
320
|
+
const warn = spyOn(console, "warn").mockImplementation(() => {});
|
|
321
|
+
try {
|
|
322
|
+
const { client, postMessage } = makeClient({});
|
|
323
|
+
await leaveExternalChannel(client, "C1", "joined");
|
|
324
|
+
expect(postMessage).not.toHaveBeenCalled();
|
|
325
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
326
|
+
} finally {
|
|
327
|
+
warn.mockRestore();
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe("scanExternalChannelsOnBoot", () => {
|
|
333
|
+
test("boot scan sweeps expired alert keys", async () => {
|
|
334
|
+
await upsertKv({
|
|
335
|
+
namespace: EXTERNAL_ALERT_KV_NAMESPACE,
|
|
336
|
+
key: "C_OLD:boot-scan",
|
|
337
|
+
value: "sent",
|
|
338
|
+
valueType: "string",
|
|
339
|
+
expiresAt: Date.now() - 1,
|
|
340
|
+
});
|
|
341
|
+
const { client } = makeClient({});
|
|
342
|
+
await scanExternalChannelsOnBoot(client);
|
|
343
|
+
const rows = await getDbClient().query<{ key: string }>(
|
|
344
|
+
"SELECT key FROM kv_entries WHERE namespace = ?",
|
|
345
|
+
[EXTERNAL_ALERT_KV_NAMESPACE],
|
|
346
|
+
);
|
|
347
|
+
expect(rows).toEqual([]);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("1 external + 1 internal: 1 report-only alert, 0 leaves", async () => {
|
|
351
|
+
const { client, leave, postMessage } = makeClient({
|
|
352
|
+
channels: [
|
|
353
|
+
{ id: "C_EXT", is_ext_shared: true },
|
|
354
|
+
{ id: "C_INT", is_ext_shared: false, is_pending_ext_shared: false },
|
|
355
|
+
],
|
|
356
|
+
});
|
|
357
|
+
await scanExternalChannelsOnBoot(client);
|
|
358
|
+
expect(leave).not.toHaveBeenCalled();
|
|
359
|
+
expect(postMessage).toHaveBeenCalledTimes(1);
|
|
360
|
+
expect(postedText(postMessage)).toContain("C_EXT");
|
|
361
|
+
expect(postedText(postMessage)).toContain("Leave: not attempted (report only)");
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("flag off: no Slack calls", async () => {
|
|
365
|
+
process.env.SLACK_BLOCK_EXTERNAL_CHANNELS = "false";
|
|
366
|
+
const { client, usersConversations } = makeClient({});
|
|
367
|
+
await scanExternalChannelsOnBoot(client);
|
|
368
|
+
expect(usersConversations).not.toHaveBeenCalled();
|
|
369
|
+
});
|
|
370
|
+
});
|
|
@@ -556,6 +556,40 @@ describe("Slack renderer v2", () => {
|
|
|
556
556
|
});
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
for (const delegationEnabled of [false, true]) {
|
|
560
|
+
for (const status of ["completed", "failed"] as const) {
|
|
561
|
+
test(`worker deferred ${status} wake posts no outcome with delegation=${delegationEnabled}`, async () => {
|
|
562
|
+
process.env.SLACK_RENDER_V2_DELEGATION = String(delegationEnabled);
|
|
563
|
+
if (delegationEnabled) await ensureSlackDelegationActivation();
|
|
564
|
+
const worker = await createAgent({ name: "Fast Coder", isLead: false, status: "idle" });
|
|
565
|
+
const { channelId, threadTs } = uniqueSlackAddress("C_WORKER_WAKE");
|
|
566
|
+
const wake = await createTaskExtended("Check PR #2136 CI at the expected head", {
|
|
567
|
+
agentId: worker.id,
|
|
568
|
+
source: "schedule",
|
|
569
|
+
taskType: "deferred",
|
|
570
|
+
slackChannelId: channelId,
|
|
571
|
+
slackThreadTs: threadTs,
|
|
572
|
+
contextKey: slackContextKey({ channelId, threadTs }),
|
|
573
|
+
});
|
|
574
|
+
await startTask(wake.id);
|
|
575
|
+
await ensureSlackThreadTree([wake.id]);
|
|
576
|
+
await processSlackRenderV2();
|
|
577
|
+
if (status === "failed") {
|
|
578
|
+
await failTask(wake.id, "CI is failing at expected head");
|
|
579
|
+
} else {
|
|
580
|
+
await completeTask(wake.id, "CI is green at expected head");
|
|
581
|
+
}
|
|
582
|
+
calls.length = 0;
|
|
583
|
+
|
|
584
|
+
await processSlackRenderV2();
|
|
585
|
+
await processSlackRenderV2();
|
|
586
|
+
|
|
587
|
+
expect(await getSlackOutcomeMessage(wake.id)).toBeNull();
|
|
588
|
+
expect(calls.filter((call) => call.method === "chat.startStream")).toHaveLength(0);
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
559
593
|
test("does not relay an ordinary scheduled task without Slack context", async () => {
|
|
560
594
|
const schedule = await createScheduledTask({
|
|
561
595
|
name: "ordinary-maintenance",
|
|
@@ -2474,7 +2508,7 @@ describe("Slack renderer v2", () => {
|
|
|
2474
2508
|
expect(calls).toContainEqual(doneReaction);
|
|
2475
2509
|
});
|
|
2476
2510
|
|
|
2477
|
-
test("
|
|
2511
|
+
test("re-defers retain one ⏳ card through multiple wakes and the final result", async () => {
|
|
2478
2512
|
const lead = await createAgent({ name: "Chained Lead", isLead: true, status: "idle" });
|
|
2479
2513
|
const { channelId, threadTs } = uniqueSlackAddress("C_DEFER_CHAIN");
|
|
2480
2514
|
const ask = await createTaskExtended("answer when the build is done", {
|
|
@@ -2513,18 +2547,60 @@ describe("Slack renderer v2", () => {
|
|
|
2513
2547
|
calls.length = 0;
|
|
2514
2548
|
await processSlackRenderV2();
|
|
2515
2549
|
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
expect(next?.payload.markdown_text).toBe("⏳ Checking back later");
|
|
2519
|
-
const nextCard = await getSlackOutcomeMessage(wake.id);
|
|
2520
|
-
expect(nextCard?.permalink).toBeTruthy();
|
|
2521
|
-
// The old card hands over to it rather than repeating the new ETA.
|
|
2550
|
+
expect(calls.filter((call) => call.method === "chat.startStream")).toHaveLength(0);
|
|
2551
|
+
expect(await getSlackOutcomeMessage(wake.id)).toBeNull();
|
|
2522
2552
|
const rewrite = calls.find(
|
|
2523
2553
|
(call) => call.method === "chat.update" && call.payload.ts === card?.ts,
|
|
2524
2554
|
);
|
|
2525
|
-
expect(rewrite?.payload.text).toBe(
|
|
2526
|
-
|
|
2527
|
-
|
|
2555
|
+
expect(rewrite?.payload.text).toBe("⏳ Checking back later");
|
|
2556
|
+
expect((await getSlackOutcomeMessage(ask.id))?.deferralResolvedAt).toBeUndefined();
|
|
2557
|
+
|
|
2558
|
+
const nextSchedule = await createScheduledTask({
|
|
2559
|
+
...schedule,
|
|
2560
|
+
name: "deferred-chain-next",
|
|
2561
|
+
parentTaskId: wake.id,
|
|
2562
|
+
});
|
|
2563
|
+
const nextWake = await createStandaloneScheduleTask(nextSchedule);
|
|
2564
|
+
await startTask(nextWake.id);
|
|
2565
|
+
await completeTask(nextWake.id, "Waiting on Builder — or today at 21:00 at the latest", {
|
|
2566
|
+
addTags: ["deferred"],
|
|
2567
|
+
deferredAt: new Date().toISOString(),
|
|
2568
|
+
});
|
|
2569
|
+
calls.length = 0;
|
|
2570
|
+
await processSlackRenderV2();
|
|
2571
|
+
expect(calls.filter((call) => call.method === "chat.startStream")).toHaveLength(0);
|
|
2572
|
+
expect(
|
|
2573
|
+
calls.find((call) => call.method === "chat.update" && call.payload.ts === card?.ts)?.payload
|
|
2574
|
+
.text,
|
|
2575
|
+
).toBe("⏳ Waiting on Builder");
|
|
2576
|
+
|
|
2577
|
+
calls.length = 0;
|
|
2578
|
+
await processSlackRenderV2();
|
|
2579
|
+
expect(
|
|
2580
|
+
calls.filter((call) => call.method === "chat.update" && call.payload.ts === card?.ts),
|
|
2581
|
+
).toHaveLength(0);
|
|
2582
|
+
|
|
2583
|
+
const finalSchedule = await createScheduledTask({
|
|
2584
|
+
...schedule,
|
|
2585
|
+
name: "deferred-chain-final",
|
|
2586
|
+
parentTaskId: nextWake.id,
|
|
2587
|
+
});
|
|
2588
|
+
const finalWake = await createStandaloneScheduleTask(finalSchedule);
|
|
2589
|
+
await startTask(finalWake.id);
|
|
2590
|
+
await completeTask(finalWake.id, "The build passed.");
|
|
2591
|
+
calls.length = 0;
|
|
2592
|
+
await processSlackRenderV2();
|
|
2593
|
+
expect(calls.filter((call) => call.method === "chat.startStream")).toHaveLength(0);
|
|
2594
|
+
expect(
|
|
2595
|
+
calls.find((call) => call.method === "chat.update" && call.payload.ts === card?.ts)?.payload
|
|
2596
|
+
.text,
|
|
2597
|
+
).toContain("The build passed.");
|
|
2598
|
+
expect((await getSlackOutcomeMessage(ask.id))?.deferralResolvedAt).toBeTruthy();
|
|
2599
|
+
calls.length = 0;
|
|
2600
|
+
await processSlackRenderV2();
|
|
2601
|
+
expect(
|
|
2602
|
+
calls.filter((call) => call.method === "chat.update" && call.payload.ts === card?.ts),
|
|
2603
|
+
).toHaveLength(0);
|
|
2528
2604
|
});
|
|
2529
2605
|
|
|
2530
2606
|
test("a wake-up that failed closes the deferral card as failed, not as done", async () => {
|
|
@@ -23,10 +23,11 @@ describe("Sonnet 5.5 registry", () => {
|
|
|
23
23
|
.filter((row) => row.provider === provider && row.model === model)
|
|
24
24
|
.map((row) => [row.tokenClass, row.pricePerMillionUsd]),
|
|
25
25
|
);
|
|
26
|
+
// Sonnet 5.5 cache hits cost 0.05x input, not the usual 0.1x.
|
|
26
27
|
expect(rates).toEqual({
|
|
27
28
|
input: 2,
|
|
28
29
|
output: 10,
|
|
29
|
-
cached_input: 0.
|
|
30
|
+
cached_input: 0.1,
|
|
30
31
|
cache_write: 2.5,
|
|
31
32
|
cache_write_1h: 4,
|
|
32
33
|
});
|
|
@@ -41,4 +42,11 @@ describe("Sonnet 5.5 registry", () => {
|
|
|
41
42
|
).toBe(2);
|
|
42
43
|
}
|
|
43
44
|
});
|
|
45
|
+
|
|
46
|
+
test("the shortname fallback seeds the same cache-read rate when the snapshot lacks the model", () => {
|
|
47
|
+
const row = buildModelsDevSeedRows({ anthropic: { models: {} } }).find(
|
|
48
|
+
(r) => r.provider === "claude" && r.model === "sonnet" && r.tokenClass === "cached_input",
|
|
49
|
+
);
|
|
50
|
+
expect(row?.pricePerMillionUsd).toBe(0.1);
|
|
51
|
+
});
|
|
44
52
|
});
|
|
@@ -246,12 +246,27 @@ describe("migration 117 consolidated connections-redesign schema", () => {
|
|
|
246
246
|
new Date().toISOString(),
|
|
247
247
|
new Bun.CryptoHasher("sha256").update(migration117).digest("hex"),
|
|
248
248
|
);
|
|
249
|
+
// 213 rebuilds script_connections with post-117 columns, so it must wait for 117 too.
|
|
250
|
+
const migration213 = readFileSync(
|
|
251
|
+
"src/be/migrations/213_script_connections_sql.sql",
|
|
252
|
+
"utf-8",
|
|
253
|
+
);
|
|
254
|
+
database
|
|
255
|
+
.prepare(
|
|
256
|
+
"INSERT INTO _migrations (version, name, applied_at, checksum) VALUES (?, ?, ?, ?)",
|
|
257
|
+
)
|
|
258
|
+
.run(
|
|
259
|
+
213,
|
|
260
|
+
"213_script_connections_sql",
|
|
261
|
+
new Date().toISOString(),
|
|
262
|
+
new Bun.CryptoHasher("sha256").update(migration213).digest("hex"),
|
|
263
|
+
);
|
|
249
264
|
runMigrations(database);
|
|
250
265
|
database.run(
|
|
251
266
|
`INSERT INTO script_connections (id, slug, kind, base_url)
|
|
252
267
|
VALUES ('existing', 'existing', 'raw', 'https://api.vendor.test')`,
|
|
253
268
|
);
|
|
254
|
-
database.run("DELETE FROM _migrations WHERE version
|
|
269
|
+
database.run("DELETE FROM _migrations WHERE version IN (117, 213)");
|
|
255
270
|
runMigrations(database);
|
|
256
271
|
expect(
|
|
257
272
|
database
|