@desplega.ai/agent-swarm 2.4.4 → 2.5.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 +4 -2
- package/dist/{acp-adapter-thq04adh.js → acp-adapter-11gp67nr.js} +8 -6
- package/dist/{actions-j6ywh6x5.js → actions-8p2kze8j.js} +24 -22
- package/dist/{amp-adapter-k8b5xmj8.js → amp-adapter-z4qvhacj.js} +2 -2
- package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
- package/dist/{app-rb7vc02s.js → app-0qp0a2gk.js} +3 -3
- package/dist/{assistant-ftta1zjf.js → assistant-jedd3ktm.js} +27 -25
- package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
- package/dist/{boot-reembed-xns7h5h9.js → boot-reembed-4xwq5tg0.js} +1 -1
- package/dist/{boot-reembed-srp6v6af.js → boot-reembed-n4wq5dd2.js} +1 -1
- package/dist/{boot-scrub-sweep-3qqrh2mz.js → boot-scrub-sweep-69h90xcq.js} +4 -4
- package/dist/{boot-seeds-9ks8hce7.js → boot-seeds-3vn0m9sg.js} +4 -4
- package/dist/{claude-adapter-k5ksyres.js → claude-adapter-221mvad5.js} +8 -6
- package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-gt6pa35d.js} +2 -2
- package/dist/{claude-managed-setup-n5a5eqks.js → claude-managed-setup-c3hp36ph.js} +4 -4
- package/dist/{claude-sdk-session-2j3j4kwn.js → claude-sdk-session-8thjsk5e.js} +9729 -9628
- package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
- package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
- package/dist/{cli-amyhm3gj.js → cli-0y6f0ckq.js} +1 -1
- package/dist/{cli-4dg55wsk.js → cli-0z3vjrpm.js} +1 -1
- package/dist/{cli-pccfmtb5.js → cli-179xcyky.js} +11 -11
- package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
- package/dist/{cli-hj3debgw.js → cli-1pr672hz.js} +3 -3
- package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
- package/dist/{cli-t7cyef5g.js → cli-36j4jnk3.js} +4 -4
- package/dist/{cli-er83cjbn.js → cli-3qv8naq8.js} +527 -231
- package/dist/cli-3zwye9sn.js +1193 -0
- package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
- package/dist/{cli-xj0re5tm.js → cli-4w24f7z3.js} +1 -1
- package/dist/{cli-v3yr2gbg.js → cli-5kzk3472.js} +2198 -968
- package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
- package/dist/{cli-w6nadkh4.js → cli-5rvg1ddk.js} +5 -5
- package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
- package/dist/{cli-9qtjfd60.js → cli-6rg2jn2y.js} +3 -2
- package/dist/{cli-2v045xyf.js → cli-7bwtc6yx.js} +3 -3
- package/dist/{cli-3cgngyd8.js → cli-7g40g8hq.js} +35 -17
- package/dist/{cli-q6r7t299.js → cli-92mqenkc.js} +2379 -154
- package/dist/{cli-cynvc9fj.js → cli-9ce2xw98.js} +8 -8
- package/dist/{cli-gqvh8d8a.js → cli-bgdtv62r.js} +36 -22
- 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-p20r8w42.js → cli-fcc5qe3f.js} +763 -268
- package/dist/{cli-1ephgwnq.js → cli-fes51ya2.js} +3 -3
- package/dist/{cli-1d70crsy.js → cli-ftmq4nvk.js} +2 -2
- package/dist/{cli-qkhc5aqy.js → cli-fy1jyyym.js} +13 -13
- package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
- package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
- package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
- package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
- package/dist/{cli-bnz0r3kc.js → cli-j1eghqfm.js} +5 -5
- package/dist/{cli-2zqpkvtt.js → cli-jpxsrxcb.js} +1 -1
- package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
- package/dist/{cli-37yb7307.js → cli-mbfj11fq.js} +1 -1
- package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
- package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
- package/dist/{cli-a0kcty51.js → cli-n6kmqngw.js} +2 -2
- package/dist/{cli-te6b5wvg.js → cli-nnvjb17s.js} +3 -3
- 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-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
- package/dist/{cli-zrwpctp4.js → cli-r25qpaxt.js} +161 -63
- package/dist/{cli-nc35pxb7.js → cli-rh5smt8t.js} +19 -4
- package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
- package/dist/{cli-esxerzg7.js → cli-s4sntwt4.js} +2 -2
- package/dist/{cli-b8k7epj1.js → cli-segs7hx9.js} +8 -8
- package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
- package/dist/{cli-vdm20wej.js → cli-sy8f9705.js} +4 -4
- package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
- package/dist/{cli-2y91wnbh.js → cli-we0ra057.js} +222 -10
- package/dist/{cli-55qmbp6z.js → cli-x21dqn18.js} +5 -5
- package/dist/{cli-dqb4n629.js → cli-x6vf4sad.js} +11 -11
- package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
- package/dist/{cli-vq97g1jq.js → cli-xawhpgc9.js} +95 -195
- package/dist/{cli-82d3nkc3.js → cli-xd4eqtr2.js} +2 -2
- package/dist/{cli-9fhkkpj5.js → cli-xt768r5m.js} +2 -2
- package/dist/{cli-zrk6jxv8.js → cli-y4x8ww6s.js} +2 -2
- package/dist/{cli-xc27fwzg.js → cli-y9jvxfhk.js} +3 -3
- package/dist/{cli-44159k28.js → cli-z63a269p.js} +2 -2
- package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
- package/dist/cli.js +31 -29
- package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
- package/dist/{codex-adapter-968xwfy7.js → codex-adapter-tcg1q3jk.js} +6 -4
- package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
- package/dist/{codex-session-runner-ht4z99fa.js → codex-session-runner-xfx4wd9x.js} +7 -5
- package/dist/{commands-pcymaeg9.js → commands-ew83nj70.js} +2 -2
- package/dist/{controller-fpjyk46r.js → controller-gnq1k1mf.js} +4 -4
- package/dist/{controllers-connect-mwzx1fh6.js → controllers-connect-tw1dan9r.js} +26 -24
- package/dist/{cursor-adapter-cp4s2e2f.js → cursor-adapter-yq5xgfcg.js} +1 -1
- package/dist/{db-xsjg9jqq.js → db-7gbc53fd.js} +14 -2
- package/dist/{db-sgd12bt9.js → db-t86xrnes.js} +3 -3
- package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
- package/dist/{dispatcher-x0jfw2c9.js → dispatcher-64bdyhps.js} +23 -21
- package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
- package/dist/{dsh-adapter-jrq7ma7h.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-8njj4h3b.js → external-channel-e9nw24zv.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-kkr97v2f.js → grok-adapter-v82srhrx.js} +9 -7
- package/dist/handlers-c9ajzhbn.js +41 -0
- package/dist/{heartbeat-wd03zhn0.js → heartbeat-2vqs1319.js} +26 -24
- package/dist/{hook-xzj2ts2p.js → hook-01nk13wx.js} +7 -5
- package/dist/{hook-f0vghj42.js → hook-jzpheegg.js} +9 -7
- package/dist/{http-4hwxv62p.js → http-fc1k6ec8.js} +859 -252
- package/dist/index-07ef7nkm.js +42 -0
- package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
- package/dist/index-7yts8n9s.js +43 -0
- package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
- package/dist/index-q3t40ryv.js +46 -0
- package/dist/{index-ww2vznkp.js → index-sepja052.js} +1 -1
- package/dist/{index-97q4xn03.js → index-zdycv3ja.js} +135 -79
- package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
- package/dist/{keepalive-hrh7xnr9.js → keepalive-5nh2rhmr.js} +4 -4
- package/dist/lead-aayq64jn.js +79 -0
- package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
- package/dist/{maintenance-e8wnwnqx.js → maintenance-hjhzdt3k.js} +1 -1
- package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
- package/dist/{node-3v3f08f7.js → node-jzjj7r9x.js} +10 -9
- package/dist/{oauth-refresh-sweep-zm0yj4sj.js → oauth-refresh-sweep-y3n1knmd.js} +3 -3
- package/dist/{onboard-dtwcq603.js → onboard-1zq3m7db.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-w3fbhpn0.js → otel-impl-h9yyza47.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-j38dehfs.js → pricing-refresh-nevhat8h.js} +3 -3
- package/dist/{rbac-roles-ae8yttq4.js → rbac-roles-4y6ngvzq.js} +1 -1
- package/dist/{rbac-roles-ejxx9b35.js → rbac-roles-exbg8xb6.js} +2 -2
- package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
- package/dist/{render-v2-n16n0phe.js → render-v2-cfsh13z3.js} +3 -3
- package/dist/{resume-z0fgjc38.js → resume-5s2t0dbs.js} +10 -8
- package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
- package/dist/{session-access-cv3mq6mc.js → session-access-dnbyp3cn.js} +6 -4
- package/dist/{setup-mafhj3yt.js → setup-1wj1y77j.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-8b86mgjp.js → work-objects-7mg6216g.js} +26 -24
- package/dist/worker-nwb789mg.js +79 -0
- package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
- package/openapi.json +793 -6
- package/package.json +11 -11
- package/src/apps/catalog.generated.json +206 -4
- package/src/apps/page-validator.ts +13 -1
- package/src/assets/key.ts +17 -0
- package/src/azure-devops/handlers.ts +1 -1
- package/src/be/chunking.ts +42 -9
- 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/draft-text.ts +15 -0
- package/src/be/db/tasks/read.ts +54 -5
- package/src/be/db/workflow-runs.ts +96 -0
- package/src/be/db.ts +85 -237
- package/src/be/memory/index-content.ts +36 -17
- package/src/be/memory/providers/openai-embedding.ts +115 -61
- package/src/be/memory/providers/sqlite-store.ts +69 -23
- package/src/be/memory/reranker.ts +31 -1
- package/src/be/memory/search-candidates.ts +47 -0
- package/src/be/memory/types.ts +14 -0
- package/src/be/onboarding.ts +3 -0
- package/src/be/scripts/typecheck.ts +2 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +106 -54
- package/src/be/seed-scripts/index.ts +2 -2
- package/src/be/skill-sync.ts +43 -2
- package/src/be/swarm-config-guard.ts +25 -1
- package/src/be/task-attachment-store.ts +30 -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/catalog.generated.json +4 -4
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +9 -2
- package/src/extensions/post-bridge.ts +1 -0
- package/src/fs/provider.ts +13 -1
- package/src/github/handlers.ts +6 -6
- package/src/gitlab/handlers.ts +5 -5
- package/src/heartbeat/heartbeat.ts +31 -11
- package/src/http/assets.ts +14 -2
- package/src/http/core.ts +12 -0
- package/src/http/fs.ts +54 -34
- package/src/http/index.ts +4 -0
- package/src/http/mcp-body.ts +37 -0
- package/src/http/mcp-user.ts +3 -5
- package/src/http/mcp.ts +6 -7
- package/src/http/memory.ts +26 -15
- package/src/http/onboarding.ts +1 -0
- package/src/http/page-proxy.ts +61 -5
- package/src/http/pages-public.ts +27 -44
- package/src/http/pages.ts +4 -4
- package/src/http/script-runs.ts +2 -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 +13 -1
- package/src/http/webhooks.ts +141 -0
- package/src/integrations/chat-sdk/adapters.ts +24 -4
- package/src/integrations/chat-sdk/attachments.ts +90 -12
- package/src/integrations/chat-sdk/facts.ts +138 -0
- package/src/integrations/chat-sdk/inbound.ts +156 -8
- package/src/integrations/chat-sdk/limits.ts +24 -0
- package/src/integrations/chat-sdk/outbound-files.ts +186 -0
- package/src/integrations/chat-sdk/outbound.ts +272 -5
- package/src/integrations/chat-sdk/relay.ts +4 -1
- package/src/integrations/chat-sdk/router.ts +28 -2
- package/src/integrations/chat-sdk/steering.ts +9 -0
- package/src/integrations/chat-sdk/templates.ts +14 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
- package/src/mcp-limits.ts +20 -0
- package/src/pages/browser-sdk.ts +8 -3
- package/src/pages/feedback-overlay.ts +1 -1
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +106 -119
- package/src/providers/codex-agents-md.ts +35 -229
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +5 -53
- package/src/realtime/transport.ts +1 -2
- package/src/script-workflows/workflow-ctx.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +2 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +14 -4
- package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
- package/src/server-user.ts +2 -0
- package/src/server.ts +2 -0
- package/src/slack/assistant.ts +2 -2
- package/src/slack/files.ts +2 -0
- package/src/slack/handlers.test.ts +158 -0
- package/src/slack/handlers.ts +23 -32
- package/src/slack/inbound-files.ts +39 -6
- package/src/slack/thread-buffer.ts +2 -1
- package/src/tasks/context-key.ts +61 -1
- package/src/tasks/task-terminal-effects.ts +21 -17
- package/src/tests/apps-card-grid.test.ts +197 -0
- package/src/tests/apps-catalog-additions.test.ts +120 -0
- package/src/tests/apps-tree.test.ts +129 -0
- 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-attachments-reply.test.ts +52 -0
- package/src/tests/chat-sdk-config.test.ts +11 -0
- package/src/tests/chat-sdk-facts.test.ts +23 -0
- package/src/tests/chat-sdk-inbound.test.ts +597 -4
- package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
- package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
- package/src/tests/codex-adapter.test.ts +167 -242
- package/src/tests/config-api-only-keys.test.ts +9 -0
- package/src/tests/context-key.test.ts +50 -0
- package/src/tests/core-auth.test.ts +5 -0
- package/src/tests/cors-allowlist.test.ts +1 -1
- 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/extensions-example-slack-reply-gate.test.ts +262 -91
- 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-handlers.test.ts +3 -3
- package/src/tests/gitlab-vcs-db.test.ts +4 -4
- package/src/tests/heartbeat-checklist.test.ts +33 -0
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
- package/src/tests/heartbeat-jev-gate.test.ts +242 -1
- package/src/tests/integration-disable-flags.test.ts +36 -0
- package/src/tests/kv-page-proxy.test.ts +91 -5
- package/src/tests/launch-password-rejection.test.ts +2 -2
- package/src/tests/mcp-input-caps.test.ts +294 -0
- package/src/tests/mcp-tools.test.ts +1 -1
- package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
- package/src/tests/memory-embedding-input-cap.test.ts +114 -0
- package/src/tests/memory.test.ts +14 -0
- package/src/tests/onboarding.test.ts +12 -0
- package/src/tests/page-guest-session.test.ts +19 -8
- package/src/tests/page-proxy-authed.test.ts +1 -1
- package/src/tests/page-proxy.test.ts +17 -10
- package/src/tests/page-session.test.ts +102 -1
- package/src/tests/pages-authed-mode.test.ts +82 -12
- package/src/tests/pages-password-mode.test.ts +5 -5
- 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/realtime-transport.test.ts +13 -7
- 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/script-runs-http.test.ts +84 -0
- package/src/tests/scripts-http.test.ts +10 -1
- 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 +23 -3
- package/src/tests/task-audio-transcription.test.ts +299 -0
- package/src/tests/task-completion-memory-chunking.test.ts +434 -0
- package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
- package/src/tests/transcription.test.ts +299 -0
- 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-system-one-decision-providers.test.ts +98 -1
- package/src/tests/workflow-system-one-decision.test.ts +2 -1
- package/src/tests/workflow-tla-races.test.ts +2 -2
- package/src/tools/chat-access.ts +10 -0
- package/src/tools/chat-post.ts +31 -11
- package/src/tools/chat-reply.ts +32 -10
- package/src/tools/memory-search.ts +3 -1
- package/src/tools/script-delete.ts +1 -1
- package/src/tools/system-one-decide.ts +17 -4
- package/src/tools/workflows/create-workflow.ts +5 -3
- package/src/types.ts +9 -2
- package/src/utils/config-env-export.ts +4 -1
- package/src/utils/page-session.ts +133 -20
- package/src/utils/secret-scrubber.ts +4 -0
- package/src/utils/skills-refresh.ts +83 -43
- package/src/utils/transcription.ts +246 -0
- 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/executors/system-one-decision.ts +28 -7
- package/src/workflows/executors/system-one-providers.ts +39 -4
- 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/extensions/slack-reply-gate/README.md +24 -22
- package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
- package/templates/extensions/slack-reply-gate/manifest.json +2 -2
- package/templates/skills/apps/SKILL.md +28 -6
- package/templates/skills/apps/content.md +28 -6
- package/templates/skills/chat-sdk/content.md +6 -2
- package/templates/skills/code-quality/content.md +2 -1
- package/templates/skills/heartbeat-runbook/content.md +5 -5
- package/templates/skills/pages/content.md +6 -4
- package/templates/skills/slack-interaction/content.md +5 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -2
- package/templates/skills/swarm-scripts/content.md +2 -2
- package/templates/skills/work-on-task/SKILL.md +1 -1
- package/templates/skills/work-on-task/content.md +1 -1
- package/dist/cli-51y09c6p.js +0 -2657
- package/dist/cli-yw91rnmw.js +0 -13
- package/dist/handlers-0pvee9nw.js +0 -39
- package/dist/index-gdh8jrdm.js +0 -43
- package/dist/index-hg0x2cdv.js +0 -40
- package/dist/index-thm0y6a8.js +0 -41
- package/dist/lead-ee8vazz2.js +0 -77
- package/dist/worker-16s75hm5.js +0 -77
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitbucket Cloud webhook event handlers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors src/azure-devops/handlers.ts and src/gitlab/handlers.ts:
|
|
5
|
+
* - Parses webhook payloads (pull request created / commented / merged / declined)
|
|
6
|
+
* - Creates agent tasks via createTaskWithSiblingAwareness()
|
|
7
|
+
* - Deduplicates deliveries (X-Request-UUID) and events via in-memory TTL maps
|
|
8
|
+
* - Detects bot mentions and reviewer requests
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
failTask,
|
|
13
|
+
findActiveTasksByVcs,
|
|
14
|
+
findTaskByVcs,
|
|
15
|
+
getAllAgents,
|
|
16
|
+
incrKv,
|
|
17
|
+
upsertKv,
|
|
18
|
+
} from "../be/db";
|
|
19
|
+
import { renderIdentity, resolveIdentity } from "../be/identity";
|
|
20
|
+
import { findUserByExternalId } from "../be/users";
|
|
21
|
+
import { resolveTemplate } from "../prompts/resolver";
|
|
22
|
+
import { bitbucketContextKey } from "../tasks/context-key";
|
|
23
|
+
import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
|
|
24
|
+
import { resolveBitbucketBotAccountId } from "./api";
|
|
25
|
+
import { getBitbucketBotName } from "./auth";
|
|
26
|
+
// Side-effect import: registers all Bitbucket event templates in the in-memory registry
|
|
27
|
+
import "./templates";
|
|
28
|
+
import type {
|
|
29
|
+
BitbucketAccount,
|
|
30
|
+
BitbucketPullRequest,
|
|
31
|
+
PullRequestCommentEvent,
|
|
32
|
+
PullRequestEvent,
|
|
33
|
+
} from "./types";
|
|
34
|
+
|
|
35
|
+
type HandlerResult = { created: boolean; taskId?: string };
|
|
36
|
+
|
|
37
|
+
// ── Dedup caches (same pattern as GitHub/GitLab) ──
|
|
38
|
+
const DEDUP_TTL_MS = 60_000;
|
|
39
|
+
const processedEvents = new Map<string, number>();
|
|
40
|
+
const processedDeliveries = new Map<string, number>();
|
|
41
|
+
|
|
42
|
+
function sweep(cache: Map<string, number>, now: number): void {
|
|
43
|
+
for (const [k, ts] of cache) {
|
|
44
|
+
if (now - ts > DEDUP_TTL_MS) cache.delete(k);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Content-key dedup: true when the same event was handled in the last 60s. */
|
|
49
|
+
export function isDuplicate(key: string): boolean {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
sweep(processedEvents, now);
|
|
52
|
+
if (processedEvents.has(key)) return true;
|
|
53
|
+
processedEvents.set(key, now);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hold the content-key reservation for `key` while `fn` runs. A throw releases
|
|
59
|
+
* it, so Bitbucket's retry of a delivery that failed with a 5xx is not taken
|
|
60
|
+
* for a duplicate. Returns null when the key is already held.
|
|
61
|
+
*/
|
|
62
|
+
async function withDedupReservation(
|
|
63
|
+
key: string,
|
|
64
|
+
fn: () => Promise<HandlerResult>,
|
|
65
|
+
): Promise<HandlerResult | null> {
|
|
66
|
+
if (isDuplicate(key)) return null;
|
|
67
|
+
try {
|
|
68
|
+
return await fn();
|
|
69
|
+
} catch (err) {
|
|
70
|
+
processedEvents.delete(key);
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Delivery dedup on `X-Request-UUID`. Bitbucket retries a delivery (same
|
|
77
|
+
* UUID) when it gets a 5xx or times out, so only a delivery that was acked
|
|
78
|
+
* is recorded — see `markDeliveryProcessed`.
|
|
79
|
+
*/
|
|
80
|
+
export function isDuplicateDelivery(requestUuid: string | undefined): boolean {
|
|
81
|
+
if (!requestUuid) return false;
|
|
82
|
+
sweep(processedDeliveries, Date.now());
|
|
83
|
+
return processedDeliveries.has(requestUuid);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function markDeliveryProcessed(requestUuid: string | undefined): void {
|
|
87
|
+
if (requestUuid) processedDeliveries.set(requestUuid, Date.now());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Reset dedup state (tests). */
|
|
91
|
+
export function resetBitbucketDedup(): void {
|
|
92
|
+
processedEvents.clear();
|
|
93
|
+
processedDeliveries.clear();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── Helpers ──
|
|
97
|
+
|
|
98
|
+
export function escapeRegExp(value: string): string {
|
|
99
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Mention patterns: the mention picker stores `@{<account_id>}` in the
|
|
104
|
+
* comment's raw content; a typed mention stays plain `@name`.
|
|
105
|
+
*/
|
|
106
|
+
function mentionPatterns(botAccountId: string | null): RegExp[] {
|
|
107
|
+
// `(?![\w-])` rather than `\b`, so `@agent-swarm-bot-two` is not the bot.
|
|
108
|
+
const patterns = [new RegExp(`@${escapeRegExp(getBitbucketBotName())}(?![\\w-])`, "gi")];
|
|
109
|
+
if (botAccountId) patterns.push(new RegExp(`@\\{${escapeRegExp(botAccountId)}\\}`, "gi"));
|
|
110
|
+
return patterns;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function detectMention(
|
|
114
|
+
text: string | null | undefined,
|
|
115
|
+
botAccountId: string | null,
|
|
116
|
+
): boolean {
|
|
117
|
+
if (!text) return false;
|
|
118
|
+
return mentionPatterns(botAccountId).some((re) => {
|
|
119
|
+
re.lastIndex = 0;
|
|
120
|
+
return re.test(text);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function extractMentionContext(text: string, botAccountId: string | null): string {
|
|
125
|
+
return mentionPatterns(botAccountId)
|
|
126
|
+
.reduce((acc, re) => acc.replace(re, ""), text)
|
|
127
|
+
.trim();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function isBot(account: BitbucketAccount | undefined, botAccountId: string | null): boolean {
|
|
131
|
+
return !!botAccountId && account?.account_id === botAccountId;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function pullRequestWebUrl(repo: string, pr: BitbucketPullRequest): string {
|
|
135
|
+
return pr.links?.html?.href ?? `https://bitbucket.org/${repo}/pull-requests/${pr.id}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function contextKeyOf(event: PullRequestEvent): string {
|
|
139
|
+
return bitbucketContextKey({
|
|
140
|
+
repoUuid: event.repository.uuid,
|
|
141
|
+
pullRequestId: event.pullrequest.id,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── Payload shape ──
|
|
146
|
+
|
|
147
|
+
/** The pull request event, or null when the payload lacks the fields we read. */
|
|
148
|
+
export function pullRequestEventOf(body: unknown): PullRequestEvent | null {
|
|
149
|
+
const event = body as Partial<PullRequestEvent> | null | undefined;
|
|
150
|
+
const pr = event?.pullrequest;
|
|
151
|
+
const repo = event?.repository;
|
|
152
|
+
if (
|
|
153
|
+
typeof pr?.id !== "number" ||
|
|
154
|
+
typeof pr.title !== "string" ||
|
|
155
|
+
typeof repo?.uuid !== "string" ||
|
|
156
|
+
typeof repo.full_name !== "string" ||
|
|
157
|
+
!event?.actor
|
|
158
|
+
) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return event as PullRequestEvent;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The comment event, or null when the pull request or comment is missing. */
|
|
165
|
+
export function pullRequestCommentEventOf(body: unknown): PullRequestCommentEvent | null {
|
|
166
|
+
const event = pullRequestEventOf(body) as PullRequestCommentEvent | null;
|
|
167
|
+
const comment = event?.comment;
|
|
168
|
+
if (!event || typeof comment?.id !== "number" || typeof comment.content?.raw !== "string") {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return event;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function warnMalformed(eventKey: string) {
|
|
175
|
+
console.warn(`[Bitbucket] Ignoring ${eventKey} event: required payload fields are missing`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function findLeadAgent() {
|
|
179
|
+
const agents = await getAllAgents();
|
|
180
|
+
return (
|
|
181
|
+
agents.find((a) => a.role === "lead" && a.status === "idle") ??
|
|
182
|
+
agents.find((a) => a.role === "lead") ??
|
|
183
|
+
null
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Identity resolution ──
|
|
188
|
+
|
|
189
|
+
const UNMAPPED_NAMESPACE = "integration:unmapped:bitbucket";
|
|
190
|
+
const UNMAPPED_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
191
|
+
|
|
192
|
+
function externalIdOf(account: BitbucketAccount | undefined): string | null {
|
|
193
|
+
return account?.account_id?.trim() || account?.uuid?.trim() || null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Resolve a Bitbucket account to a `users.id` via its linked `bitbucket`
|
|
198
|
+
* identity (Atlassian `account_id`). Webhook payloads carry no email, so an
|
|
199
|
+
* unknown sender is recorded as an unmapped tracker entry for operator triage.
|
|
200
|
+
*/
|
|
201
|
+
export async function resolveBitbucketSender(
|
|
202
|
+
account: BitbucketAccount | undefined,
|
|
203
|
+
sampleEventType: string,
|
|
204
|
+
sampleContext: string,
|
|
205
|
+
): Promise<string | undefined> {
|
|
206
|
+
const externalId = externalIdOf(account);
|
|
207
|
+
if (!externalId) return undefined;
|
|
208
|
+
const existing = await findUserByExternalId("bitbucket", externalId);
|
|
209
|
+
if (existing) return existing.id;
|
|
210
|
+
|
|
211
|
+
await upsertKv({
|
|
212
|
+
namespace: UNMAPPED_NAMESPACE,
|
|
213
|
+
key: `${externalId}:meta`,
|
|
214
|
+
value: {
|
|
215
|
+
lastSeenAt: new Date().toISOString(),
|
|
216
|
+
displayName: account?.display_name,
|
|
217
|
+
sampleEventType,
|
|
218
|
+
sampleContext: sampleContext.slice(0, 100),
|
|
219
|
+
},
|
|
220
|
+
valueType: "json",
|
|
221
|
+
expiresAt: Date.now() + UNMAPPED_TTL_MS,
|
|
222
|
+
});
|
|
223
|
+
await incrKv(UNMAPPED_NAMESPACE, `${externalId}:count`, 1);
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Render a Bitbucket account for agent-visible text: the resolved canonical
|
|
229
|
+
* name or the explicit UNKNOWN sentinel.
|
|
230
|
+
*/
|
|
231
|
+
export async function renderBitbucketIdentity(
|
|
232
|
+
account: BitbucketAccount | undefined,
|
|
233
|
+
): Promise<string> {
|
|
234
|
+
return renderIdentity(await resolveIdentity("bitbucket", externalIdOf(account) ?? ""));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function createBitbucketTask(
|
|
238
|
+
text: string,
|
|
239
|
+
event: PullRequestEvent,
|
|
240
|
+
fields: {
|
|
241
|
+
taskType: "bitbucket-pr" | "bitbucket-comment";
|
|
242
|
+
vcsEventType: string;
|
|
243
|
+
author: BitbucketAccount | undefined;
|
|
244
|
+
requestedByUserId: string | undefined;
|
|
245
|
+
vcsCommentId?: number;
|
|
246
|
+
parentTaskId?: string;
|
|
247
|
+
},
|
|
248
|
+
): Promise<HandlerResult> {
|
|
249
|
+
const { pullrequest: pr, repository } = event;
|
|
250
|
+
const lead = await findLeadAgent();
|
|
251
|
+
const task = await createTaskWithSiblingAwareness(
|
|
252
|
+
text,
|
|
253
|
+
{
|
|
254
|
+
agentId: lead?.id ?? null,
|
|
255
|
+
routingReason: lead ? "skill" : undefined,
|
|
256
|
+
routingSource: lead ? "engine_default" : undefined,
|
|
257
|
+
source: "bitbucket",
|
|
258
|
+
vcsProvider: "bitbucket",
|
|
259
|
+
taskType: fields.taskType,
|
|
260
|
+
vcsRepo: repository.full_name,
|
|
261
|
+
vcsEventType: fields.vcsEventType,
|
|
262
|
+
vcsNumber: pr.id,
|
|
263
|
+
vcsCommentId: fields.vcsCommentId,
|
|
264
|
+
vcsAuthor: externalIdOf(fields.author) ?? undefined,
|
|
265
|
+
requestedByUserId: fields.requestedByUserId,
|
|
266
|
+
vcsUrl: pullRequestWebUrl(repository.full_name, pr),
|
|
267
|
+
parentTaskId: fields.parentTaskId,
|
|
268
|
+
contextKey: contextKeyOf(event),
|
|
269
|
+
},
|
|
270
|
+
{ origin: "webhook" },
|
|
271
|
+
);
|
|
272
|
+
return { created: true, taskId: task.id };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ── Event Handlers ──
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* `pullrequest:created`: a bot mention in the description, or the bot listed
|
|
279
|
+
* as a reviewer, creates one task.
|
|
280
|
+
*/
|
|
281
|
+
export async function handlePullRequestCreated(body: unknown): Promise<HandlerResult> {
|
|
282
|
+
const event = pullRequestEventOf(body);
|
|
283
|
+
if (!event) {
|
|
284
|
+
warnMalformed("pullrequest:created");
|
|
285
|
+
return { created: false };
|
|
286
|
+
}
|
|
287
|
+
const { pullrequest: pr, repository } = event;
|
|
288
|
+
const repo = repository.full_name;
|
|
289
|
+
const author = pr.author ?? event.actor;
|
|
290
|
+
console.log(`[Bitbucket] PR #${pr.id} created by ${author.display_name} in ${repo}`);
|
|
291
|
+
|
|
292
|
+
const botAccountId = await resolveBitbucketBotAccountId();
|
|
293
|
+
if (isBot(author, botAccountId)) {
|
|
294
|
+
console.log(`[Bitbucket] PR opened by the bot itself, skipping`);
|
|
295
|
+
return { created: false };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const mentioned = detectMention(pr.description, botAccountId);
|
|
299
|
+
const reviewRequested =
|
|
300
|
+
!!botAccountId && (pr.reviewers ?? []).some((r) => r.account_id === botAccountId);
|
|
301
|
+
if (!mentioned && !reviewRequested) {
|
|
302
|
+
console.log(`[Bitbucket] PR opened without bot mention or review request, skipping`);
|
|
303
|
+
return { created: false };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const dedupKey = `bitbucket-pr-${repository.uuid}-${pr.id}-created`;
|
|
307
|
+
const outcome = await withDedupReservation(dedupKey, async () => {
|
|
308
|
+
const requestedByUserId = await resolveBitbucketSender(
|
|
309
|
+
author,
|
|
310
|
+
"pull_request",
|
|
311
|
+
`PR #${pr.id}: ${pr.title}`,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const context = pr.description ? extractMentionContext(pr.description, botAccountId) : "";
|
|
315
|
+
// A mention is the explicit ask; the reviewer template is for a bare review request.
|
|
316
|
+
const templateId = mentioned
|
|
317
|
+
? "bitbucket.pull_request.opened"
|
|
318
|
+
: "bitbucket.pull_request.review_requested";
|
|
319
|
+
const result = resolveTemplate(templateId, {
|
|
320
|
+
pr_id: pr.id,
|
|
321
|
+
pr_title: pr.title,
|
|
322
|
+
repo,
|
|
323
|
+
username: await renderBitbucketIdentity(author),
|
|
324
|
+
source_branch: pr.source?.branch?.name ?? "",
|
|
325
|
+
target_branch: pr.destination?.branch?.name ?? "",
|
|
326
|
+
pr_url: pullRequestWebUrl(repo, pr),
|
|
327
|
+
context_section: context ? `Context: ${context}\n\n` : "",
|
|
328
|
+
});
|
|
329
|
+
if (result.skipped) {
|
|
330
|
+
return { created: false };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return createBitbucketTask(result.text, event, {
|
|
334
|
+
taskType: "bitbucket-pr",
|
|
335
|
+
vcsEventType: "pull_request",
|
|
336
|
+
author,
|
|
337
|
+
requestedByUserId,
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
if (!outcome) console.log(`[Bitbucket] Skipping duplicate PR event`);
|
|
341
|
+
return outcome ?? { created: false };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* `pullrequest:comment_created`: a bot mention in a new PR comment (general or
|
|
346
|
+
* inline) creates a task, linked to any active task for the same PR.
|
|
347
|
+
*/
|
|
348
|
+
export async function handlePullRequestCommentCreated(body: unknown): Promise<HandlerResult> {
|
|
349
|
+
const event = pullRequestCommentEventOf(body);
|
|
350
|
+
if (!event) {
|
|
351
|
+
warnMalformed("pullrequest:comment_created");
|
|
352
|
+
return { created: false };
|
|
353
|
+
}
|
|
354
|
+
const { pullrequest: pr, repository, comment } = event;
|
|
355
|
+
const repo = repository.full_name;
|
|
356
|
+
const author = comment.user ?? event.actor;
|
|
357
|
+
|
|
358
|
+
const botAccountId = await resolveBitbucketBotAccountId();
|
|
359
|
+
if (isBot(author, botAccountId)) {
|
|
360
|
+
return { created: false };
|
|
361
|
+
}
|
|
362
|
+
if (!detectMention(comment.content.raw, botAccountId)) {
|
|
363
|
+
return { created: false };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
console.log(`[Bitbucket] Comment by ${author.display_name} on PR #${pr.id} in ${repo}`);
|
|
367
|
+
|
|
368
|
+
const dedupKey = `bitbucket-comment-${repository.uuid}-${pr.id}-${comment.id}`;
|
|
369
|
+
const outcome = await withDedupReservation(dedupKey, async () => {
|
|
370
|
+
const requestedByUserId = await resolveBitbucketSender(
|
|
371
|
+
author,
|
|
372
|
+
"pull_request_comment",
|
|
373
|
+
comment.content.raw,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
const existingTask = await findTaskByVcs(repo, pr.id, "bitbucket");
|
|
377
|
+
const existingTaskNote = existingTask
|
|
378
|
+
? `\n\n_Note: There's an active task (${existingTask.id}) for this PR #${pr.id}._`
|
|
379
|
+
: "";
|
|
380
|
+
|
|
381
|
+
const line = comment.inline?.to ?? comment.inline?.from;
|
|
382
|
+
const locationSection = comment.inline
|
|
383
|
+
? `File: ${comment.inline.path}${line != null ? `:${line}` : ""}\n`
|
|
384
|
+
: "";
|
|
385
|
+
|
|
386
|
+
const result = resolveTemplate("bitbucket.comment.mentioned", {
|
|
387
|
+
pr_id: pr.id,
|
|
388
|
+
pr_title: pr.title,
|
|
389
|
+
username: await renderBitbucketIdentity(author),
|
|
390
|
+
repo,
|
|
391
|
+
pr_url: comment.links?.html?.href ?? pullRequestWebUrl(repo, pr),
|
|
392
|
+
location_section: locationSection,
|
|
393
|
+
context: extractMentionContext(comment.content.raw, botAccountId),
|
|
394
|
+
existing_task_note: existingTaskNote,
|
|
395
|
+
});
|
|
396
|
+
if (result.skipped) {
|
|
397
|
+
return { created: false };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return createBitbucketTask(result.text, event, {
|
|
401
|
+
taskType: "bitbucket-comment",
|
|
402
|
+
vcsEventType: "pull_request_comment",
|
|
403
|
+
author,
|
|
404
|
+
requestedByUserId,
|
|
405
|
+
vcsCommentId: comment.id,
|
|
406
|
+
parentTaskId: existingTask?.id,
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
return outcome ?? { created: false };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* `pullrequest:fulfilled` / `pullrequest:rejected`: cancel every active task
|
|
414
|
+
* for the PR.
|
|
415
|
+
*/
|
|
416
|
+
export async function handlePullRequestClosed(
|
|
417
|
+
body: unknown,
|
|
418
|
+
outcome: "merged" | "declined",
|
|
419
|
+
): Promise<HandlerResult> {
|
|
420
|
+
const event = pullRequestEventOf(body);
|
|
421
|
+
if (!event) {
|
|
422
|
+
warnMalformed(outcome === "merged" ? "pullrequest:fulfilled" : "pullrequest:rejected");
|
|
423
|
+
return { created: false };
|
|
424
|
+
}
|
|
425
|
+
const { pullrequest: pr, repository } = event;
|
|
426
|
+
// Every active task, not only the newest: a comment task is a child of the
|
|
427
|
+
// PR task, and failTask does not cascade up to the parent.
|
|
428
|
+
const reason = outcome === "merged" ? "PR was merged" : "PR was declined";
|
|
429
|
+
for (const task of await findActiveTasksByVcs(repository.full_name, pr.id, "bitbucket")) {
|
|
430
|
+
console.log(`[Bitbucket] Cancelling task ${task.id} — ${reason}`);
|
|
431
|
+
await failTask(task.id, reason);
|
|
432
|
+
}
|
|
433
|
+
return { created: false };
|
|
434
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Bitbucket Cloud Integration
|
|
2
|
+
export {
|
|
3
|
+
postBitbucketPullRequestComment,
|
|
4
|
+
resetBitbucketBotCache,
|
|
5
|
+
resolveBitbucketBotAccountId,
|
|
6
|
+
} from "./api";
|
|
7
|
+
export {
|
|
8
|
+
getBitbucketBotName,
|
|
9
|
+
getBitbucketToken,
|
|
10
|
+
initBitbucket,
|
|
11
|
+
isBitbucketEnabled,
|
|
12
|
+
resetBitbucket,
|
|
13
|
+
verifyBitbucketWebhook,
|
|
14
|
+
} from "./auth";
|
|
15
|
+
export {
|
|
16
|
+
handlePullRequestClosed,
|
|
17
|
+
handlePullRequestCommentCreated,
|
|
18
|
+
handlePullRequestCreated,
|
|
19
|
+
isDuplicateDelivery,
|
|
20
|
+
markDeliveryProcessed,
|
|
21
|
+
pullRequestCommentEventOf,
|
|
22
|
+
pullRequestEventOf,
|
|
23
|
+
} from "./handlers";
|
|
24
|
+
export type {
|
|
25
|
+
BitbucketWebhookEvent,
|
|
26
|
+
PullRequestCommentEvent,
|
|
27
|
+
PullRequestEvent,
|
|
28
|
+
} from "./types";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitbucket Cloud event prompt template definitions.
|
|
3
|
+
*
|
|
4
|
+
* Each template is registered at module load time via registerTemplate().
|
|
5
|
+
* Handlers import this module for the side-effect of registration.
|
|
6
|
+
*
|
|
7
|
+
* Template text uses {{variable}} syntax. The resolver interpolates variables
|
|
8
|
+
* and expands {{@template[id]}} references before returning the final text.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { registerTemplate } from "../prompts/registry";
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Common building blocks (Bitbucket-specific)
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
registerTemplate({
|
|
18
|
+
eventType: "common.delegation_instruction.bitbucket",
|
|
19
|
+
header: "",
|
|
20
|
+
defaultBody:
|
|
21
|
+
"\n\n**Delegation instruction:** As the lead agent, analyze this and decide whether to handle it yourself or delegate to a worker agent. Use `send-task` to delegate with clear instructions.",
|
|
22
|
+
variables: [],
|
|
23
|
+
category: "common",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// There is no Bitbucket CLI in the worker image, so the recipes are curl calls
|
|
27
|
+
// against the REST API with the worker's BITBUCKET_TOKEN.
|
|
28
|
+
registerTemplate({
|
|
29
|
+
eventType: "common.command_suggestions.bitbucket_pr",
|
|
30
|
+
header: "",
|
|
31
|
+
defaultBody:
|
|
32
|
+
'Workers have `git` authenticated for bitbucket.org and `BITBUCKET_TOKEN` in env; there is no Bitbucket CLI, so use curl against `https://api.bitbucket.org/2.0/repositories/{{repo}}` with `-H "Authorization: Bearer $BITBUCKET_TOKEN"`. Inspect the PR with `GET .../pullrequests/{{pr_id}}` and its diff with `GET .../pullrequests/{{pr_id}}/diff`. Reply with `POST .../pullrequests/{{pr_id}}/comments` and JSON body `{"content":{"raw":"<reply>"}}`. Find a PR by branch with `GET .../pullrequests?q=source.branch.name="<branch>"`. Open a PR with `POST .../pullrequests` and JSON body `{"title":"<title>","source":{"branch":{"name":"<branch>"}},"destination":{"branch":{"name":"<base>"}}}`.',
|
|
33
|
+
variables: [
|
|
34
|
+
{ name: "repo", description: "Repository full name (<workspace>/<repo_slug>)" },
|
|
35
|
+
{ name: "pr_id", description: "Pull request ID" },
|
|
36
|
+
],
|
|
37
|
+
category: "common",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const SHARED_VARIABLES = [
|
|
41
|
+
{ name: "pr_id", description: "Pull request ID" },
|
|
42
|
+
{ name: "pr_title", description: "Pull request title" },
|
|
43
|
+
{ name: "repo", description: "Repository full name (<workspace>/<repo_slug>)" },
|
|
44
|
+
{ name: "pr_url", description: "Pull request web URL" },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const PR_BRANCH_VARIABLES = [
|
|
48
|
+
{ name: "source_branch", description: "Source branch name" },
|
|
49
|
+
{ name: "target_branch", description: "Target branch name" },
|
|
50
|
+
{ name: "context_section", description: "Context section with description or empty string" },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Pull request events
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
registerTemplate({
|
|
58
|
+
eventType: "bitbucket.pull_request.opened",
|
|
59
|
+
header: "[Bitbucket PR #{{pr_id}}] {{pr_title}}",
|
|
60
|
+
defaultBody: `Repo: {{repo}}
|
|
61
|
+
Author: {{username}}
|
|
62
|
+
Branch: {{source_branch}} → {{target_branch}}
|
|
63
|
+
URL: {{pr_url}}
|
|
64
|
+
|
|
65
|
+
{{context_section}}{{@template[common.command_suggestions.bitbucket_pr]}}{{@template[common.delegation_instruction.bitbucket]}}`,
|
|
66
|
+
variables: [
|
|
67
|
+
...SHARED_VARIABLES,
|
|
68
|
+
{
|
|
69
|
+
name: "username",
|
|
70
|
+
description:
|
|
71
|
+
"Author identity — resolved canonical name (e.g. 'Luis (bitbucket:557058:…)') or the UNKNOWN sentinel",
|
|
72
|
+
},
|
|
73
|
+
...PR_BRANCH_VARIABLES,
|
|
74
|
+
],
|
|
75
|
+
category: "event",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
registerTemplate({
|
|
79
|
+
eventType: "bitbucket.pull_request.review_requested",
|
|
80
|
+
header: "[Bitbucket PR #{{pr_id}}] Review requested: {{pr_title}}",
|
|
81
|
+
defaultBody: `Repo: {{repo}}
|
|
82
|
+
Author: {{username}}
|
|
83
|
+
Branch: {{source_branch}} → {{target_branch}}
|
|
84
|
+
URL: {{pr_url}}
|
|
85
|
+
|
|
86
|
+
You were added as a reviewer on this pull request.
|
|
87
|
+
|
|
88
|
+
{{context_section}}{{@template[common.command_suggestions.bitbucket_pr]}}{{@template[common.delegation_instruction.bitbucket]}}`,
|
|
89
|
+
variables: [
|
|
90
|
+
...SHARED_VARIABLES,
|
|
91
|
+
{
|
|
92
|
+
name: "username",
|
|
93
|
+
description:
|
|
94
|
+
"Author identity — resolved canonical name (e.g. 'Luis (bitbucket:557058:…)') or the UNKNOWN sentinel",
|
|
95
|
+
},
|
|
96
|
+
...PR_BRANCH_VARIABLES,
|
|
97
|
+
],
|
|
98
|
+
category: "event",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Pull request comment events
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
registerTemplate({
|
|
106
|
+
eventType: "bitbucket.comment.mentioned",
|
|
107
|
+
header: "[Bitbucket Comment on PR #{{pr_id}}] {{username}} mentioned bot",
|
|
108
|
+
defaultBody: `Repo: {{repo}}
|
|
109
|
+
PR: {{pr_title}}
|
|
110
|
+
URL: {{pr_url}}
|
|
111
|
+
{{location_section}}
|
|
112
|
+
Comment:
|
|
113
|
+
{{context}}{{existing_task_note}}
|
|
114
|
+
|
|
115
|
+
{{@template[common.command_suggestions.bitbucket_pr]}}{{@template[common.delegation_instruction.bitbucket]}}`,
|
|
116
|
+
variables: [
|
|
117
|
+
...SHARED_VARIABLES,
|
|
118
|
+
{
|
|
119
|
+
name: "username",
|
|
120
|
+
description:
|
|
121
|
+
"Comment author identity — resolved canonical name (e.g. 'Luis (bitbucket:557058:…)') or the UNKNOWN sentinel",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "location_section",
|
|
125
|
+
description: "`File: <path>:<line>` line for inline comments, or empty string",
|
|
126
|
+
},
|
|
127
|
+
{ name: "context", description: "Extracted mention context from comment" },
|
|
128
|
+
{
|
|
129
|
+
name: "existing_task_note",
|
|
130
|
+
description: "Note about existing active task, or empty string",
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
category: "event",
|
|
134
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitbucket Cloud webhook payload types (subsets of the documented event
|
|
3
|
+
* payloads; only the fields the handlers read).
|
|
4
|
+
* https://support.atlassian.com/bitbucket-cloud/docs/event-payloads/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface BitbucketAccount {
|
|
8
|
+
type?: string;
|
|
9
|
+
/** Atlassian account id, e.g. `557058:<uuid>`. Absent for some app/team actors. */
|
|
10
|
+
account_id?: string;
|
|
11
|
+
/** Bitbucket user UUID, wrapped in braces: `{…}`. */
|
|
12
|
+
uuid?: string;
|
|
13
|
+
display_name?: string;
|
|
14
|
+
nickname?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface BitbucketRepository {
|
|
18
|
+
/** Repository UUID, wrapped in braces: `{…}`. Stable across renames. */
|
|
19
|
+
uuid: string;
|
|
20
|
+
/** `<workspace>/<repo_slug>` */
|
|
21
|
+
full_name: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
links?: { html?: { href: string } };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface BitbucketBranchRef {
|
|
27
|
+
branch?: { name: string };
|
|
28
|
+
commit?: { hash: string };
|
|
29
|
+
repository?: BitbucketRepository;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface BitbucketPullRequest {
|
|
33
|
+
id: number;
|
|
34
|
+
title: string;
|
|
35
|
+
description?: string | null;
|
|
36
|
+
state?: "OPEN" | "MERGED" | "DECLINED" | "SUPERSEDED";
|
|
37
|
+
author?: BitbucketAccount;
|
|
38
|
+
source?: BitbucketBranchRef;
|
|
39
|
+
destination?: BitbucketBranchRef;
|
|
40
|
+
reviewers?: BitbucketAccount[];
|
|
41
|
+
links?: { html?: { href: string } };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface BitbucketComment {
|
|
45
|
+
id: number;
|
|
46
|
+
content: { raw: string; markup?: string; html?: string };
|
|
47
|
+
user?: BitbucketAccount;
|
|
48
|
+
inline?: { path: string; from?: number | null; to?: number | null } | null;
|
|
49
|
+
parent?: { id: number } | null;
|
|
50
|
+
links?: { html?: { href: string } };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** `pullrequest:created`, `pullrequest:fulfilled`, `pullrequest:rejected` */
|
|
54
|
+
export interface PullRequestEvent {
|
|
55
|
+
actor: BitbucketAccount;
|
|
56
|
+
pullrequest: BitbucketPullRequest;
|
|
57
|
+
repository: BitbucketRepository;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** `pullrequest:comment_created` */
|
|
61
|
+
export interface PullRequestCommentEvent extends PullRequestEvent {
|
|
62
|
+
comment: BitbucketComment;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type BitbucketWebhookEvent = PullRequestEvent | PullRequestCommentEvent;
|