@desplega.ai/agent-swarm 2.4.5 → 2.5.1
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 +4 -2
- package/dist/{acp-adapter-2wdkktv6.js → acp-adapter-7g6bpwbk.js} +8 -6
- package/dist/{actions-c40ebcm4.js → actions-w8xd014k.js} +23 -21
- package/dist/{amp-adapter-wb67m95z.js → amp-adapter-z4qvhacj.js} +2 -2
- package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
- package/dist/{app-353vre8g.js → app-nndcvftq.js} +3 -3
- package/dist/{assistant-sj1njh23.js → assistant-hjmqyscx.js} +24 -22
- package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
- package/dist/{boot-reembed-b9c3az3c.js → boot-reembed-r8rhf9h8.js} +1 -1
- package/dist/{boot-reembed-qdkvxeem.js → boot-reembed-vwcj2y5d.js} +1 -1
- package/dist/{boot-scrub-sweep-m3wh8eks.js → boot-scrub-sweep-fr9q36ne.js} +4 -4
- package/dist/{boot-seeds-j2273s54.js → boot-seeds-9jqwnegq.js} +4 -4
- package/dist/{claude-adapter-m7q0a085.js → claude-adapter-2f8b85df.js} +8 -6
- package/dist/{claude-managed-adapter-5ptb6tzs.js → claude-managed-adapter-gt6pa35d.js} +2 -2
- package/dist/{claude-managed-setup-smrqk5j5.js → claude-managed-setup-c3hp36ph.js} +4 -4
- package/dist/{claude-sdk-session-38xnz21e.js → claude-sdk-session-05kat8rv.js} +9012 -8928
- package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
- package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
- package/dist/{cli-1sp861s4.js → cli-0jy85zz4.js} +11 -11
- package/dist/{cli-j99azg3g.js → cli-1dp1zj04.js} +5 -5
- package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
- package/dist/{cli-94gwyg7m.js → cli-22rmcjkg.js} +9 -9
- package/dist/{cli-ms85309d.js → cli-2jg61wab.js} +11 -11
- package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
- package/dist/{cli-9yqgm9nd.js → cli-2wba1ac9.js} +161 -63
- package/dist/{cli-cxxe61f6.js → cli-3ee5z0q0.js} +2 -2
- package/dist/{cli-k8g16gz3.js → cli-3qv8naq8.js} +115 -119
- package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
- package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
- package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
- package/dist/{cli-848hxdwj.js → cli-6rg2jn2y.js} +2 -1
- package/dist/{cli-dk44dxec.js → cli-9mm5ade4.js} +1 -1
- package/dist/{cli-7c9qs530.js → cli-ajqhxtnz.js} +5 -5
- package/dist/{cli-be2161h1.js → cli-cah5detr.js} +6 -6
- package/dist/{cli-v8fjakwf.js → cli-cwyafhw1.js} +1 -1
- package/dist/{cli-wxbn3yfx.js → cli-cz5nmdxt.js} +2 -2
- package/dist/{cli-wt645v7q.js → cli-dabehxhp.js} +1 -1
- package/dist/{cli-vfha21c9.js → cli-e5bxnjps.js} +2 -2
- package/dist/{cli-37j04k05.js → cli-e9btqw4y.js} +13 -13
- package/dist/{cli-1syn2xy1.js → cli-eym3jg7w.js} +32 -18
- package/dist/{cli-ftc1g5tj.js → cli-fes51ya2.js} +3 -3
- package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
- package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
- package/dist/{cli-1f3qzw6y.js → cli-gwcf52gz.js} +2 -2
- package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
- package/dist/{cli-v0yrfmdh.js → cli-hadv0725.js} +3 -3
- package/dist/{cli-g35m2xwm.js → cli-j1eghqfm.js} +2 -2
- package/dist/{cli-qfxc84gh.js → cli-jpxsrxcb.js} +1 -1
- package/dist/{cli-n72rg53p.js → cli-kcsd1ez8.js} +848 -426
- package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
- package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
- package/dist/{cli-st8aw1m6.js → cli-mq7kg1b1.js} +4 -4
- package/dist/{cli-bnes4z6b.js → cli-mz2ygeyq.js} +3 -3
- package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
- package/dist/{cli-f01h25yt.js → cli-n6kmqngw.js} +2 -2
- package/dist/cli-nj12tn2c.js +1193 -0
- package/dist/{cli-1dmcq2eh.js → cli-nzfnt2gs.js} +1 -1
- package/dist/{cli-625tb7aq.js → cli-p859hcc8.js} +2 -2
- package/dist/{cli-smvcw9z5.js → cli-pasyvjmm.js} +1 -1
- package/dist/cli-pkaqk57w.js +128 -0
- package/dist/{cli-xpa64rek.js → cli-pyy4c9ch.js} +2362 -159
- package/dist/{cli-wt0jf9hm.js → cli-q9pzwxvk.js} +553 -193
- package/dist/{cli-z7d29rw9.js → cli-qfjm4gvt.js} +4 -4
- package/dist/{cli-p14m2cnp.js → cli-qfm6my86.js} +1 -1
- package/dist/{cli-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
- package/dist/{cli-4zfqgjc5.js → cli-r9fy0ddc.js} +2 -2
- package/dist/{cli-eww120g8.js → cli-rh5smt8t.js} +18 -4
- package/dist/{cli-a92r1127.js → cli-rngde2jv.js} +2 -2
- package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
- package/dist/{cli-fv5hepyy.js → cli-sn1vbtg2.js} +3 -3
- package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
- package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
- package/dist/{cli-43x64hv3.js → cli-v2g93k4v.js} +4 -4
- package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
- package/dist/{cli-3a45zcgm.js → cli-xc65x52q.js} +2 -2
- package/dist/{cli-f0g3pgvf.js → cli-xd4eqtr2.js} +2 -2
- package/dist/{cli-018r4jq6.js → cli-y3zyaee5.js} +8 -8
- package/dist/{cli-kpw04va0.js → cli-z2dv25mf.js} +1 -1
- package/dist/{cli-qwhy2qma.js → cli-z63a269p.js} +2 -2
- package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
- package/dist/{cli-tw4p90jb.js → cli-znvq0k3t.js} +5 -5
- package/dist/cli.js +29 -27
- package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
- package/dist/{codex-adapter-tn6k5mkg.js → codex-adapter-p4ptwar5.js} +6 -4
- package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
- package/dist/{codex-session-runner-7vjt7jbw.js → codex-session-runner-964c7dnk.js} +7 -5
- package/dist/{commands-t7rz9146.js → commands-mbvhbrcy.js} +2 -2
- package/dist/{controller-m2jasma5.js → controller-gnq1k1mf.js} +4 -4
- package/dist/{controllers-connect-9ftpex99.js → controllers-connect-6dkm71tg.js} +25 -23
- package/dist/{cursor-adapter-mjv79e4z.js → cursor-adapter-yq5xgfcg.js} +1 -1
- package/dist/{db-6sfrn9g3.js → db-88wcymxt.js} +14 -2
- package/dist/{db-rn673r06.js → db-af9y8nkf.js} +3 -3
- package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
- package/dist/{dispatcher-c155xz0p.js → dispatcher-606ydx5v.js} +22 -20
- package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
- package/dist/{dsh-adapter-3mmszc1x.js → dsh-adapter-tvhc4k2d.js} +1 -1
- package/dist/{e2b-16d6ch39.js → e2b-9gkdhm3c.js} +2 -2
- package/dist/{e2b-zn0b4w5w.js → e2b-cjtf8k1z.js} +2 -2
- package/dist/{easter-egg-3d-5cgynaja.js → easter-egg-3d-e4qv8dxj.js} +1 -1
- package/dist/{event-bus-9vpk8gmq.js → event-bus-3z78ep5n.js} +1 -1
- package/dist/{execute-t4e6m3rw.js → execute-0xpz8950.js} +85 -23
- package/dist/{external-channel-35k6tkv8.js → external-channel-y8q5z8z2.js} +2 -2
- package/dist/{google-generative-ai-bmq8gkf3.js → google-generative-ai-mk9vcpe1.js} +15 -15
- package/dist/{google-vertex-b1rvp52s.js → google-vertex-q580kz8k.js} +20 -20
- package/dist/{grok-adapter-tfrq7tg1.js → grok-adapter-39aha52t.js} +9 -7
- package/dist/handlers-rk9dqech.js +41 -0
- package/dist/{heartbeat-dyg4npkp.js → heartbeat-jfe9zfjr.js} +25 -23
- package/dist/{hook-drspbwpe.js → hook-b1mez1m8.js} +9 -7
- package/dist/{hook-x26b31bg.js → hook-ffevzed1.js} +7 -5
- package/dist/{http-pk972xyw.js → http-d5b44f1r.js} +544 -76
- package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
- package/dist/index-4q3vj1yr.js +46 -0
- package/dist/{index-arytgj5q.js → index-5en23s5z.js} +1 -1
- package/dist/index-d2qeem97.js +42 -0
- package/dist/index-mj46rk9k.js +43 -0
- package/dist/{index-jwxkkex2.js → index-nq8hhv28.js} +13 -11
- package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
- package/dist/{keepalive-7yrng7px.js → keepalive-wsby1btq.js} +4 -4
- package/dist/lead-paa141j6.js +79 -0
- package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
- package/dist/{maintenance-e6cemdqt.js → maintenance-vnshcyv9.js} +1 -1
- package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
- package/dist/{node-de6akczr.js → node-jzjj7r9x.js} +1 -1
- package/dist/{oauth-refresh-sweep-yw3xdbcj.js → oauth-refresh-sweep-d6sqn5np.js} +3 -3
- package/dist/{onboard-0a9y5yrc.js → onboard-nr4xhpzy.js} +2 -2
- package/dist/{openai-codex-responses-de9pvs4d.js → openai-codex-responses-79s8xnbh.js} +29 -23
- package/dist/{openai-completions-mtqge3nf.js → openai-completions-tdbyts0x.js} +21 -20
- package/dist/{openai-decisions-ygk4kkxd.js → openai-decisions-bqhnjyfz.js} +2 -1
- package/dist/{openai-responses-dkyng71g.js → openai-responses-dm3t5vqp.js} +26 -17
- package/dist/{opencode-adapter-x4tqzhkh.js → opencode-adapter-e56086m1.js} +7 -5
- package/dist/{openrouter-images-wvv7camj.js → openrouter-images-ssttr1y4.js} +6 -3
- package/dist/{otel-impl-trjpkfb7.js → otel-impl-ytn9wyv9.js} +2 -2
- package/dist/{pi-messages-nhh1pkz2.js → pi-messages-3nf8bq1a.js} +6 -4
- package/dist/{pi-mono-adapter-mcfkn9vf.js → pi-mono-adapter-tzps8f38.js} +9 -37
- package/dist/{pricing-refresh-d2b5my0y.js → pricing-refresh-pva04zee.js} +3 -3
- package/dist/{rbac-roles-nxh5ahwk.js → rbac-roles-7rc1q3g4.js} +1 -1
- package/dist/{rbac-roles-hey3yv9d.js → rbac-roles-ff1ffmec.js} +2 -2
- package/dist/{render-v2-rr3p2atg.js → render-v2-pyfsg9zk.js} +3 -3
- package/dist/{resume-2efkf1s1.js → resume-phj8xhqr.js} +10 -8
- package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
- package/dist/{session-access-s2mttxpw.js → session-access-jzx2z9h5.js} +6 -4
- package/dist/{setup-vypma5yt.js → setup-rfg0f0sg.js} +1 -1
- package/dist/{typesafe-system-one-2hgpayzy.js → typesafe-system-one-bre38hvs.js} +4 -2
- package/dist/{virtual-modules-4r3zkc20.js → virtual-modules-mnfbh5mr.js} +8 -2
- package/dist/{work-objects-rvhckdkp.js → work-objects-44z2yjg8.js} +25 -23
- package/dist/worker-zsfqzxhm.js +79 -0
- package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
- package/openapi.json +785 -5
- package/package.json +9 -9
- package/src/assets/key.ts +17 -0
- package/src/azure-devops/handlers.ts +1 -1
- package/src/be/db/approvals.ts +18 -0
- package/src/be/db/session-moves.ts +164 -0
- package/src/be/db/session-tree.ts +384 -0
- package/src/be/db/tasks/read.ts +40 -4
- package/src/be/db/workflow-runs.ts +96 -0
- package/src/be/db.ts +80 -236
- package/src/be/onboarding.ts +3 -0
- package/src/be/skill-sync.ts +43 -2
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/bitbucket/api.ts +81 -0
- package/src/bitbucket/auth.ts +64 -0
- package/src/bitbucket/handlers.ts +434 -0
- package/src/bitbucket/index.ts +28 -0
- package/src/bitbucket/templates.ts +134 -0
- package/src/bitbucket/types.ts +65 -0
- package/src/commands/runner.ts +55 -3
- package/src/e2b/env.ts +1 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +2 -2
- package/src/extensions/post-bridge.ts +1 -0
- package/src/github/handlers.ts +6 -6
- package/src/gitlab/auth.ts +8 -2
- package/src/gitlab/handlers.ts +63 -15
- package/src/gitlab/index.ts +1 -1
- package/src/http/assets.ts +14 -2
- package/src/http/core.ts +12 -0
- package/src/http/index.ts +4 -0
- package/src/http/onboarding.ts +1 -0
- package/src/http/sessions.ts +366 -12
- package/src/http/skills.ts +48 -1
- package/src/http/tasks.ts +1 -1
- package/src/http/users.ts +1 -0
- package/src/http/webhooks.ts +141 -0
- package/src/integrations/chat-sdk/adapters.ts +19 -3
- package/src/integrations/chat-sdk/facts.ts +30 -12
- package/src/integrations/chat-sdk/outbound.ts +38 -1
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +5 -53
- package/src/scripts-runtime/types/swarm-extension.d.ts +6 -4
- package/src/slack/handlers.test.ts +158 -0
- package/src/slack/handlers.ts +19 -28
- package/src/tasks/context-key.ts +61 -1
- package/src/tests/bitbucket-auth.test.ts +156 -0
- package/src/tests/bitbucket-handlers.test.ts +425 -0
- package/src/tests/bitbucket-webhook.test.ts +220 -0
- package/src/tests/chat-sdk-inbound.test.ts +39 -0
- package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
- package/src/tests/context-key.test.ts +50 -0
- package/src/tests/core-auth.test.ts +5 -0
- package/src/tests/db-task-read-contract.test.ts +7 -0
- package/src/tests/db-task-write-contract.test.ts +6 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/fixtures/bitbucket/pullrequest-comment-created-inline.json +174 -0
- package/src/tests/fixtures/bitbucket/pullrequest-comment-created.json +170 -0
- package/src/tests/fixtures/bitbucket/pullrequest-created-mention.json +132 -0
- package/src/tests/fixtures/bitbucket/pullrequest-created-reviewer.json +140 -0
- package/src/tests/fixtures/bitbucket/pullrequest-fulfilled.json +134 -0
- package/src/tests/fixtures/bitbucket/pullrequest-rejected.json +133 -0
- package/src/tests/generate-mcp-docs.test.ts +101 -0
- package/src/tests/gitlab-auth.test.ts +13 -5
- package/src/tests/gitlab-handlers.test.ts +273 -25
- package/src/tests/gitlab-vcs-db.test.ts +4 -4
- package/src/tests/integration-disable-flags.test.ts +36 -0
- package/src/tests/mcp-tools.test.ts +1 -1
- package/src/tests/onboarding.test.ts +12 -0
- package/src/tests/pi-codemode.test.ts +8 -2
- package/src/tests/preload-env.test.ts +57 -0
- package/src/tests/preload.ts +21 -0
- package/src/tests/prompt-template-remaining.test.ts +38 -0
- package/src/tests/run-root-coverage.test.ts +39 -0
- package/src/tests/runner-bitbucket-vcs.test.ts +191 -0
- package/src/tests/runner-skills-refresh.test.ts +58 -0
- package/src/tests/secret-scrubber.test.ts +15 -0
- package/src/tests/sessions-move.test.ts +576 -0
- package/src/tests/sessions-tree.test.ts +493 -0
- package/src/tests/skill-files-http.test.ts +50 -0
- package/src/tests/skills-bundle.test.ts +99 -0
- package/src/tests/swarm-config-export-to-env.test.ts +2 -1
- package/src/tests/vcs-bitbucket.test.ts +91 -0
- package/src/tests/vcs-provider-isolation.test.ts +123 -0
- package/src/tests/vcs-provider.test.ts +16 -1
- package/src/tests/vcs-tracking.test.ts +3 -3
- package/src/tests/workflow-foreach-bodies.test.ts +637 -0
- package/src/tests/workflow-foreach-concurrency.test.ts +342 -0
- package/src/tests/workflow-foreach-retry.test.ts +537 -0
- package/src/tests/workflow-foreach.test.ts +44 -23
- package/src/tests/workflow-step-claims.test.ts +6 -6
- package/src/tests/workflow-tla-races.test.ts +2 -2
- package/src/tools/script-delete.ts +1 -1
- package/src/tools/workflows/create-workflow.ts +5 -3
- package/src/types.ts +3 -2
- package/src/utils/config-env-export.ts +1 -0
- package/src/utils/secret-scrubber.ts +4 -0
- package/src/utils/skills-refresh.ts +83 -43
- package/src/vcs/bitbucket.ts +58 -0
- package/src/vcs/index.ts +2 -0
- package/src/vcs/types.ts +2 -2
- package/src/workflows/approval-resolution.ts +4 -1
- package/src/workflows/checkpoint.ts +9 -1
- package/src/workflows/constants.ts +3 -0
- package/src/workflows/definition.ts +48 -17
- package/src/workflows/engine.ts +25 -15
- package/src/workflows/executors/foreach.ts +45 -99
- package/src/workflows/foreach-body.ts +146 -0
- package/src/workflows/foreach-driver.ts +552 -0
- package/src/workflows/foreach-join.ts +206 -7
- package/src/workflows/node-timeout.ts +21 -0
- package/src/workflows/recovery.ts +68 -3
- package/src/workflows/resume.ts +221 -43
- package/src/workflows/retry-poller.ts +39 -24
- package/src/workflows/step-input.ts +59 -0
- package/src/workflows/task-step-routing.ts +114 -56
- package/templates/skills/slack-interaction/content.md +5 -0
- package/templates/skills/work-on-task/SKILL.md +1 -1
- package/templates/skills/work-on-task/content.md +1 -1
- package/dist/cli-9hstj09h.js +0 -2657
- package/dist/cli-yw91rnmw.js +0 -13
- package/dist/handlers-7gw64dxw.js +0 -39
- package/dist/index-bgdyvcnv.js +0 -40
- package/dist/index-gabbcdn2.js +0 -43
- package/dist/index-tfb26zhy.js +0 -41
- package/dist/lead-7e02z839.js +0 -77
- package/dist/worker-5snytpbd.js +0 -77
package/src/tests/preload.ts
CHANGED
|
@@ -22,6 +22,27 @@ import { startHangWatchdog } from "./hang-watchdog";
|
|
|
22
22
|
|
|
23
23
|
startHangWatchdog();
|
|
24
24
|
|
|
25
|
+
// Worker deployments configure Slack rendering, links, and organization defaults.
|
|
26
|
+
// Start tests from the same unset defaults as CI; suites can still opt in by
|
|
27
|
+
// setting their own values after the preload runs.
|
|
28
|
+
for (const key of Object.keys(process.env)) {
|
|
29
|
+
if (
|
|
30
|
+
key.startsWith("SLACK_") ||
|
|
31
|
+
[
|
|
32
|
+
"COMB_ENABLED",
|
|
33
|
+
"APP_URL",
|
|
34
|
+
"AGENT_FS_API_URL",
|
|
35
|
+
"AGENT_FS_LIVE_URL",
|
|
36
|
+
"AGENT_FS_DEFAULT_ORG_ID",
|
|
37
|
+
"AGENT_FS_DEFAULT_DRIVE_ID",
|
|
38
|
+
"SWARM_ORG_NAME",
|
|
39
|
+
"SWARM_ORG_WEBSITE",
|
|
40
|
+
].includes(key)
|
|
41
|
+
) {
|
|
42
|
+
delete process.env[key];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
25
46
|
// The API drain (src/be/api-drain.ts) is off unless API_DRAIN_MAX_MS is set,
|
|
26
47
|
// but a shell can inherit it (a swarm worker container gets the deploy's value).
|
|
27
48
|
// Test servers are stopped with SIGTERM and their fixtures leave in_progress rows
|
|
@@ -6,6 +6,7 @@ import { resolveTemplate } from "../prompts/resolver";
|
|
|
6
6
|
|
|
7
7
|
// Side-effect imports: register all templates from each source
|
|
8
8
|
import "../gitlab/templates";
|
|
9
|
+
import "../bitbucket/templates";
|
|
9
10
|
import "../agentmail/templates";
|
|
10
11
|
import "../linear/templates";
|
|
11
12
|
import "../heartbeat/templates";
|
|
@@ -28,6 +29,7 @@ async function ensureTemplatesRegistered(): Promise<void> {
|
|
|
28
29
|
// module state across parallel test files, a single-template check is racy.
|
|
29
30
|
const ts = Date.now();
|
|
30
31
|
await import(`../gitlab/templates?t=${ts}`);
|
|
32
|
+
await import(`../bitbucket/templates?t=${ts}`);
|
|
31
33
|
await import(`../agentmail/templates?t=${ts}`);
|
|
32
34
|
await import(`../linear/templates?t=${ts}`);
|
|
33
35
|
await import(`../heartbeat/templates?t=${ts}`);
|
|
@@ -94,6 +96,42 @@ describe("template registration — all sources", () => {
|
|
|
94
96
|
expect(eventTypes).toContain("gitlab.pipeline.failed");
|
|
95
97
|
});
|
|
96
98
|
|
|
99
|
+
test("Bitbucket templates are registered (2 common + 3 event)", () => {
|
|
100
|
+
const eventTypes = getAllTemplateDefinitions().map((d) => d.eventType);
|
|
101
|
+
expect(eventTypes).toContain("common.delegation_instruction.bitbucket");
|
|
102
|
+
expect(eventTypes).toContain("common.command_suggestions.bitbucket_pr");
|
|
103
|
+
expect(eventTypes).toContain("bitbucket.pull_request.opened");
|
|
104
|
+
expect(eventTypes).toContain("bitbucket.pull_request.review_requested");
|
|
105
|
+
expect(eventTypes).toContain("bitbucket.comment.mentioned");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("Bitbucket templates resolve with no unresolved variables", () => {
|
|
109
|
+
const shared = {
|
|
110
|
+
pr_id: 42,
|
|
111
|
+
pr_title: "Fix retry",
|
|
112
|
+
repo: "acme-ws/widgets",
|
|
113
|
+
username: "Dana",
|
|
114
|
+
pr_url: "https://bitbucket.org/acme-ws/widgets/pull-requests/42",
|
|
115
|
+
};
|
|
116
|
+
const pr = { ...shared, source_branch: "feat", target_branch: "main", context_section: "" };
|
|
117
|
+
for (const id of ["bitbucket.pull_request.opened", "bitbucket.pull_request.review_requested"]) {
|
|
118
|
+
const result = resolveTemplate(id, pr);
|
|
119
|
+
expect(result.skipped).toBe(false);
|
|
120
|
+
expect(result.unresolved).toEqual([]);
|
|
121
|
+
expect(result.text).toContain("[Bitbucket PR #42]");
|
|
122
|
+
expect(result.text).toContain("api.bitbucket.org/2.0/repositories/acme-ws/widgets");
|
|
123
|
+
}
|
|
124
|
+
const comment = resolveTemplate("bitbucket.comment.mentioned", {
|
|
125
|
+
...shared,
|
|
126
|
+
location_section: "File: src/a.ts:3\n",
|
|
127
|
+
context: "please fix",
|
|
128
|
+
existing_task_note: "",
|
|
129
|
+
});
|
|
130
|
+
expect(comment.unresolved).toEqual([]);
|
|
131
|
+
expect(comment.text).toContain("File: src/a.ts:3");
|
|
132
|
+
expect(comment.text).toContain("please fix");
|
|
133
|
+
});
|
|
134
|
+
|
|
97
135
|
test("AgentMail templates are registered (5 event)", () => {
|
|
98
136
|
const all = getAllTemplateDefinitions();
|
|
99
137
|
const eventTypes = all.map((d) => d.eventType);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mergeLcov } from "../../scripts/run-root-coverage";
|
|
3
|
+
|
|
4
|
+
describe("mergeLcov", () => {
|
|
5
|
+
test("disjoint function hits remain a conservative lower bound without FN/FNDA identities", () => {
|
|
6
|
+
// a.test.ts calls a(); b.test.ts calls b() in the same two-function module.
|
|
7
|
+
const report = "SF:src/example.ts\nFNF:2\nFNH:1\nend_of_record\n";
|
|
8
|
+
const merged = mergeLcov([report, report]);
|
|
9
|
+
|
|
10
|
+
expect(merged).toContain("FNF:2\nFNH:1\n");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("DA hits sum across reports and LH counts lines hit in any report", () => {
|
|
14
|
+
const merged = mergeLcov([
|
|
15
|
+
"SF:src/example.ts\nDA:1,2\nDA:2,0\nDA:3,0\nend_of_record\n",
|
|
16
|
+
"SF:src/example.ts\nDA:1,3\nDA:2,1\nDA:3,0\nend_of_record\n",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
expect(merged).toBe(
|
|
20
|
+
"TN:\nSF:src/example.ts\nFNF:0\nFNH:0\nDA:1,5\nDA:2,1\nDA:3,0\nLF:3\nLH:2\nend_of_record\n",
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("FNH never exceeds FNF when merging function totals", () => {
|
|
25
|
+
const reports = [
|
|
26
|
+
"SF:src/example.ts\nFNF:2\nFNH:2\nend_of_record\n",
|
|
27
|
+
"SF:src/example.ts\nFNF:3\nFNH:1\nend_of_record\n",
|
|
28
|
+
];
|
|
29
|
+
for (const inputs of [reports, reports.toReversed(), [...reports, ...reports]]) {
|
|
30
|
+
const merged = mergeLcov(inputs);
|
|
31
|
+
const found = Number(merged.match(/^FNF:(\d+)$/m)![1]);
|
|
32
|
+
const hit = Number(merged.match(/^FNH:(\d+)$/m)![1]);
|
|
33
|
+
|
|
34
|
+
expect(found).toBe(3);
|
|
35
|
+
expect(hit).toBe(2);
|
|
36
|
+
expect(hit).toBeLessThanOrEqual(found);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker-side Bitbucket PR linking. There is no Bitbucket CLI in the worker
|
|
3
|
+
* image, so `detectVcsForTask` asks the Bitbucket REST API (via `fetch`) for an
|
|
4
|
+
* open PR on the current branch and PATCHes `/api/tasks/:id/vcs` when it finds
|
|
5
|
+
* one. Both calls go through `globalThis.fetch`, which these tests stub, so no
|
|
6
|
+
* server or port is involved. The git side runs against a real temp repo.
|
|
7
|
+
*/
|
|
8
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { detectVcsForTask, findBitbucketOpenPullRequest } from "../commands/runner";
|
|
13
|
+
import { expectChildOk, runChild } from "./test-proc";
|
|
14
|
+
|
|
15
|
+
const API_URL = "http://swarm.invalid";
|
|
16
|
+
const BRANCH = "feature/bb-link";
|
|
17
|
+
const PR_HTML = "https://bitbucket.org/acme-ws/web.app/pull-requests/17";
|
|
18
|
+
|
|
19
|
+
type Call = { url: string; method: string; headers: Headers; body: string };
|
|
20
|
+
type BitbucketReply = { status: number; body: unknown } | "throw";
|
|
21
|
+
|
|
22
|
+
const originalFetch = globalThis.fetch;
|
|
23
|
+
const originalToken = process.env.BITBUCKET_TOKEN;
|
|
24
|
+
let calls: Call[] = [];
|
|
25
|
+
let bitbucketReply: BitbucketReply = { status: 200, body: { values: [] } };
|
|
26
|
+
let repoDir = "";
|
|
27
|
+
let taskSeq = 0;
|
|
28
|
+
|
|
29
|
+
function nextTaskId(): string {
|
|
30
|
+
taskSeq += 1;
|
|
31
|
+
return `00000000-0000-4000-8000-${String(taskSeq).padStart(12, "0")}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function git(args: string[]): Promise<void> {
|
|
35
|
+
expectChildOk(await runChild(["git", "-C", repoDir, ...args]), `git ${args.join(" ")}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function stubFetch(): void {
|
|
39
|
+
globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
40
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
41
|
+
calls.push({
|
|
42
|
+
url,
|
|
43
|
+
method: init?.method ?? "GET",
|
|
44
|
+
headers: new Headers(init?.headers),
|
|
45
|
+
body: typeof init?.body === "string" ? init.body : "",
|
|
46
|
+
});
|
|
47
|
+
if (url.startsWith("https://api.bitbucket.org/")) {
|
|
48
|
+
if (bitbucketReply === "throw") throw new TypeError("network down");
|
|
49
|
+
return new Response(JSON.stringify(bitbucketReply.body), {
|
|
50
|
+
status: bitbucketReply.status,
|
|
51
|
+
headers: { "Content-Type": "application/json" },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return new Response("{}", { status: 200, headers: { "Content-Type": "application/json" } });
|
|
55
|
+
}) as typeof fetch;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const bitbucketCalls = () => calls.filter((c) => c.url.startsWith("https://api.bitbucket.org/"));
|
|
59
|
+
const patchCalls = () => calls.filter((c) => c.method === "PATCH");
|
|
60
|
+
|
|
61
|
+
beforeAll(async () => {
|
|
62
|
+
repoDir = await mkdtemp(join(tmpdir(), "runner-bb-vcs-"));
|
|
63
|
+
await git(["init", "--quiet"]);
|
|
64
|
+
await git(["checkout", "--quiet", "-b", BRANCH]);
|
|
65
|
+
await git(["remote", "add", "origin", "https://bitbucket.org/acme-ws/web.app.git"]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
afterAll(async () => {
|
|
69
|
+
await rm(repoDir, { recursive: true, force: true });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
calls = [];
|
|
74
|
+
bitbucketReply = { status: 200, body: { values: [] } };
|
|
75
|
+
process.env.BITBUCKET_TOKEN = "example-bb-token";
|
|
76
|
+
stubFetch();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
afterEach(() => {
|
|
80
|
+
globalThis.fetch = originalFetch;
|
|
81
|
+
if (originalToken === undefined) delete process.env.BITBUCKET_TOKEN;
|
|
82
|
+
else process.env.BITBUCKET_TOKEN = originalToken;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("detectVcsForTask on a Bitbucket remote", () => {
|
|
86
|
+
test("an open PR for the branch links the task", async () => {
|
|
87
|
+
bitbucketReply = {
|
|
88
|
+
status: 200,
|
|
89
|
+
body: { values: [{ id: 17, links: { html: { href: PR_HTML } } }] },
|
|
90
|
+
};
|
|
91
|
+
const taskId = nextTaskId();
|
|
92
|
+
await detectVcsForTask(API_URL, "swarm-key", taskId, repoDir);
|
|
93
|
+
|
|
94
|
+
const [lookup] = bitbucketCalls();
|
|
95
|
+
expect(lookup).toBeDefined();
|
|
96
|
+
const lookupUrl = new URL(lookup!.url);
|
|
97
|
+
expect(lookupUrl.pathname).toBe("/2.0/repositories/acme-ws/web.app/pullrequests");
|
|
98
|
+
expect(lookupUrl.searchParams.get("q")).toBe(`source.branch.name="${BRANCH}" AND state="OPEN"`);
|
|
99
|
+
expect(lookupUrl.searchParams.get("pagelen")).toBe("1");
|
|
100
|
+
expect(lookup!.headers.get("authorization")).toBe("Bearer example-bb-token");
|
|
101
|
+
|
|
102
|
+
const patches = patchCalls();
|
|
103
|
+
expect(patches).toHaveLength(1);
|
|
104
|
+
expect(patches[0]!.url).toBe(`${API_URL}/api/tasks/${taskId}/vcs`);
|
|
105
|
+
expect(patches[0]!.headers.get("authorization")).toBe("Bearer swarm-key");
|
|
106
|
+
expect(JSON.parse(patches[0]!.body)).toEqual({
|
|
107
|
+
vcsProvider: "bitbucket",
|
|
108
|
+
vcsRepo: "acme-ws/web.app",
|
|
109
|
+
vcsNumber: 17,
|
|
110
|
+
vcsUrl: PR_HTML,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("no open PR sends no PATCH", async () => {
|
|
115
|
+
await detectVcsForTask(API_URL, "swarm-key", nextTaskId(), repoDir);
|
|
116
|
+
expect(bitbucketCalls()).toHaveLength(1);
|
|
117
|
+
expect(patchCalls()).toHaveLength(0);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("a non-200 answer is swallowed and sends no PATCH", async () => {
|
|
121
|
+
bitbucketReply = { status: 401, body: { type: "error", error: { message: "Unauthorized" } } };
|
|
122
|
+
await expect(
|
|
123
|
+
detectVcsForTask(API_URL, "swarm-key", nextTaskId(), repoDir),
|
|
124
|
+
).resolves.toBeUndefined();
|
|
125
|
+
expect(bitbucketCalls()).toHaveLength(1);
|
|
126
|
+
expect(patchCalls()).toHaveLength(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("a network failure is swallowed and sends no PATCH", async () => {
|
|
130
|
+
bitbucketReply = "throw";
|
|
131
|
+
await expect(
|
|
132
|
+
detectVcsForTask(API_URL, "swarm-key", nextTaskId(), repoDir),
|
|
133
|
+
).resolves.toBeUndefined();
|
|
134
|
+
expect(patchCalls()).toHaveLength(0);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("without BITBUCKET_TOKEN it calls nothing", async () => {
|
|
138
|
+
delete process.env.BITBUCKET_TOKEN;
|
|
139
|
+
await detectVcsForTask(API_URL, "swarm-key", nextTaskId(), repoDir);
|
|
140
|
+
expect(calls).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("findBitbucketOpenPullRequest", () => {
|
|
145
|
+
test("escapes quotes in the branch name inside the BBQL query", async () => {
|
|
146
|
+
let seen = "";
|
|
147
|
+
const fetchImpl = (async (input: string | URL | Request) => {
|
|
148
|
+
seen = String(input);
|
|
149
|
+
return new Response(JSON.stringify({ values: [] }), { status: 200 });
|
|
150
|
+
}) as typeof fetch;
|
|
151
|
+
await findBitbucketOpenPullRequest("git@bitbucket.org:ws/repo.git", 'we"ird', {
|
|
152
|
+
token: "t",
|
|
153
|
+
fetchImpl,
|
|
154
|
+
});
|
|
155
|
+
expect(new URL(seen).searchParams.get("q")).toBe(
|
|
156
|
+
'source.branch.name="we\\"ird" AND state="OPEN"',
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("rejects a reply without a numeric id or an html link", async () => {
|
|
161
|
+
for (const values of [
|
|
162
|
+
[{ id: "17", links: { html: { href: PR_HTML } } }],
|
|
163
|
+
[{ id: 17, links: {} }],
|
|
164
|
+
[{}],
|
|
165
|
+
]) {
|
|
166
|
+
const fetchImpl = (async () =>
|
|
167
|
+
new Response(JSON.stringify({ values }), { status: 200 })) as unknown as typeof fetch;
|
|
168
|
+
expect(
|
|
169
|
+
await findBitbucketOpenPullRequest("https://bitbucket.org/ws/repo", "b", {
|
|
170
|
+
token: "t",
|
|
171
|
+
fetchImpl,
|
|
172
|
+
}),
|
|
173
|
+
).toBeNull();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("returns null for a non-Bitbucket remote without calling fetch", async () => {
|
|
178
|
+
let called = false;
|
|
179
|
+
const fetchImpl = (async () => {
|
|
180
|
+
called = true;
|
|
181
|
+
return new Response("{}");
|
|
182
|
+
}) as unknown as typeof fetch;
|
|
183
|
+
expect(
|
|
184
|
+
await findBitbucketOpenPullRequest("https://github.com/ws/repo", "b", {
|
|
185
|
+
token: "t",
|
|
186
|
+
fetchImpl,
|
|
187
|
+
}),
|
|
188
|
+
).toBeNull();
|
|
189
|
+
expect(called).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -34,6 +34,9 @@ type StubState = {
|
|
|
34
34
|
};
|
|
35
35
|
calls: { signature: number; list: number; sync: number };
|
|
36
36
|
skillFilesStatus: number;
|
|
37
|
+
/** 404 by default so the legacy list path stays covered; bundle tests opt in. */
|
|
38
|
+
bundleStatus: number;
|
|
39
|
+
bundleCalls: number;
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
const FAKE_HOME = join(tmpdir(), `runner-refresh-test-${process.pid}`);
|
|
@@ -67,6 +70,8 @@ const state: StubState = {
|
|
|
67
70
|
signature: "hash-v1",
|
|
68
71
|
},
|
|
69
72
|
calls: { signature: 0, list: 0, sync: 0 },
|
|
73
|
+
bundleStatus: 404,
|
|
74
|
+
bundleCalls: 0,
|
|
70
75
|
};
|
|
71
76
|
|
|
72
77
|
let server: ReturnType<typeof Bun.serve> | null = null;
|
|
@@ -91,6 +96,22 @@ describe("refreshSkillsIfChanged", () => {
|
|
|
91
96
|
generatedAt: new Date().toISOString(),
|
|
92
97
|
});
|
|
93
98
|
}
|
|
99
|
+
if (url.pathname.endsWith("/skills/bundle")) {
|
|
100
|
+
state.bundleCalls++;
|
|
101
|
+
if (state.bundleStatus !== 200) {
|
|
102
|
+
return new Response("not found", { status: state.bundleStatus });
|
|
103
|
+
}
|
|
104
|
+
return Response.json({
|
|
105
|
+
skills: state.skillsBody.skills.map((skill) => ({
|
|
106
|
+
...skill,
|
|
107
|
+
files: skill.isComplex
|
|
108
|
+
? [{ path: "refs/notes.md", content: "bundled note", isBinary: false }]
|
|
109
|
+
: [],
|
|
110
|
+
})),
|
|
111
|
+
total: state.skillsBody.skills.length,
|
|
112
|
+
signature: state.skillsBody.signature,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
94
115
|
if (url.pathname.match(/\/api\/agents\/[^/]+\/skills$/)) {
|
|
95
116
|
state.calls.list++;
|
|
96
117
|
if (state.listStatus !== 200) {
|
|
@@ -337,6 +358,43 @@ describe("refreshSkillsIfChanged", () => {
|
|
|
337
358
|
rmSync(cleanHome, { recursive: true, force: true });
|
|
338
359
|
});
|
|
339
360
|
|
|
361
|
+
test("bundle endpoint replaces the list call and the per-file fan-out", async () => {
|
|
362
|
+
state.calls = { signature: 0, list: 0, sync: 0 };
|
|
363
|
+
state.bundleCalls = 0;
|
|
364
|
+
state.bundleStatus = 200;
|
|
365
|
+
state.signatureHash = "hash-bundle";
|
|
366
|
+
state.skillsBody.signature = "hash-bundle";
|
|
367
|
+
state.skillsBody.skills = [
|
|
368
|
+
{
|
|
369
|
+
id: "skill-complex",
|
|
370
|
+
name: "complex-skill",
|
|
371
|
+
description: "has bundled files",
|
|
372
|
+
content: "---\nname: complex-skill\n---\n\nComplex body.",
|
|
373
|
+
isComplex: true,
|
|
374
|
+
isActive: true,
|
|
375
|
+
isEnabled: true,
|
|
376
|
+
},
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
const home = join(FAKE_HOME, "bundle-home");
|
|
380
|
+
mkdirSync(home, { recursive: true });
|
|
381
|
+
const lastHash = { current: null as string | null };
|
|
382
|
+
const result = await refreshWithHome(makeCtx(), lastHash, home);
|
|
383
|
+
|
|
384
|
+
expect(result.changed).toBe(true);
|
|
385
|
+
expect(lastHash.current).toBe("hash-bundle");
|
|
386
|
+
// One signature probe + one bundle call. No list call, no manifest and no
|
|
387
|
+
// per-file fetches.
|
|
388
|
+
expect(state.bundleCalls).toBe(1);
|
|
389
|
+
expect(state.calls).toEqual({ signature: 1, list: 0, sync: 0 });
|
|
390
|
+
expect(
|
|
391
|
+
readFileSync(join(home, ".claude", "skills", "complex-skill", "refs", "notes.md"), "utf-8"),
|
|
392
|
+
).toBe("bundled note");
|
|
393
|
+
|
|
394
|
+
state.bundleStatus = 404;
|
|
395
|
+
rmSync(home, { recursive: true, force: true });
|
|
396
|
+
});
|
|
397
|
+
|
|
340
398
|
test("list fetch failure after signature drift leaves pre-existing skills on disk and does not advance hash", async () => {
|
|
341
399
|
// Arrange: fresh isolated home with a pre-existing swarm-managed skill
|
|
342
400
|
const isolatedHome = join(FAKE_HOME, "list-fail-home");
|
|
@@ -74,6 +74,21 @@ describe("scrubSecrets — env-based replacement", () => {
|
|
|
74
74
|
expect(out).toBe("Authorization: Bearer [REDACTED:GITHUB_TOKEN] end");
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
test("redacts the Bitbucket token and webhook secret as exact keys", () => {
|
|
78
|
+
const values = {
|
|
79
|
+
BITBUCKET_TOKEN: `example-bitbucket-${randomToken(24)}`,
|
|
80
|
+
BITBUCKET_WEBHOOK_SECRET: `example-bbhook-${randomToken(24)}`,
|
|
81
|
+
};
|
|
82
|
+
for (const [name, value] of Object.entries(values)) process.env[name] = value;
|
|
83
|
+
refreshSecretScrubberCache();
|
|
84
|
+
for (const [name, value] of Object.entries(values)) {
|
|
85
|
+
expect(isSensitiveKey(name)).toBe(true);
|
|
86
|
+
expect(scrubSecrets(`Authorization: Bearer ${value} end`)).toBe(
|
|
87
|
+
`Authorization: Bearer [REDACTED:${name}] end`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
77
92
|
test("redacts any key with _API_KEY suffix", () => {
|
|
78
93
|
process.env.FOO_SERVICE_API_KEY = "example-supersecretFooApiKey_longerthan12chars";
|
|
79
94
|
refreshSecretScrubberCache();
|