@desplega.ai/agent-swarm 2.4.5 → 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.
Files changed (272) hide show
  1. package/README.md +4 -2
  2. package/dist/{acp-adapter-2wdkktv6.js → acp-adapter-11gp67nr.js} +8 -6
  3. package/dist/{actions-c40ebcm4.js → actions-8p2kze8j.js} +23 -21
  4. package/dist/{amp-adapter-wb67m95z.js → amp-adapter-z4qvhacj.js} +2 -2
  5. package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
  6. package/dist/{app-353vre8g.js → app-0qp0a2gk.js} +3 -3
  7. package/dist/{assistant-sj1njh23.js → assistant-jedd3ktm.js} +24 -22
  8. package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
  9. package/dist/{boot-reembed-qdkvxeem.js → boot-reembed-4xwq5tg0.js} +1 -1
  10. package/dist/{boot-reembed-b9c3az3c.js → boot-reembed-n4wq5dd2.js} +1 -1
  11. package/dist/{boot-scrub-sweep-m3wh8eks.js → boot-scrub-sweep-69h90xcq.js} +4 -4
  12. package/dist/{boot-seeds-j2273s54.js → boot-seeds-3vn0m9sg.js} +4 -4
  13. package/dist/{claude-adapter-m7q0a085.js → claude-adapter-221mvad5.js} +8 -6
  14. package/dist/{claude-managed-adapter-5ptb6tzs.js → claude-managed-adapter-gt6pa35d.js} +2 -2
  15. package/dist/{claude-managed-setup-smrqk5j5.js → claude-managed-setup-c3hp36ph.js} +4 -4
  16. package/dist/{claude-sdk-session-38xnz21e.js → claude-sdk-session-8thjsk5e.js} +9012 -8928
  17. package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
  18. package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
  19. package/dist/{cli-dk44dxec.js → cli-0y6f0ckq.js} +1 -1
  20. package/dist/{cli-kpw04va0.js → cli-0z3vjrpm.js} +1 -1
  21. package/dist/{cli-94gwyg7m.js → cli-179xcyky.js} +9 -9
  22. package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
  23. package/dist/{cli-4zfqgjc5.js → cli-1pr672hz.js} +2 -2
  24. package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
  25. package/dist/{cli-43x64hv3.js → cli-36j4jnk3.js} +4 -4
  26. package/dist/{cli-k8g16gz3.js → cli-3qv8naq8.js} +115 -119
  27. package/dist/cli-3zwye9sn.js +1193 -0
  28. package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
  29. package/dist/{cli-v8fjakwf.js → cli-4w24f7z3.js} +1 -1
  30. package/dist/{cli-n72rg53p.js → cli-5kzk3472.js} +812 -418
  31. package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
  32. package/dist/{cli-j99azg3g.js → cli-5rvg1ddk.js} +5 -5
  33. package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
  34. package/dist/{cli-848hxdwj.js → cli-6rg2jn2y.js} +2 -1
  35. package/dist/{cli-fv5hepyy.js → cli-7bwtc6yx.js} +3 -3
  36. package/dist/{cli-ms85309d.js → cli-7g40g8hq.js} +11 -11
  37. package/dist/{cli-xpa64rek.js → cli-92mqenkc.js} +2362 -159
  38. package/dist/{cli-018r4jq6.js → cli-9ce2xw98.js} +8 -8
  39. package/dist/{cli-1syn2xy1.js → cli-bgdtv62r.js} +32 -18
  40. package/dist/{cli-wxbn3yfx.js → cli-cz5nmdxt.js} +2 -2
  41. package/dist/{cli-wt645v7q.js → cli-dabehxhp.js} +1 -1
  42. package/dist/{cli-vfha21c9.js → cli-e5bxnjps.js} +2 -2
  43. package/dist/{cli-wt0jf9hm.js → cli-fcc5qe3f.js} +553 -193
  44. package/dist/{cli-ftc1g5tj.js → cli-fes51ya2.js} +3 -3
  45. package/dist/{cli-3a45zcgm.js → cli-ftmq4nvk.js} +2 -2
  46. package/dist/{cli-37j04k05.js → cli-fy1jyyym.js} +13 -13
  47. package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
  48. package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
  49. package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
  50. package/dist/{cli-g35m2xwm.js → cli-j1eghqfm.js} +2 -2
  51. package/dist/{cli-qfxc84gh.js → cli-jpxsrxcb.js} +1 -1
  52. package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
  53. package/dist/{cli-p14m2cnp.js → cli-mbfj11fq.js} +1 -1
  54. package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
  55. package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
  56. package/dist/{cli-f01h25yt.js → cli-n6kmqngw.js} +2 -2
  57. package/dist/{cli-v0yrfmdh.js → cli-nnvjb17s.js} +3 -3
  58. package/dist/{cli-1dmcq2eh.js → cli-nzfnt2gs.js} +1 -1
  59. package/dist/{cli-625tb7aq.js → cli-p859hcc8.js} +2 -2
  60. package/dist/{cli-smvcw9z5.js → cli-pasyvjmm.js} +1 -1
  61. package/dist/cli-pkaqk57w.js +128 -0
  62. package/dist/{cli-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
  63. package/dist/{cli-9yqgm9nd.js → cli-r25qpaxt.js} +161 -63
  64. package/dist/{cli-eww120g8.js → cli-rh5smt8t.js} +18 -4
  65. package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
  66. package/dist/{cli-a92r1127.js → cli-s4sntwt4.js} +2 -2
  67. package/dist/{cli-be2161h1.js → cli-segs7hx9.js} +6 -6
  68. package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
  69. package/dist/{cli-z7d29rw9.js → cli-sy8f9705.js} +4 -4
  70. package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
  71. package/dist/{cli-st8aw1m6.js → cli-we0ra057.js} +4 -4
  72. package/dist/{cli-7c9qs530.js → cli-x21dqn18.js} +5 -5
  73. package/dist/{cli-1sp861s4.js → cli-x6vf4sad.js} +11 -11
  74. package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
  75. package/dist/{cli-tw4p90jb.js → cli-xawhpgc9.js} +5 -5
  76. package/dist/{cli-f0g3pgvf.js → cli-xd4eqtr2.js} +2 -2
  77. package/dist/{cli-1f3qzw6y.js → cli-xt768r5m.js} +2 -2
  78. package/dist/{cli-cxxe61f6.js → cli-y4x8ww6s.js} +2 -2
  79. package/dist/{cli-bnes4z6b.js → cli-y9jvxfhk.js} +3 -3
  80. package/dist/{cli-qwhy2qma.js → cli-z63a269p.js} +2 -2
  81. package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
  82. package/dist/cli.js +29 -27
  83. package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
  84. package/dist/{codex-adapter-tn6k5mkg.js → codex-adapter-tcg1q3jk.js} +6 -4
  85. package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
  86. package/dist/{codex-session-runner-7vjt7jbw.js → codex-session-runner-xfx4wd9x.js} +7 -5
  87. package/dist/{commands-t7rz9146.js → commands-ew83nj70.js} +2 -2
  88. package/dist/{controller-m2jasma5.js → controller-gnq1k1mf.js} +4 -4
  89. package/dist/{controllers-connect-9ftpex99.js → controllers-connect-tw1dan9r.js} +25 -23
  90. package/dist/{cursor-adapter-mjv79e4z.js → cursor-adapter-yq5xgfcg.js} +1 -1
  91. package/dist/{db-6sfrn9g3.js → db-7gbc53fd.js} +14 -2
  92. package/dist/{db-rn673r06.js → db-t86xrnes.js} +3 -3
  93. package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
  94. package/dist/{dispatcher-c155xz0p.js → dispatcher-64bdyhps.js} +22 -20
  95. package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
  96. package/dist/{dsh-adapter-3mmszc1x.js → dsh-adapter-tvhc4k2d.js} +1 -1
  97. package/dist/{e2b-16d6ch39.js → e2b-9gkdhm3c.js} +2 -2
  98. package/dist/{e2b-zn0b4w5w.js → e2b-cjtf8k1z.js} +2 -2
  99. package/dist/{easter-egg-3d-5cgynaja.js → easter-egg-3d-e4qv8dxj.js} +1 -1
  100. package/dist/{event-bus-9vpk8gmq.js → event-bus-3z78ep5n.js} +1 -1
  101. package/dist/{execute-t4e6m3rw.js → execute-0xpz8950.js} +85 -23
  102. package/dist/{external-channel-35k6tkv8.js → external-channel-e9nw24zv.js} +2 -2
  103. package/dist/{google-generative-ai-bmq8gkf3.js → google-generative-ai-mk9vcpe1.js} +15 -15
  104. package/dist/{google-vertex-b1rvp52s.js → google-vertex-q580kz8k.js} +20 -20
  105. package/dist/{grok-adapter-tfrq7tg1.js → grok-adapter-v82srhrx.js} +9 -7
  106. package/dist/handlers-c9ajzhbn.js +41 -0
  107. package/dist/{heartbeat-dyg4npkp.js → heartbeat-2vqs1319.js} +25 -23
  108. package/dist/{hook-x26b31bg.js → hook-01nk13wx.js} +7 -5
  109. package/dist/{hook-drspbwpe.js → hook-jzpheegg.js} +9 -7
  110. package/dist/{http-pk972xyw.js → http-fc1k6ec8.js} +544 -76
  111. package/dist/index-07ef7nkm.js +42 -0
  112. package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
  113. package/dist/index-7yts8n9s.js +43 -0
  114. package/dist/index-q3t40ryv.js +46 -0
  115. package/dist/{index-arytgj5q.js → index-sepja052.js} +1 -1
  116. package/dist/{index-jwxkkex2.js → index-zdycv3ja.js} +13 -11
  117. package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
  118. package/dist/{keepalive-7yrng7px.js → keepalive-5nh2rhmr.js} +4 -4
  119. package/dist/lead-aayq64jn.js +79 -0
  120. package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
  121. package/dist/{maintenance-e6cemdqt.js → maintenance-hjhzdt3k.js} +1 -1
  122. package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
  123. package/dist/{node-de6akczr.js → node-jzjj7r9x.js} +1 -1
  124. package/dist/{oauth-refresh-sweep-yw3xdbcj.js → oauth-refresh-sweep-y3n1knmd.js} +3 -3
  125. package/dist/{onboard-0a9y5yrc.js → onboard-1zq3m7db.js} +2 -2
  126. package/dist/{openai-codex-responses-de9pvs4d.js → openai-codex-responses-79s8xnbh.js} +29 -23
  127. package/dist/{openai-completions-mtqge3nf.js → openai-completions-tdbyts0x.js} +21 -20
  128. package/dist/{openai-decisions-ygk4kkxd.js → openai-decisions-bqhnjyfz.js} +2 -1
  129. package/dist/{openai-responses-dkyng71g.js → openai-responses-dm3t5vqp.js} +26 -17
  130. package/dist/{opencode-adapter-x4tqzhkh.js → opencode-adapter-e56086m1.js} +7 -5
  131. package/dist/{openrouter-images-wvv7camj.js → openrouter-images-ssttr1y4.js} +6 -3
  132. package/dist/{otel-impl-trjpkfb7.js → otel-impl-h9yyza47.js} +2 -2
  133. package/dist/{pi-messages-nhh1pkz2.js → pi-messages-3nf8bq1a.js} +6 -4
  134. package/dist/{pi-mono-adapter-mcfkn9vf.js → pi-mono-adapter-tzps8f38.js} +9 -37
  135. package/dist/{pricing-refresh-d2b5my0y.js → pricing-refresh-nevhat8h.js} +3 -3
  136. package/dist/{rbac-roles-nxh5ahwk.js → rbac-roles-4y6ngvzq.js} +1 -1
  137. package/dist/{rbac-roles-hey3yv9d.js → rbac-roles-exbg8xb6.js} +2 -2
  138. package/dist/{render-v2-rr3p2atg.js → render-v2-cfsh13z3.js} +3 -3
  139. package/dist/{resume-2efkf1s1.js → resume-5s2t0dbs.js} +10 -8
  140. package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
  141. package/dist/{session-access-s2mttxpw.js → session-access-dnbyp3cn.js} +6 -4
  142. package/dist/{setup-vypma5yt.js → setup-1wj1y77j.js} +1 -1
  143. package/dist/{typesafe-system-one-2hgpayzy.js → typesafe-system-one-bre38hvs.js} +4 -2
  144. package/dist/{virtual-modules-4r3zkc20.js → virtual-modules-mnfbh5mr.js} +8 -2
  145. package/dist/{work-objects-rvhckdkp.js → work-objects-7mg6216g.js} +25 -23
  146. package/dist/worker-nwb789mg.js +79 -0
  147. package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
  148. package/openapi.json +785 -5
  149. package/package.json +9 -9
  150. package/src/assets/key.ts +17 -0
  151. package/src/azure-devops/handlers.ts +1 -1
  152. package/src/be/db/approvals.ts +18 -0
  153. package/src/be/db/session-moves.ts +164 -0
  154. package/src/be/db/session-tree.ts +384 -0
  155. package/src/be/db/tasks/read.ts +40 -4
  156. package/src/be/db/workflow-runs.ts +96 -0
  157. package/src/be/db.ts +80 -236
  158. package/src/be/onboarding.ts +3 -0
  159. package/src/be/skill-sync.ts +43 -2
  160. package/src/be/swarm-config-guard.ts +1 -0
  161. package/src/bitbucket/api.ts +81 -0
  162. package/src/bitbucket/auth.ts +64 -0
  163. package/src/bitbucket/handlers.ts +434 -0
  164. package/src/bitbucket/index.ts +28 -0
  165. package/src/bitbucket/templates.ts +134 -0
  166. package/src/bitbucket/types.ts +65 -0
  167. package/src/commands/runner.ts +55 -3
  168. package/src/e2b/env.ts +1 -0
  169. package/src/extensions/contract-types.generated.ts +1 -1
  170. package/src/extensions/contract.ts +2 -2
  171. package/src/extensions/post-bridge.ts +1 -0
  172. package/src/github/handlers.ts +6 -6
  173. package/src/gitlab/handlers.ts +5 -5
  174. package/src/http/assets.ts +14 -2
  175. package/src/http/core.ts +12 -0
  176. package/src/http/index.ts +4 -0
  177. package/src/http/onboarding.ts +1 -0
  178. package/src/http/sessions.ts +366 -12
  179. package/src/http/skills.ts +48 -1
  180. package/src/http/tasks.ts +1 -1
  181. package/src/http/users.ts +1 -0
  182. package/src/http/webhooks.ts +141 -0
  183. package/src/integrations/chat-sdk/adapters.ts +19 -3
  184. package/src/integrations/chat-sdk/facts.ts +30 -12
  185. package/src/integrations/chat-sdk/outbound.ts +38 -1
  186. package/src/providers/acp-targets.ts +1 -1
  187. package/src/providers/amp-adapter.ts +1 -1
  188. package/src/providers/pi-mono-adapter.ts +5 -53
  189. package/src/scripts-runtime/types/swarm-extension.d.ts +6 -4
  190. package/src/slack/handlers.test.ts +158 -0
  191. package/src/slack/handlers.ts +19 -28
  192. package/src/tasks/context-key.ts +61 -1
  193. package/src/tests/bitbucket-auth.test.ts +156 -0
  194. package/src/tests/bitbucket-handlers.test.ts +425 -0
  195. package/src/tests/bitbucket-webhook.test.ts +220 -0
  196. package/src/tests/chat-sdk-inbound.test.ts +39 -0
  197. package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
  198. package/src/tests/context-key.test.ts +50 -0
  199. package/src/tests/core-auth.test.ts +5 -0
  200. package/src/tests/db-task-read-contract.test.ts +7 -0
  201. package/src/tests/db-task-write-contract.test.ts +6 -0
  202. package/src/tests/docs-index-footprint.test.js +3 -3
  203. package/src/tests/fixtures/bitbucket/pullrequest-comment-created-inline.json +174 -0
  204. package/src/tests/fixtures/bitbucket/pullrequest-comment-created.json +170 -0
  205. package/src/tests/fixtures/bitbucket/pullrequest-created-mention.json +132 -0
  206. package/src/tests/fixtures/bitbucket/pullrequest-created-reviewer.json +140 -0
  207. package/src/tests/fixtures/bitbucket/pullrequest-fulfilled.json +134 -0
  208. package/src/tests/fixtures/bitbucket/pullrequest-rejected.json +133 -0
  209. package/src/tests/generate-mcp-docs.test.ts +101 -0
  210. package/src/tests/gitlab-handlers.test.ts +3 -3
  211. package/src/tests/gitlab-vcs-db.test.ts +4 -4
  212. package/src/tests/integration-disable-flags.test.ts +36 -0
  213. package/src/tests/mcp-tools.test.ts +1 -1
  214. package/src/tests/onboarding.test.ts +12 -0
  215. package/src/tests/pi-codemode.test.ts +8 -2
  216. package/src/tests/preload-env.test.ts +57 -0
  217. package/src/tests/preload.ts +21 -0
  218. package/src/tests/prompt-template-remaining.test.ts +38 -0
  219. package/src/tests/run-root-coverage.test.ts +39 -0
  220. package/src/tests/runner-bitbucket-vcs.test.ts +191 -0
  221. package/src/tests/runner-skills-refresh.test.ts +58 -0
  222. package/src/tests/secret-scrubber.test.ts +15 -0
  223. package/src/tests/sessions-move.test.ts +576 -0
  224. package/src/tests/sessions-tree.test.ts +493 -0
  225. package/src/tests/skill-files-http.test.ts +50 -0
  226. package/src/tests/skills-bundle.test.ts +99 -0
  227. package/src/tests/swarm-config-export-to-env.test.ts +2 -1
  228. package/src/tests/vcs-bitbucket.test.ts +91 -0
  229. package/src/tests/vcs-provider-isolation.test.ts +123 -0
  230. package/src/tests/vcs-provider.test.ts +16 -1
  231. package/src/tests/vcs-tracking.test.ts +3 -3
  232. package/src/tests/workflow-foreach-bodies.test.ts +637 -0
  233. package/src/tests/workflow-foreach-concurrency.test.ts +342 -0
  234. package/src/tests/workflow-foreach-retry.test.ts +537 -0
  235. package/src/tests/workflow-foreach.test.ts +44 -23
  236. package/src/tests/workflow-step-claims.test.ts +6 -6
  237. package/src/tests/workflow-tla-races.test.ts +2 -2
  238. package/src/tools/script-delete.ts +1 -1
  239. package/src/tools/workflows/create-workflow.ts +5 -3
  240. package/src/types.ts +3 -2
  241. package/src/utils/config-env-export.ts +1 -0
  242. package/src/utils/secret-scrubber.ts +4 -0
  243. package/src/utils/skills-refresh.ts +83 -43
  244. package/src/vcs/bitbucket.ts +58 -0
  245. package/src/vcs/index.ts +2 -0
  246. package/src/vcs/types.ts +2 -2
  247. package/src/workflows/approval-resolution.ts +4 -1
  248. package/src/workflows/checkpoint.ts +9 -1
  249. package/src/workflows/constants.ts +3 -0
  250. package/src/workflows/definition.ts +48 -17
  251. package/src/workflows/engine.ts +25 -15
  252. package/src/workflows/executors/foreach.ts +45 -99
  253. package/src/workflows/foreach-body.ts +146 -0
  254. package/src/workflows/foreach-driver.ts +552 -0
  255. package/src/workflows/foreach-join.ts +206 -7
  256. package/src/workflows/node-timeout.ts +21 -0
  257. package/src/workflows/recovery.ts +68 -3
  258. package/src/workflows/resume.ts +221 -43
  259. package/src/workflows/retry-poller.ts +39 -24
  260. package/src/workflows/step-input.ts +59 -0
  261. package/src/workflows/task-step-routing.ts +114 -56
  262. package/templates/skills/slack-interaction/content.md +5 -0
  263. package/templates/skills/work-on-task/SKILL.md +1 -1
  264. package/templates/skills/work-on-task/content.md +1 -1
  265. package/dist/cli-9hstj09h.js +0 -2657
  266. package/dist/cli-yw91rnmw.js +0 -13
  267. package/dist/handlers-7gw64dxw.js +0 -39
  268. package/dist/index-bgdyvcnv.js +0 -40
  269. package/dist/index-gabbcdn2.js +0 -43
  270. package/dist/index-tfb26zhy.js +0 -41
  271. package/dist/lead-7e02z839.js +0 -77
  272. package/dist/worker-5snytpbd.js +0 -77
@@ -0,0 +1,92 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { createTelegramAdapter } from "@chat-adapter/telegram";
3
+ import { escapeTelegramHashLines, postChatMessage } from "../integrations/chat-sdk/outbound";
4
+ import { startTelegramHarness, type TelegramHarness } from "./chat-sdk-harness";
5
+
6
+ describe("escapeTelegramHashLines", () => {
7
+ test("escapes a line-leading # that is not a heading", () => {
8
+ expect(escapeTelegramHashLines("#2203 is still an open draft")).toBe(
9
+ "\\#2203 is still an open draft",
10
+ );
11
+ expect(escapeTelegramHashLines("#tag")).toBe("\\#tag");
12
+ expect(escapeTelegramHashLines("#1 priority")).toBe("\\#1 priority");
13
+ expect(escapeTelegramHashLines("##x")).toBe("\\##x");
14
+ });
15
+
16
+ test("keeps CommonMark ATX headings", () => {
17
+ for (const heading of ["# Title", "## Title", "###### Six", "#", "## ", " # Indented"]) {
18
+ expect(escapeTelegramHashLines(heading)).toBe(heading);
19
+ }
20
+ });
21
+
22
+ test("escapes a run of seven or more #", () => {
23
+ expect(escapeTelegramHashLines("####### seven")).toBe("\\####### seven");
24
+ expect(escapeTelegramHashLines("#######")).toBe("\\#######");
25
+ });
26
+
27
+ test("leaves a mid-line # alone", () => {
28
+ expect(escapeTelegramHashLines("PR #2203 is open, see #tag")).toBe(
29
+ "PR #2203 is open, see #tag",
30
+ );
31
+ });
32
+
33
+ test("escapes after a paragraph, a bold line, list and quote markers", () => {
34
+ expect(escapeTelegramHashLines("Status:\n#2203 open")).toBe("Status:\n\\#2203 open");
35
+ expect(escapeTelegramHashLines("**Status**\n#2203 open")).toBe("**Status**\n\\#2203 open");
36
+ expect(escapeTelegramHashLines("- #2203 open")).toBe("- \\#2203 open");
37
+ expect(escapeTelegramHashLines("1. #2203 open")).toBe("1. \\#2203 open");
38
+ expect(escapeTelegramHashLines("- [ ] #2203 open")).toBe("- [ ] \\#2203 open");
39
+ expect(escapeTelegramHashLines("> #2203 open")).toBe("> \\#2203 open");
40
+ });
41
+
42
+ test("leaves fenced code alone", () => {
43
+ const text = "```sh\n#!/bin/sh\n#2203\n```\n#2203 after";
44
+ expect(escapeTelegramHashLines(text)).toBe("```sh\n#!/bin/sh\n#2203\n```\n\\#2203 after");
45
+ expect(escapeTelegramHashLines("~~~\n#x\n```\n#y\n~~~\n#z")).toBe(
46
+ "~~~\n#x\n```\n#y\n~~~\n\\#z",
47
+ );
48
+ });
49
+
50
+ test("the MarkdownV2 fallback renders the escape as a literal #", () => {
51
+ const adapter = createTelegramAdapter({
52
+ botToken: "t",
53
+ mode: "webhook",
54
+ secretToken: "s",
55
+ } as Parameters<typeof createTelegramAdapter>[0]) as unknown as {
56
+ formatConverter: { fromMarkdown(markdown: string): string };
57
+ };
58
+ const render = (text: string) =>
59
+ adapter.formatConverter.fromMarkdown(escapeTelegramHashLines(text));
60
+ expect(render("#2203 is still an open draft")).toBe("\\#2203 is still an open draft");
61
+ expect(render("# Title")).toBe("*Title*");
62
+ });
63
+ });
64
+
65
+ describe("Telegram outbound markdown", () => {
66
+ let harness: TelegramHarness;
67
+
68
+ beforeAll(async () => {
69
+ harness = await startTelegramHarness();
70
+ });
71
+
72
+ afterAll(async () => {
73
+ await harness.stop();
74
+ });
75
+
76
+ async function sentMarkdown(text: string): Promise<string | undefined> {
77
+ const before = harness.mock.calls.length;
78
+ await postChatMessage({ adapter: "telegram", threadId: "telegram:4242", text });
79
+ const send = harness.mock.calls.slice(before).find((call) => call.method === "sendRichMessage");
80
+ return (send?.body.rich_message as { markdown?: string } | undefined)?.markdown;
81
+ }
82
+
83
+ test("a line-leading #2203 reaches sendRichMessage escaped; headings do not", async () => {
84
+ expect(await sentMarkdown("#2203 is still an open draft...")).toBe(
85
+ "\\#2203 is still an open draft...",
86
+ );
87
+ expect(await sentMarkdown("Done.\n#2203 is still an open draft...")).toBe(
88
+ "Done.\n\\#2203 is still an open draft...",
89
+ );
90
+ expect(await sentMarkdown("# Title\n## Sub\nPR #2203")).toBe("# Title\n## Sub\nPR #2203");
91
+ });
92
+ });
@@ -4,6 +4,7 @@ import {
4
4
  agentmailContextKey,
5
5
  azureDevOpsContextKey,
6
6
  azureDevOpsWorkItemContextKey,
7
+ bitbucketContextKey,
7
8
  buildJiraContextKey,
8
9
  chatSdkContextKey,
9
10
  githubContextKey,
@@ -157,6 +158,55 @@ describe("azure-devops work item keys", () => {
157
158
  });
158
159
  });
159
160
 
161
+ describe("bitbucket pull request keys", () => {
162
+ const UUID = "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d";
163
+
164
+ test("strips the braces Bitbucket wraps repository UUIDs in", () => {
165
+ expect(bitbucketContextKey({ repoUuid: `{${UUID}}`, pullRequestId: 12 })).toBe(
166
+ `task:trackers:bitbucket:${UUID}:pr:12`,
167
+ );
168
+ expect(bitbucketContextKey({ repoUuid: UUID, pullRequestId: "12" })).toBe(
169
+ `task:trackers:bitbucket:${UUID}:pr:12`,
170
+ );
171
+ });
172
+
173
+ test("lowercases the UUID so payload casing cannot fork a thread", () => {
174
+ expect(bitbucketContextKey({ repoUuid: `{${UUID.toUpperCase()}}`, pullRequestId: 3 })).toBe(
175
+ `task:trackers:bitbucket:${UUID}:pr:3`,
176
+ );
177
+ });
178
+
179
+ test("round-trips through parseContextKey", () => {
180
+ const key = bitbucketContextKey({ repoUuid: `{${UUID}}`, pullRequestId: 42 });
181
+ expect(parseContextKey(key)).toEqual({
182
+ family: "trackers",
183
+ subFamily: "bitbucket",
184
+ parts: { repoUuid: UUID, kind: "pr", pullRequestId: 42 },
185
+ });
186
+ });
187
+
188
+ test("rejects empty, braces-only, unbalanced or separator-bearing UUIDs", () => {
189
+ expect(() => bitbucketContextKey({ repoUuid: "", pullRequestId: 1 })).toThrow();
190
+ expect(() => bitbucketContextKey({ repoUuid: "{}", pullRequestId: 1 })).toThrow();
191
+ expect(() => bitbucketContextKey({ repoUuid: "{{abc}", pullRequestId: 1 })).toThrow();
192
+ expect(() => bitbucketContextKey({ repoUuid: "a:b", pullRequestId: 1 })).toThrow();
193
+ });
194
+
195
+ test("rejects a non-integer pull request id", () => {
196
+ expect(() => bitbucketContextKey({ repoUuid: UUID, pullRequestId: 1.5 })).toThrow(/integer/);
197
+ expect(() => bitbucketContextKey({ repoUuid: UUID, pullRequestId: "abc" })).toThrow(/integer/);
198
+ expect(() => bitbucketContextKey({ repoUuid: UUID, pullRequestId: -1 })).toThrow(/integer/);
199
+ });
200
+
201
+ test("parseContextKey rejects malformed bitbucket keys", () => {
202
+ expect(() => parseContextKey(`task:trackers:bitbucket:${UUID}:pr:abc`)).toThrow();
203
+ expect(() => parseContextKey(`task:trackers:bitbucket:${UUID}:issue:1`)).toThrow();
204
+ expect(() => parseContextKey(`task:trackers:bitbucket:${UUID}:pr`)).toThrow();
205
+ expect(() => parseContextKey("task:trackers:bitbucket::pr:1")).toThrow();
206
+ expect(() => parseContextKey(`task:trackers:bitbucket:${UUID}:pr:1:extra`)).toThrow();
207
+ });
208
+ });
209
+
160
210
  describe("parseContextKey", () => {
161
211
  test("round-trips slack keys", () => {
162
212
  const key = slackContextKey({ channelId: "C0A4J7GB0UD", threadTs: "1776800534.257079" });
@@ -88,6 +88,11 @@ describe("handleCore auth middleware (route() auth.apiKey=false is honored)", ()
88
88
  expect(res.status).not.toBe(401);
89
89
  });
90
90
 
91
+ test("Bitbucket webhook is public", async () => {
92
+ const res = await fetch(`http://localhost:${port}/api/bitbucket/webhook`, { method: "POST" });
93
+ expect(res.status).not.toBe(401);
94
+ });
95
+
91
96
  test("AgentMail webhook is still public", async () => {
92
97
  const res = await fetch(`http://localhost:${port}/api/agentmail/webhook`, { method: "POST" });
93
98
  expect(res.status).not.toBe(401);
@@ -102,6 +102,7 @@ type:UpsertAssetKeyMappingInput
102
102
  type:WorkflowFilters
103
103
  type:WorkflowRunListOptions
104
104
  type:WorkflowRunPage
105
+ value:AssetKeyMovesBlockedError
105
106
  value:EXTENSION_AGENT_ROLE
106
107
  value:ExtensionAgentAssignmentError
107
108
  value:KvTypeCollisionError
@@ -116,6 +117,7 @@ value:UNATTRIBUTED_USER_ID
116
117
  value:__resetSqliteVecExtensionPathCacheForTests
117
118
  value:abandonSlackOutcomeDelivery
118
119
  value:acceptTask
120
+ value:assertAssetKeyMovesAllowed
119
121
  value:assignUnassignedTaskPending
120
122
  value:autoEncryptLegacyPlaintextSecrets
121
123
  value:backfillSupersedeTaskResumeTaskId
@@ -215,6 +217,7 @@ value:ensureSlackRenderV2Activation
215
217
  value:extensionAgentAssignmentError
216
218
  value:failPendingResumeIfUnclaimed
217
219
  value:failTask
220
+ value:findActiveTasksByVcs
218
221
  value:findCompletedTaskInThread
219
222
  value:findExistingLinearTrackerContextWork
220
223
  value:findRecentCancelledTaskInThread
@@ -335,6 +338,7 @@ value:getPageVersion
335
338
  value:getPageVersions
336
339
  value:getPausedTasksForAgent
337
340
  value:getPendingApprovalVoteState
341
+ value:getPendingApprovalsForTasks
338
342
  value:getPendingChatRelayTasks
339
343
  value:getPendingEventWaitNames
340
344
  value:getPendingSlackRelayTasks
@@ -486,6 +490,7 @@ value:listUserFavorites
486
490
  value:listWorkflowRuns
487
491
  value:listWorkflowRunsPage
488
492
  value:listWorkflows
493
+ value:literalPrefixPattern
489
494
  value:markChatRelayAttempted
490
495
  value:markChatRelayDelivered
491
496
  value:markChatRelayGivenUp
@@ -505,6 +510,7 @@ value:markTaskSlackReplySent
505
510
  value:markTasksNotified
506
511
  value:maskSecrets
507
512
  value:moveAssetKey
513
+ value:moveTaskChainKeyInTx
508
514
  value:moveTaskFromBacklog
509
515
  value:moveTaskToBacklog
510
516
  value:normalizeSkillFilePath
@@ -615,6 +621,7 @@ type:PendingChatRelay
615
621
  type:TaskFilters
616
622
  value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
617
623
  value:TASK_LINEAGE_LIMITS
624
+ value:findActiveTasksByVcs
618
625
  value:findCompletedTaskInThread
619
626
  value:findExistingLinearTrackerContextWork
620
627
  value:findRecentCancelledTaskInThread
@@ -125,6 +125,7 @@ type:UpsertAssetKeyMappingInput
125
125
  type:WorkflowFilters
126
126
  type:WorkflowRunListOptions
127
127
  type:WorkflowRunPage
128
+ value:AssetKeyMovesBlockedError
128
129
  value:EXTENSION_AGENT_ROLE
129
130
  value:ExtensionAgentAssignmentError
130
131
  value:KvTypeCollisionError
@@ -139,6 +140,7 @@ value:UNATTRIBUTED_USER_ID
139
140
  value:__resetSqliteVecExtensionPathCacheForTests
140
141
  value:abandonSlackOutcomeDelivery
141
142
  value:acceptTask
143
+ value:assertAssetKeyMovesAllowed
142
144
  value:assignUnassignedTaskPending
143
145
  value:autoEncryptLegacyPlaintextSecrets
144
146
  value:backfillSupersedeTaskResumeTaskId
@@ -238,6 +240,7 @@ value:ensureSlackRenderV2Activation
238
240
  value:extensionAgentAssignmentError
239
241
  value:failPendingResumeIfUnclaimed
240
242
  value:failTask
243
+ value:findActiveTasksByVcs
241
244
  value:findCompletedTaskInThread
242
245
  value:findExistingLinearTrackerContextWork
243
246
  value:findRecentCancelledTaskInThread
@@ -358,6 +361,7 @@ value:getPageVersion
358
361
  value:getPageVersions
359
362
  value:getPausedTasksForAgent
360
363
  value:getPendingApprovalVoteState
364
+ value:getPendingApprovalsForTasks
361
365
  value:getPendingChatRelayTasks
362
366
  value:getPendingEventWaitNames
363
367
  value:getPendingSlackRelayTasks
@@ -509,6 +513,7 @@ value:listUserFavorites
509
513
  value:listWorkflowRuns
510
514
  value:listWorkflowRunsPage
511
515
  value:listWorkflows
516
+ value:literalPrefixPattern
512
517
  value:markChatRelayAttempted
513
518
  value:markChatRelayDelivered
514
519
  value:markChatRelayGivenUp
@@ -528,6 +533,7 @@ value:markTaskSlackReplySent
528
533
  value:markTasksNotified
529
534
  value:maskSecrets
530
535
  value:moveAssetKey
536
+ value:moveTaskChainKeyInTx
531
537
  value:moveTaskFromBacklog
532
538
  value:moveTaskToBacklog
533
539
  value:normalizeSkillFilePath
@@ -78,7 +78,7 @@ describe("docs API index footprint", () => {
78
78
  expect(tasks).toContain('"path":"/api/task-templates","method":"get"');
79
79
  });
80
80
 
81
- test("OpenAPI and generated pages keep all 425 operations", async () => {
81
+ test("OpenAPI and generated pages keep all 444 operations", async () => {
82
82
  const spec = await Bun.file(resolve(repoRoot, "openapi.json")).json();
83
83
  const operationKeys = [];
84
84
  const tags = new Set();
@@ -103,8 +103,8 @@ describe("docs API index footprint", () => {
103
103
  expect(spec.paths["/api/agents/{id}/runtime"]?.get).toBeDefined();
104
104
 
105
105
  const generatedKeys = await generatedOperationKeys();
106
- expect(operationKeys).toHaveLength(440);
107
- expect(generatedKeys).toHaveLength(440);
106
+ expect(operationKeys).toHaveLength(445);
107
+ expect(generatedKeys).toHaveLength(445);
108
108
  expect(new Set(generatedKeys)).toEqual(new Set(operationKeys));
109
109
  });
110
110
 
@@ -0,0 +1,174 @@
1
+ {
2
+ "actor": {
3
+ "type": "user",
4
+ "display_name": "Dana Developer",
5
+ "nickname": "dana",
6
+ "account_id": "557058:11111111-1111-4111-8111-111111111111",
7
+ "uuid": "{11111111-2222-4333-8444-555555555555}",
8
+ "links": {
9
+ "html": {
10
+ "href": "https://bitbucket.org/%7B11111111-2222-4333-8444-555555555555%7D/"
11
+ }
12
+ }
13
+ },
14
+ "repository": {
15
+ "type": "repository",
16
+ "full_name": "acme-ws/widgets",
17
+ "name": "widgets",
18
+ "is_private": true,
19
+ "scm": "git",
20
+ "uuid": "{0f9b2c4e-7a1d-4e5f-9c3b-2d8e6a1f4b70}",
21
+ "links": {
22
+ "html": {
23
+ "href": "https://bitbucket.org/acme-ws/widgets"
24
+ }
25
+ },
26
+ "workspace": {
27
+ "type": "workspace",
28
+ "slug": "acme-ws",
29
+ "name": "Acme",
30
+ "uuid": "{a1a1a1a1-b2b2-4c3c-8d4d-e5e5e5e5e5e5}"
31
+ },
32
+ "project": {
33
+ "type": "project",
34
+ "key": "WID",
35
+ "name": "Widgets"
36
+ }
37
+ },
38
+ "pullrequest": {
39
+ "type": "pullrequest",
40
+ "id": 42,
41
+ "title": "Add retry to the widget fetcher",
42
+ "description": "Adds exponential backoff.",
43
+ "state": "OPEN",
44
+ "author": {
45
+ "type": "user",
46
+ "display_name": "Dana Developer",
47
+ "nickname": "dana",
48
+ "account_id": "557058:11111111-1111-4111-8111-111111111111",
49
+ "uuid": "{11111111-2222-4333-8444-555555555555}",
50
+ "links": {
51
+ "html": {
52
+ "href": "https://bitbucket.org/%7B11111111-2222-4333-8444-555555555555%7D/"
53
+ }
54
+ }
55
+ },
56
+ "source": {
57
+ "branch": {
58
+ "name": "feature/retry"
59
+ },
60
+ "commit": {
61
+ "hash": "1a2b3c4d5e6f"
62
+ },
63
+ "repository": {
64
+ "type": "repository",
65
+ "full_name": "acme-ws/widgets",
66
+ "name": "widgets",
67
+ "is_private": true,
68
+ "scm": "git",
69
+ "uuid": "{0f9b2c4e-7a1d-4e5f-9c3b-2d8e6a1f4b70}",
70
+ "links": {
71
+ "html": {
72
+ "href": "https://bitbucket.org/acme-ws/widgets"
73
+ }
74
+ },
75
+ "workspace": {
76
+ "type": "workspace",
77
+ "slug": "acme-ws",
78
+ "name": "Acme",
79
+ "uuid": "{a1a1a1a1-b2b2-4c3c-8d4d-e5e5e5e5e5e5}"
80
+ },
81
+ "project": {
82
+ "type": "project",
83
+ "key": "WID",
84
+ "name": "Widgets"
85
+ }
86
+ }
87
+ },
88
+ "destination": {
89
+ "branch": {
90
+ "name": "main"
91
+ },
92
+ "commit": {
93
+ "hash": "6f5e4d3c2b1a"
94
+ },
95
+ "repository": {
96
+ "type": "repository",
97
+ "full_name": "acme-ws/widgets",
98
+ "name": "widgets",
99
+ "is_private": true,
100
+ "scm": "git",
101
+ "uuid": "{0f9b2c4e-7a1d-4e5f-9c3b-2d8e6a1f4b70}",
102
+ "links": {
103
+ "html": {
104
+ "href": "https://bitbucket.org/acme-ws/widgets"
105
+ }
106
+ },
107
+ "workspace": {
108
+ "type": "workspace",
109
+ "slug": "acme-ws",
110
+ "name": "Acme",
111
+ "uuid": "{a1a1a1a1-b2b2-4c3c-8d4d-e5e5e5e5e5e5}"
112
+ },
113
+ "project": {
114
+ "type": "project",
115
+ "key": "WID",
116
+ "name": "Widgets"
117
+ }
118
+ }
119
+ },
120
+ "merge_commit": null,
121
+ "participants": [],
122
+ "reviewers": [],
123
+ "close_source_branch": true,
124
+ "created_on": "2026-10-22T09:00:00.000000+00:00",
125
+ "updated_on": "2026-10-22T09:00:00.000000+00:00",
126
+ "links": {
127
+ "html": {
128
+ "href": "https://bitbucket.org/acme-ws/widgets/pull-requests/42"
129
+ }
130
+ }
131
+ },
132
+ "comment": {
133
+ "type": "pullrequest_comment",
134
+ "id": 9002,
135
+ "content": {
136
+ "type": "rendered",
137
+ "raw": "@{557058:00000000-0000-4000-8000-0000000000b0} this off-by-one looks wrong",
138
+ "markup": "markdown",
139
+ "html": ""
140
+ },
141
+ "user": {
142
+ "type": "user",
143
+ "display_name": "Dana Developer",
144
+ "nickname": "dana",
145
+ "account_id": "557058:11111111-1111-4111-8111-111111111111",
146
+ "uuid": "{11111111-2222-4333-8444-555555555555}",
147
+ "links": {
148
+ "html": {
149
+ "href": "https://bitbucket.org/%7B11111111-2222-4333-8444-555555555555%7D/"
150
+ }
151
+ }
152
+ },
153
+ "deleted": false,
154
+ "pending": false,
155
+ "inline": {
156
+ "from": null,
157
+ "to": 17,
158
+ "path": "src/fetcher.ts"
159
+ },
160
+ "parent": null,
161
+ "created_on": "2026-10-22T09:05:00.000000+00:00",
162
+ "updated_on": "2026-10-22T09:05:00.000000+00:00",
163
+ "links": {
164
+ "html": {
165
+ "href": "https://bitbucket.org/acme-ws/widgets/pull-requests/42/_/diff#comment-9002"
166
+ }
167
+ },
168
+ "pullrequest": {
169
+ "type": "pullrequest",
170
+ "id": 42,
171
+ "title": "Add retry to the widget fetcher"
172
+ }
173
+ }
174
+ }
@@ -0,0 +1,170 @@
1
+ {
2
+ "actor": {
3
+ "type": "user",
4
+ "display_name": "Dana Developer",
5
+ "nickname": "dana",
6
+ "account_id": "557058:11111111-1111-4111-8111-111111111111",
7
+ "uuid": "{11111111-2222-4333-8444-555555555555}",
8
+ "links": {
9
+ "html": {
10
+ "href": "https://bitbucket.org/%7B11111111-2222-4333-8444-555555555555%7D/"
11
+ }
12
+ }
13
+ },
14
+ "repository": {
15
+ "type": "repository",
16
+ "full_name": "acme-ws/widgets",
17
+ "name": "widgets",
18
+ "is_private": true,
19
+ "scm": "git",
20
+ "uuid": "{0f9b2c4e-7a1d-4e5f-9c3b-2d8e6a1f4b70}",
21
+ "links": {
22
+ "html": {
23
+ "href": "https://bitbucket.org/acme-ws/widgets"
24
+ }
25
+ },
26
+ "workspace": {
27
+ "type": "workspace",
28
+ "slug": "acme-ws",
29
+ "name": "Acme",
30
+ "uuid": "{a1a1a1a1-b2b2-4c3c-8d4d-e5e5e5e5e5e5}"
31
+ },
32
+ "project": {
33
+ "type": "project",
34
+ "key": "WID",
35
+ "name": "Widgets"
36
+ }
37
+ },
38
+ "pullrequest": {
39
+ "type": "pullrequest",
40
+ "id": 42,
41
+ "title": "Add retry to the widget fetcher",
42
+ "description": "Adds exponential backoff.",
43
+ "state": "OPEN",
44
+ "author": {
45
+ "type": "user",
46
+ "display_name": "Dana Developer",
47
+ "nickname": "dana",
48
+ "account_id": "557058:11111111-1111-4111-8111-111111111111",
49
+ "uuid": "{11111111-2222-4333-8444-555555555555}",
50
+ "links": {
51
+ "html": {
52
+ "href": "https://bitbucket.org/%7B11111111-2222-4333-8444-555555555555%7D/"
53
+ }
54
+ }
55
+ },
56
+ "source": {
57
+ "branch": {
58
+ "name": "feature/retry"
59
+ },
60
+ "commit": {
61
+ "hash": "1a2b3c4d5e6f"
62
+ },
63
+ "repository": {
64
+ "type": "repository",
65
+ "full_name": "acme-ws/widgets",
66
+ "name": "widgets",
67
+ "is_private": true,
68
+ "scm": "git",
69
+ "uuid": "{0f9b2c4e-7a1d-4e5f-9c3b-2d8e6a1f4b70}",
70
+ "links": {
71
+ "html": {
72
+ "href": "https://bitbucket.org/acme-ws/widgets"
73
+ }
74
+ },
75
+ "workspace": {
76
+ "type": "workspace",
77
+ "slug": "acme-ws",
78
+ "name": "Acme",
79
+ "uuid": "{a1a1a1a1-b2b2-4c3c-8d4d-e5e5e5e5e5e5}"
80
+ },
81
+ "project": {
82
+ "type": "project",
83
+ "key": "WID",
84
+ "name": "Widgets"
85
+ }
86
+ }
87
+ },
88
+ "destination": {
89
+ "branch": {
90
+ "name": "main"
91
+ },
92
+ "commit": {
93
+ "hash": "6f5e4d3c2b1a"
94
+ },
95
+ "repository": {
96
+ "type": "repository",
97
+ "full_name": "acme-ws/widgets",
98
+ "name": "widgets",
99
+ "is_private": true,
100
+ "scm": "git",
101
+ "uuid": "{0f9b2c4e-7a1d-4e5f-9c3b-2d8e6a1f4b70}",
102
+ "links": {
103
+ "html": {
104
+ "href": "https://bitbucket.org/acme-ws/widgets"
105
+ }
106
+ },
107
+ "workspace": {
108
+ "type": "workspace",
109
+ "slug": "acme-ws",
110
+ "name": "Acme",
111
+ "uuid": "{a1a1a1a1-b2b2-4c3c-8d4d-e5e5e5e5e5e5}"
112
+ },
113
+ "project": {
114
+ "type": "project",
115
+ "key": "WID",
116
+ "name": "Widgets"
117
+ }
118
+ }
119
+ },
120
+ "merge_commit": null,
121
+ "participants": [],
122
+ "reviewers": [],
123
+ "close_source_branch": true,
124
+ "created_on": "2026-10-22T09:00:00.000000+00:00",
125
+ "updated_on": "2026-10-22T09:00:00.000000+00:00",
126
+ "links": {
127
+ "html": {
128
+ "href": "https://bitbucket.org/acme-ws/widgets/pull-requests/42"
129
+ }
130
+ }
131
+ },
132
+ "comment": {
133
+ "type": "pullrequest_comment",
134
+ "id": 9001,
135
+ "content": {
136
+ "type": "rendered",
137
+ "raw": "@{557058:00000000-0000-4000-8000-0000000000b0} can you fix the flaky test?",
138
+ "markup": "markdown",
139
+ "html": "<p>can you fix the flaky test?</p>"
140
+ },
141
+ "user": {
142
+ "type": "user",
143
+ "display_name": "Dana Developer",
144
+ "nickname": "dana",
145
+ "account_id": "557058:11111111-1111-4111-8111-111111111111",
146
+ "uuid": "{11111111-2222-4333-8444-555555555555}",
147
+ "links": {
148
+ "html": {
149
+ "href": "https://bitbucket.org/%7B11111111-2222-4333-8444-555555555555%7D/"
150
+ }
151
+ }
152
+ },
153
+ "deleted": false,
154
+ "pending": false,
155
+ "inline": null,
156
+ "parent": null,
157
+ "created_on": "2026-10-22T09:05:00.000000+00:00",
158
+ "updated_on": "2026-10-22T09:05:00.000000+00:00",
159
+ "links": {
160
+ "html": {
161
+ "href": "https://bitbucket.org/acme-ws/widgets/pull-requests/42/_/diff#comment-9001"
162
+ }
163
+ },
164
+ "pullrequest": {
165
+ "type": "pullrequest",
166
+ "id": 42,
167
+ "title": "Add retry to the widget fetcher"
168
+ }
169
+ }
170
+ }