@desplega.ai/agent-swarm 1.172.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-fpecx1g1.js → acp-adapter-sj2g0m84.js} +18 -17
  3. package/dist/{actions-pw87a1a8.js → actions-fegk44gr.js} +65 -35
  4. package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
  6. package/dist/{app-wmf0m843.js → app-kfvgbvzy.js} +19 -13
  7. package/dist/{assistant-emp1e108.js → assistant-z7zdy9ja.js} +65 -38
  8. package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-5c2z0mbe.js} +11 -9
  9. package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-j8xa181e.js} +11 -9
  10. package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-hnkn3rgt.js} +12 -10
  11. package/dist/{boot-seeds-8q637aew.js → boot-seeds-kwjq4d2z.js} +15 -13
  12. package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-9vmvpvgd.js} +20 -19
  13. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  14. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  15. package/dist/{claude-sdk-session-gsfsh662.js → claude-sdk-session-9jye6cxr.js} +12698 -11995
  16. package/dist/{cli-k46r9hmk.js → cli-03gedhng.js} +16 -14
  17. package/dist/{cli-kb4ehzx5.js → cli-1e4b6bzh.js} +7 -7
  18. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  19. package/dist/cli-2m1ktfzz.js +17795 -0
  20. package/dist/{cli-8vs9trd4.js → cli-3dj8k4vy.js} +1 -1
  21. package/dist/{cli-zxm9p3x4.js → cli-3fbmmawm.js} +5 -5
  22. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  23. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  24. package/dist/{cli-6jb2rf77.js → cli-5t4f29ez.js} +3 -3
  25. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  26. package/dist/cli-6cvfxs8r.js +47 -0
  27. package/dist/{cli-pj6x9qce.js → cli-6jyj59nb.js} +2 -2
  28. package/dist/{cli-388egyf6.js → cli-6razj6t4.js} +2 -2
  29. package/dist/{cli-qsztpfry.js → cli-74ahz61x.js} +2 -2
  30. package/dist/{cli-4nz8p1p5.js → cli-7x0g4sn1.js} +15474 -16082
  31. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  32. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  33. package/dist/cli-9traj3dn.js +638 -0
  34. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  35. package/dist/{cli-bf7485pm.js → cli-apx87jne.js} +2 -2
  36. package/dist/{cli-ztrv9y21.js → cli-bzszjf2p.js} +3689 -1081
  37. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  38. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  39. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  40. package/dist/{cli-1hgat6pa.js → cli-d5f9ed5c.js} +5 -5
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  43. package/dist/{cli-j6kpj9az.js → cli-ee9gtz70.js} +1553 -68
  44. package/dist/{cli-e0a9sk00.js → cli-eejsgvm4.js} +4 -4
  45. package/dist/{cli-tgpfnm6r.js → cli-ef1hg1wv.js} +518 -59
  46. package/dist/cli-f527ce74.js +781 -0
  47. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  48. package/dist/cli-hjwmhcg1.js +10622 -0
  49. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  50. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  51. package/dist/{cli-4fnb6884.js → cli-hvqbtna4.js} +7 -1259
  52. package/dist/{cli-pstq9888.js → cli-j0pfptdp.js} +220 -103
  53. package/dist/cli-jta2eyzc.js +619 -0
  54. package/dist/{cli-esnpehf5.js → cli-k382tshb.js} +12 -13
  55. package/dist/cli-k3dp5tpf.js +263 -0
  56. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  57. package/dist/cli-kceah1eh.js +92 -0
  58. package/dist/{cli-prrt9fa0.js → cli-ke896fcf.js} +10 -10
  59. package/dist/{cli-nv4zd0b9.js → cli-kt6zqg0e.js} +6 -6
  60. package/dist/{cli-g6t9texm.js → cli-m50t4309.js} +5 -18
  61. package/dist/cli-n2wq4j7m.js +6985 -0
  62. package/dist/{cli-c2pq2rjb.js → cli-n3hzg89b.js} +46 -15
  63. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  64. package/dist/{cli-5rv1j9ga.js → cli-ne236afy.js} +53 -7
  65. package/dist/cli-p0sg24zp.js +16 -0
  66. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  67. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  68. package/dist/{cli-g4f5pj26.js → cli-q0ke1yww.js} +111 -25
  69. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  70. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  71. package/dist/{cli-gj81e47t.js → cli-r2pnvj8g.js} +2 -2
  72. package/dist/{cli-r8smj8b8.js → cli-r62a35ea.js} +19 -3
  73. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  74. package/dist/cli-rhs9pz33.js +294 -0
  75. package/dist/{cli-nyhb1r6x.js → cli-s2sp957d.js} +52 -29
  76. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  77. package/dist/{cli-xczpdp9e.js → cli-se8w6wny.js} +6 -6
  78. package/dist/cli-skxvvpap.js +10140 -0
  79. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  80. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  81. package/dist/{cli-cf69s3ew.js → cli-tr4sxp68.js} +3 -3
  82. package/dist/{cli-8hzfb6xz.js → cli-tsde1hdr.js} +15 -15
  83. package/dist/{cli-cceskcvt.js → cli-txqb9s38.js} +8 -8
  84. package/dist/{cli-aspcaef3.js → cli-v1yb0rqb.js} +6 -6
  85. package/dist/{cli-j1pmc07z.js → cli-vatyhshy.js} +268 -40
  86. package/dist/{cli-04emfe2q.js → cli-vdach5a9.js} +5 -5
  87. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  88. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  89. package/dist/{cli-hwwvy36w.js → cli-wayw93nt.js} +3 -3
  90. package/dist/cli-wmgrkscj.js +23254 -0
  91. package/dist/{cli-gp1rtyvk.js → cli-z5jjpaxw.js} +29 -6
  92. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  93. package/dist/cli.js +32 -48
  94. package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-y81sssk0.js} +16 -15
  95. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  96. package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-8vx4j6a9.js} +16 -15
  97. package/dist/{commands-n0hshcvs.js → commands-kcpvpcrr.js} +21 -10
  98. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  99. package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-kwpvj3rf.js} +45 -38
  100. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  101. package/dist/{db-hkqvrdcb.js → db-k8ankj20.js} +13 -11
  102. package/dist/{db-4w7tmt1g.js → db-ww5d7jq3.js} +29 -9
  103. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  104. package/dist/{dispatcher-5sd69y14.js → dispatcher-e8gq5m68.js} +41 -34
  105. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  106. package/dist/{dsh-adapter-2h8m4cab.js → dsh-adapter-3epjm3af.js} +7 -6
  107. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  108. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  109. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  110. package/dist/external-channel-h3359cwn.js +52 -0
  111. package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-g8db7x0f.js} +5 -3
  112. package/dist/{google-vertex-3f1jk41f.js → google-vertex-r2692q0r.js} +5 -3
  113. package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-0d4x7gkx.js} +19 -18
  114. package/dist/{handlers-s20xr10r.js → handlers-an1gzv21.js} +44 -37
  115. package/dist/{heartbeat-3k31q4g2.js → heartbeat-6dakeb61.js} +46 -39
  116. package/dist/{hook-gdn6a2fn.js → hook-6v6cv6d5.js} +12 -24
  117. package/dist/{hook-ygwerrdh.js → hook-sa5xye4y.js} +13 -12
  118. package/dist/{http-jdteeg81.js → http-r7f3eky0.js} +1598 -279
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-e0snym4t.js → index-7ep4gzdk.js} +41 -34
  121. package/dist/index-9gw58n4k.js +193 -0
  122. package/dist/index-9j87mcm8.js +56305 -0
  123. package/dist/index-bsmcresk.js +79424 -0
  124. package/dist/index-mh85qhec.js +475 -0
  125. package/dist/{index-vm3bm9t7.js → index-rmpw9a8q.js} +41 -34
  126. package/dist/{index-6ksewd5y.js → index-s2wxgzr0.js} +1015 -732
  127. package/dist/{index-tsszt7g9.js → index-v1v6n7hg.js} +46 -39
  128. package/dist/index-we27kd31.js +12810 -0
  129. package/dist/{index-ffqzqgwb.js → index-zk96aq1k.js} +11 -9
  130. package/dist/{keepalive-jekc896t.js → keepalive-be9vwy5h.js} +13 -11
  131. package/dist/{lead-4k9s9szx.js → lead-0861cwrf.js} +64 -57
  132. package/dist/{maintenance-shazch4n.js → maintenance-mzzgc8bn.js} +11 -9
  133. package/dist/node-c9we95xf.js +1 -1
  134. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  135. package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-cpdnfwdj.js} +12 -10
  136. package/dist/{onboard-ydrbnvww.js → onboard-fh8qs4r3.js} +11 -3
  137. package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-nm42wwj8.js} +19 -14
  138. package/dist/{otel-impl-6pf5cv86.js → otel-impl-x6c1c9g4.js} +248 -869
  139. package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-9n2ya240.js} +245 -107
  140. package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-qb0rv0ap.js} +13 -11
  141. package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-1gzdwbh1.js} +12 -10
  142. package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-3mraey52.js} +13 -11
  143. package/dist/{render-v2-pd1rg54q.js → render-v2-agpa45zz.js} +19 -13
  144. package/dist/{resume-gw4a2c6v.js → resume-x0ydy9s9.js} +27 -21
  145. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  146. package/dist/{setup-jda1ksdh.js → setup-as9ga0d6.js} +2 -2
  147. package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-9gkxqrap.js} +7 -6
  148. package/dist/{work-objects-tb7ycye8.js → work-objects-y5g1y7ev.js} +49 -37
  149. package/dist/{worker-njjccz5d.js → worker-860c3e38.js} +64 -57
  150. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  151. package/openapi.json +951 -356
  152. package/package.json +12 -13
  153. package/src/apps/catalog.generated.json +1309 -53
  154. package/src/apps/page-validator.ts +93 -31
  155. package/src/azure-devops/api.ts +94 -18
  156. package/src/azure-devops/auth.ts +32 -0
  157. package/src/azure-devops/boards-sync.ts +708 -0
  158. package/src/azure-devops/handlers.ts +52 -8
  159. package/src/azure-devops/html.ts +26 -0
  160. package/src/azure-devops/index.ts +15 -0
  161. package/src/azure-devops/templates.ts +86 -0
  162. package/src/azure-devops/types.ts +65 -1
  163. package/src/be/config-refs.ts +60 -0
  164. package/src/be/db/page-versions.ts +15 -0
  165. package/src/be/db/runtime.ts +35 -8
  166. package/src/be/db/skill-name-resolution.ts +36 -0
  167. package/src/be/db/tasks/read.ts +230 -0
  168. package/src/be/db/tasks/write.ts +7 -0
  169. package/src/be/db.ts +74 -13
  170. package/src/be/migrations/213_script_connections_sql.sql +79 -0
  171. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  172. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  173. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  174. package/src/be/model-catalog-store.ts +5 -1
  175. package/src/be/model-tier-resolution.ts +12 -1
  176. package/src/be/model-validation.ts +5 -2
  177. package/src/be/models-catalog.ts +26 -1
  178. package/src/be/modelsdev-cache.json +198 -63
  179. package/src/be/modelsdev-cache.ts +9 -1
  180. package/src/be/script-connections.ts +47 -8
  181. package/src/be/script-credential-broker.ts +39 -2
  182. package/src/be/scripts/run-saved.ts +1 -0
  183. package/src/be/scripts/typecheck.ts +10 -8
  184. package/src/be/seed/runner.ts +13 -1
  185. package/src/be/seed/state-db.ts +5 -0
  186. package/src/be/seed/types.ts +6 -0
  187. package/src/be/seed-pricing.ts +19 -2
  188. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  189. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  190. package/src/be/seed-skills/bundled-files.generated.json +10 -18
  191. package/src/be/seed-skills/index.ts +107 -3
  192. package/src/be/sql-connections.ts +118 -0
  193. package/src/be/swarm-config-guard.ts +53 -0
  194. package/src/cli.tsx +0 -16
  195. package/src/commands/onboard/non-interactive.ts +4 -2
  196. package/src/commands/profile-sync.ts +5 -3
  197. package/src/commands/provider-credentials.ts +52 -6
  198. package/src/commands/runner.ts +196 -56
  199. package/src/e2b/env.ts +5 -0
  200. package/src/extensions/contract-types.generated.ts +1 -1
  201. package/src/extensions/contract.ts +45 -0
  202. package/src/extensions/dispatcher.ts +7 -0
  203. package/src/extensions/post-bridge.ts +40 -0
  204. package/src/github/handlers.ts +11 -4
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/all-routes.ts +0 -1
  208. package/src/http/config.ts +36 -17
  209. package/src/http/core.ts +4 -0
  210. package/src/http/index.ts +14 -2
  211. package/src/http/integrations.ts +88 -1
  212. package/src/http/mcp-oauth.ts +6 -13
  213. package/src/http/mcp.ts +15 -5
  214. package/src/http/page-proxy.ts +2 -4
  215. package/src/http/pages-public.ts +6 -5
  216. package/src/http/pages.ts +5 -14
  217. package/src/http/poll.ts +28 -68
  218. package/src/http/requested-by.ts +54 -0
  219. package/src/http/route-def.ts +7 -6
  220. package/src/http/script-connection-proxy.ts +51 -4
  221. package/src/http/script-connections.ts +5 -2
  222. package/src/http/scripts.ts +1 -0
  223. package/src/http/session-cost-recompute.ts +12 -2
  224. package/src/http/session-data.ts +4 -0
  225. package/src/http/stats.ts +1 -30
  226. package/src/http/tasks.ts +61 -5
  227. package/src/http/utils.ts +14 -0
  228. package/src/http/webhooks.ts +183 -2
  229. package/src/http/x.ts +1 -0
  230. package/src/integrations/chat-sdk/adapters.ts +129 -0
  231. package/src/integrations/chat-sdk/attachments.ts +232 -0
  232. package/src/integrations/chat-sdk/background.ts +36 -0
  233. package/src/integrations/chat-sdk/commands.ts +194 -0
  234. package/src/integrations/chat-sdk/config.ts +198 -0
  235. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  236. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  237. package/src/integrations/chat-sdk/handlers.ts +28 -0
  238. package/src/integrations/chat-sdk/inbound.ts +426 -0
  239. package/src/integrations/chat-sdk/limits.ts +48 -0
  240. package/src/integrations/chat-sdk/logger.ts +81 -0
  241. package/src/integrations/chat-sdk/outbound.ts +123 -0
  242. package/src/integrations/chat-sdk/relay.ts +172 -0
  243. package/src/integrations/chat-sdk/router.ts +169 -0
  244. package/src/integrations/chat-sdk/runtime.ts +351 -0
  245. package/src/integrations/chat-sdk/schema.ts +245 -0
  246. package/src/integrations/chat-sdk/state.ts +243 -0
  247. package/src/integrations/chat-sdk/status.ts +244 -0
  248. package/src/integrations/chat-sdk/steering.ts +58 -0
  249. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  250. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  251. package/src/integrations/chat-sdk/templates.ts +16 -0
  252. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  253. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  254. package/src/integrations/chat-sdk/typing.ts +65 -0
  255. package/src/jira/adf.ts +3 -1
  256. package/src/kv-reserved-namespaces.ts +2 -0
  257. package/src/oauth/mcp-wrapper.ts +10 -6
  258. package/src/otel-impl.ts +2 -1
  259. package/src/pages/version.ts +4 -5
  260. package/src/prompts/base-prompt.ts +111 -45
  261. package/src/providers/acp-target-catalog.ts +6 -0
  262. package/src/providers/acp-targets.ts +56 -3
  263. package/src/providers/amp-adapter.ts +1 -1
  264. package/src/providers/claude-adapter.ts +27 -2
  265. package/src/providers/claude-flag-settings.ts +133 -0
  266. package/src/providers/claude-managed-adapter.ts +1 -1
  267. package/src/providers/claude-sdk-session.ts +7 -0
  268. package/src/providers/claude-session-events.ts +6 -1
  269. package/src/providers/dsh-adapter.ts +1 -1
  270. package/src/providers/opencode-adapter.ts +268 -37
  271. package/src/providers/opencode-billing.ts +86 -0
  272. package/src/providers/opencode-swarm-plugin.ts +51 -0
  273. package/src/providers/pi-mono-adapter.ts +398 -153
  274. package/src/providers/pricing-sources.md +37 -3
  275. package/src/providers/reasoning-effort.ts +27 -0
  276. package/src/providers/types.ts +11 -2
  277. package/src/rbac/legacy-policy.ts +8 -2
  278. package/src/rbac/permissions.ts +21 -0
  279. package/src/scheduler/scheduler.ts +4 -2
  280. package/src/scripts-runtime/api-client.ts +15 -1
  281. package/src/scripts-runtime/api-types.ts +9 -1
  282. package/src/scripts-runtime/ctx.ts +1 -1
  283. package/src/scripts-runtime/loader.ts +19 -7
  284. package/src/scripts-runtime/mcp-client.ts +30 -19
  285. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  286. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  287. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  288. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  289. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  290. package/src/server.ts +52 -18
  291. package/src/slack/actions.ts +29 -1
  292. package/src/slack/app.ts +14 -2
  293. package/src/slack/assistant.ts +36 -2
  294. package/src/slack/channel-join.ts +3 -16
  295. package/src/slack/commands.ts +9 -1
  296. package/src/slack/event-dedup.ts +1 -1
  297. package/src/slack/external-channel-egress.ts +113 -0
  298. package/src/slack/external-channel.ts +309 -0
  299. package/src/slack/handlers.ts +61 -0
  300. package/src/slack/render-v2.ts +40 -47
  301. package/src/slack/work-objects.ts +5 -0
  302. package/src/tasks/additive-buffer.ts +4 -2
  303. package/src/tasks/context-key.ts +94 -2
  304. package/src/tasks/model-pin.ts +67 -0
  305. package/src/tasks/worker-follow-up.ts +4 -31
  306. package/src/telemetry.ts +2 -1
  307. package/src/tests/acp-adapter.test.ts +42 -0
  308. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  309. package/src/tests/apps-catalog-feedback.test.ts +310 -0
  310. package/src/tests/apps-catalog-inputs.test.ts +70 -0
  311. package/src/tests/apps-catalog-table.test.ts +113 -0
  312. package/src/tests/azure-devops-boards-sync.test.ts +584 -0
  313. package/src/tests/azure-devops-html.test.ts +13 -0
  314. package/src/tests/azure-devops-webhook-malformed.test.ts +4 -0
  315. package/src/tests/base-prompt.test.ts +58 -54
  316. package/src/tests/chat-sdk-commands.test.ts +191 -0
  317. package/src/tests/chat-sdk-config.test.ts +334 -0
  318. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  319. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  320. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  321. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  322. package/src/tests/chat-sdk-harness.ts +146 -0
  323. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  324. package/src/tests/chat-sdk-relay.test.ts +470 -0
  325. package/src/tests/chat-sdk-reload.test.ts +128 -0
  326. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  327. package/src/tests/chat-sdk-slack.test.ts +268 -0
  328. package/src/tests/chat-sdk-state.test.ts +163 -0
  329. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  330. package/src/tests/chat-sdk-teams.test.ts +277 -0
  331. package/src/tests/chat-sdk-tools.test.ts +520 -0
  332. package/src/tests/chat-sdk-typing.test.ts +120 -0
  333. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  334. package/src/tests/claude-adapter-binary.test.ts +144 -0
  335. package/src/tests/claude-flag-settings.test.ts +114 -0
  336. package/src/tests/claude-managed-adapter.test.ts +1 -1
  337. package/src/tests/claude-sdk-lifecycle.test.ts +148 -1
  338. package/src/tests/claude-sdk-process.fixture.ts +7 -0
  339. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  340. package/src/tests/claude-worker-parity.test.ts +2 -7
  341. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  342. package/src/tests/config-refs.test.ts +66 -0
  343. package/src/tests/context-key.test.ts +51 -0
  344. package/src/tests/cors-allowlist.test.ts +25 -0
  345. package/src/tests/create-page-tool.test.ts +18 -0
  346. package/src/tests/credential-check.test.ts +139 -0
  347. package/src/tests/credentials.test.ts +21 -0
  348. package/src/tests/db-runtime.test.ts +61 -0
  349. package/src/tests/db-task-read-contract.test.ts +20 -0
  350. package/src/tests/db-task-write-contract.test.ts +10 -0
  351. package/src/tests/deferred-task-wake.test.ts +52 -4
  352. package/src/tests/docs-index-footprint.test.js +3 -3
  353. package/src/tests/dsh-adapter.test.ts +1 -1
  354. package/src/tests/enabled-capabilities.test.ts +20 -0
  355. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  356. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  357. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  358. package/src/tests/explicit-model-validation.test.ts +105 -1
  359. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  360. package/src/tests/extensions-post-bridge.test.ts +51 -0
  361. package/src/tests/fetch-resolved-env.test.ts +128 -0
  362. package/src/tests/fixtures/azure-boards/workitem.commented.json +89 -0
  363. package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
  364. package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
  365. package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
  366. package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
  367. package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
  368. package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
  369. package/src/tests/fixtures/catalog-table-definition.json.txt +468 -0
  370. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  371. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  372. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  373. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  374. package/src/tests/fixtures/runner-identity-refresh.ts +3 -0
  375. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  376. package/src/tests/github-handlers.test.ts +35 -0
  377. package/src/tests/harness-provider-resolution.test.ts +12 -0
  378. package/src/tests/heartbeat-checklist.test.ts +3 -2
  379. package/src/tests/hook-runtime-close.test.ts +0 -2
  380. package/src/tests/http-api-integration.test.ts +0 -25
  381. package/src/tests/migration-046-budgets.test.ts +17 -16
  382. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  383. package/src/tests/model-tier-resolution.test.ts +93 -0
  384. package/src/tests/models-catalog.test.ts +42 -1
  385. package/src/tests/node-env-runtime.test.ts +98 -0
  386. package/src/tests/openai-compatible-providers.test.ts +134 -0
  387. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  388. package/src/tests/opencode-adapter.test.ts +348 -3
  389. package/src/tests/opencode-billing.test.ts +28 -0
  390. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  391. package/src/tests/opencode-shell-env.test.ts +145 -0
  392. package/src/tests/openrouter-base-url.test.ts +101 -151
  393. package/src/tests/page-proxy.test.ts +1 -1
  394. package/src/tests/pages-browser-sdk.test.ts +76 -0
  395. package/src/tests/pages-feedback.test.ts +1 -1
  396. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  397. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  398. package/src/tests/pi-mono-adapter.test.ts +385 -5
  399. package/src/tests/pi-shell-env.test.ts +170 -0
  400. package/src/tests/prompt-template-remaining.test.ts +35 -0
  401. package/src/tests/rbac-engine.test.ts +11 -3
  402. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  403. package/src/tests/runner-identity-refresh.test.ts +3 -0
  404. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  405. package/src/tests/script-connections-sql.test.ts +675 -0
  406. package/src/tests/script-connections.test.ts +7 -1
  407. package/src/tests/scripts-http.test.ts +201 -1
  408. package/src/tests/scripts-only-gating.test.ts +2 -3
  409. package/src/tests/scripts-runtime.test.ts +58 -40
  410. package/src/tests/secret-scrubber.test.ts +86 -0
  411. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  412. package/src/tests/seed-skills-retire.test.ts +182 -0
  413. package/src/tests/send-task-requester-gate.test.ts +58 -0
  414. package/src/tests/session-cost-recompute-unit.test.ts +94 -0
  415. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  416. package/src/tests/slack-channel-join.test.ts +4 -1
  417. package/src/tests/slack-external-channel-config.test.ts +65 -0
  418. package/src/tests/slack-external-channel-egress.test.ts +237 -0
  419. package/src/tests/slack-external-channel-guard.test.ts +280 -0
  420. package/src/tests/slack-external-channel-tools.test.ts +173 -0
  421. package/src/tests/slack-external-channel.test.ts +370 -0
  422. package/src/tests/slack-render-v2.test.ts +86 -10
  423. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  424. package/src/tests/spec-base-url.test.ts +16 -1
  425. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  426. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  427. package/src/tests/swarm-diff.test.ts +1 -1
  428. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  429. package/src/tests/task-session-prompts.test.ts +132 -0
  430. package/src/tests/task-tool-manifest.test.ts +14 -0
  431. package/src/tests/task-tools-ctx.test.ts +2 -0
  432. package/src/tests/tool-annotations.test.ts +6 -6
  433. package/src/tests/ui-logs-parser.test.ts +43 -0
  434. package/src/tests/used-by-route.test.ts +6 -0
  435. package/src/tests/vendored-openapi.test.ts +16 -1
  436. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  437. package/src/tests/workflow-swarm-script.test.ts +38 -0
  438. package/src/tests/workflow-wait-filter.test.ts +5 -3
  439. package/src/tools/chat-access.ts +41 -0
  440. package/src/tools/chat-open-dm.ts +59 -0
  441. package/src/tools/chat-post.ts +74 -0
  442. package/src/tools/chat-read-thread.ts +100 -0
  443. package/src/tools/chat-reply.ts +75 -0
  444. package/src/tools/create-page.ts +3 -13
  445. package/src/tools/defer-task.ts +17 -0
  446. package/src/tools/model-catalog/index.ts +2 -1
  447. package/src/tools/register-chat-channel.ts +139 -0
  448. package/src/tools/send-task.ts +24 -0
  449. package/src/tools/skills/skill-get.ts +17 -9
  450. package/src/tools/slack-post.ts +6 -0
  451. package/src/tools/slack-read.ts +8 -0
  452. package/src/tools/slack-reply.ts +6 -0
  453. package/src/tools/slack-start-thread.ts +6 -0
  454. package/src/tools/slack-update.ts +6 -0
  455. package/src/tools/slack-upload-file.ts +6 -0
  456. package/src/tools/swarm-config/get-config.ts +2 -1
  457. package/src/tools/swarm-config/list-config.ts +2 -1
  458. package/src/tools/swarm-config/set-config.ts +16 -3
  459. package/src/tools/tool-config.ts +8 -6
  460. package/src/types.ts +12 -2
  461. package/src/utils/config-env-export.ts +74 -0
  462. package/src/utils/credentials.test.ts +92 -0
  463. package/src/utils/credentials.ts +52 -4
  464. package/src/utils/harness-provider.ts +15 -6
  465. package/src/utils/node-env.ts +24 -0
  466. package/src/utils/runtime-model-catalog.ts +11 -1
  467. package/src/utils/secret-scrubber.ts +71 -2
  468. package/src/utils/task-tool-manifest.ts +19 -2
  469. package/src/workflows/executors/swarm-script.ts +1 -0
  470. package/templates/skills/agent-browser/SKILL.md +1 -1
  471. package/templates/skills/agent-browser/content.md +1 -1
  472. package/templates/skills/apps/SKILL.md +749 -0
  473. package/templates/skills/apps/content.md +60 -22
  474. package/templates/skills/chat-sdk/config.json +14 -0
  475. package/templates/skills/chat-sdk/content.md +84 -0
  476. package/templates/skills/composio-gmail/content.md +7 -0
  477. package/templates/skills/composio-google-docs/content.md +1 -1
  478. package/templates/skills/email-domain-health/config.json +14 -0
  479. package/templates/skills/email-domain-health/content.md +131 -0
  480. package/templates/skills/kv-storage/content.md +2 -2
  481. package/templates/skills/pages/content.md +8 -10
  482. package/templates/skills/qa-loom/config.json +14 -0
  483. package/templates/skills/qa-loom/content.md +187 -0
  484. package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
  485. package/templates/skills/qa-loom/files/run-qa.sh +57 -0
  486. package/templates/skills/slack-interaction/content.md +4 -0
  487. package/templates/skills/swarm-extensions/content.md +2 -0
  488. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  489. package/templates/skills/swarm-scripts/config.json +1 -1
  490. package/templates/skills/swarm-scripts/content.md +4 -2
  491. package/templates/skills/workflow-iterate/content.md +38 -0
  492. package/dist/artifact-eczsknc3.js +0 -3843
  493. package/dist/cli-9xsmdpdt.js +0 -56377
  494. package/dist/cli-bd957dgb.js +0 -153
  495. package/dist/cli-q51yprfq.js +0 -1104
  496. package/dist/cli-t1ze64h3.js +0 -811
  497. package/dist/index-9ch1n761.js +0 -1102
  498. package/src/artifact-sdk/index.ts +0 -2
  499. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  500. package/src/artifact-sdk/port.ts +0 -12
  501. package/src/artifact-sdk/server.ts +0 -183
  502. package/src/artifact-sdk/tunnel.ts +0 -19
  503. package/src/commands/artifact.ts +0 -249
  504. package/src/http/ecosystem.ts +0 -77
  505. package/src/tests/artifact-commands.test.ts +0 -92
  506. package/src/tests/artifact-sdk.test.ts +0 -793
  507. package/src/tests/register-service-security.test.ts +0 -194
  508. package/src/tools/list-services.ts +0 -107
  509. package/src/tools/register-service.ts +0 -177
  510. package/src/tools/unregister-service.ts +0 -73
  511. package/src/tools/update-service-status.ts +0 -96
  512. package/templates/skills/artifacts/config.json +0 -14
  513. package/templates/skills/artifacts/content.md +0 -344
  514. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  515. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  516. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  517. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  518. package/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
  519. package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
  520. package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
  521. package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
  522. package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
  523. package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
  524. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  525. /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
@@ -10,7 +10,12 @@
10
10
 
11
11
  import { findTaskByVcs, getAllAgents, incrKv, upsertKv } from "../be/db";
12
12
  import { renderIdentity, resolveIdentity } from "../be/identity";
13
- import { findOrCreateUserByEmail, findUserByExternalId, linkIdentity } from "../be/users";
13
+ import {
14
+ findOrCreateUserByEmail,
15
+ findUserByEmail,
16
+ findUserByExternalId,
17
+ linkIdentity,
18
+ } from "../be/users";
14
19
  import { resolveTemplate } from "../prompts/resolver";
15
20
  import { azureDevOpsContextKey } from "../tasks/context-key";
16
21
  import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
@@ -36,7 +41,7 @@ type HandlerResult = { created: boolean; taskId?: string };
36
41
  const processedEvents = new Map<string, number>();
37
42
  const DEDUP_TTL_MS = 60_000;
38
43
 
39
- function isDuplicate(key: string): boolean {
44
+ export function isDuplicate(key: string): boolean {
40
45
  const now = Date.now();
41
46
  // Cleanup expired
42
47
  for (const [k, ts] of processedEvents) {
@@ -49,7 +54,7 @@ function isDuplicate(key: string): boolean {
49
54
 
50
55
  // ── Helpers ──
51
56
 
52
- function escapeRegExp(value: string): string {
57
+ export function escapeRegExp(value: string): string {
53
58
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
54
59
  }
55
60
 
@@ -143,13 +148,15 @@ export function commentedPayloadOf(
143
148
  return { comment, pullRequest };
144
149
  }
145
150
 
146
- function warnMalformed(event: { eventType?: string; resourceVersion?: string } | null | undefined) {
151
+ export function warnMalformed(
152
+ event: { eventType?: string; resourceVersion?: string } | null | undefined,
153
+ ) {
147
154
  console.warn(
148
155
  `[AzureDevOps] Ignoring ${event?.eventType} event with resourceVersion ${event?.resourceVersion ?? "unknown"}: required resource fields are missing`,
149
156
  );
150
157
  }
151
158
 
152
- async function findLeadAgent() {
159
+ export async function findLeadAgent() {
153
160
  const agents = await getAllAgents();
154
161
  return (
155
162
  agents.find((a) => a.role === "lead" && a.status === "idle") ??
@@ -173,7 +180,7 @@ function externalIdOf(identity: AzureDevOpsIdentityRef): string {
173
180
  * linked identity (`uniqueName`) → email when `uniqueName` is one → unmapped
174
181
  * tracker entry for operator triage.
175
182
  */
176
- async function resolveAzureDevOpsSender(
183
+ export async function resolveAzureDevOpsSender(
177
184
  identity: AzureDevOpsIdentityRef,
178
185
  sampleEventType: string,
179
186
  sampleContext: string,
@@ -192,6 +199,15 @@ async function resolveAzureDevOpsSender(
192
199
  return linked.id;
193
200
  }
194
201
 
202
+ await recordUnmappedIdentity(externalId, sampleEventType, sampleContext);
203
+ return undefined;
204
+ }
205
+
206
+ async function recordUnmappedIdentity(
207
+ externalId: string,
208
+ sampleEventType: string,
209
+ sampleContext: string,
210
+ ): Promise<void> {
195
211
  await upsertKv({
196
212
  namespace: UNMAPPED_NAMESPACE,
197
213
  key: `${externalId}:meta`,
@@ -204,14 +220,42 @@ async function resolveAzureDevOpsSender(
204
220
  expiresAt: Date.now() + UNMAPPED_TTL_MS,
205
221
  });
206
222
  await incrKv(UNMAPPED_NAMESPACE, `${externalId}:count`, 1);
207
- return undefined;
223
+ }
224
+
225
+ /**
226
+ * Authorize an actor to trigger a task: a swarm user already linked to the
227
+ * identity (or owning its sign-in email), or a sign-in name listed in
228
+ * AZURE_DEVOPS_ALLOWED_ACTORS (comma-separated). Unlike
229
+ * `resolveAzureDevOpsSender` this never creates users for strangers. Returns
230
+ * the `users.id`, or null after recording the actor for operator triage.
231
+ */
232
+ export async function authorizeAzureDevOpsActor(
233
+ identity: AzureDevOpsIdentityRef,
234
+ sampleEventType: string,
235
+ sampleContext: string,
236
+ ): Promise<string | null> {
237
+ const externalId = externalIdOf(identity);
238
+ const known =
239
+ (await findUserByExternalId("azure-devops", externalId)) ??
240
+ (externalId.includes("@") ? await findUserByEmail(externalId) : null);
241
+ if (known) return known.id;
242
+
243
+ const allowed = (process.env.AZURE_DEVOPS_ALLOWED_ACTORS ?? "")
244
+ .split(",")
245
+ .map((name) => name.trim().toLowerCase())
246
+ .filter(Boolean);
247
+ if (allowed.includes(externalId.toLowerCase())) {
248
+ return (await resolveAzureDevOpsSender(identity, sampleEventType, sampleContext)) ?? null;
249
+ }
250
+ await recordUnmappedIdentity(externalId, sampleEventType, sampleContext);
251
+ return null;
208
252
  }
209
253
 
210
254
  /**
211
255
  * Render an Azure DevOps identity for agent-visible text: the resolved
212
256
  * canonical name or the explicit UNKNOWN sentinel.
213
257
  */
214
- async function renderAzureDevOpsIdentity(identity: AzureDevOpsIdentityRef): Promise<string> {
258
+ export async function renderAzureDevOpsIdentity(identity: AzureDevOpsIdentityRef): Promise<string> {
215
259
  return renderIdentity(await resolveIdentity("azure-devops", externalIdOf(identity)));
216
260
  }
217
261
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Minimal HTML helpers for Azure Boards text. Work-item descriptions and
3
+ * comments are HTML; prompts want plain text.
4
+ */
5
+
6
+ const ENTITIES: Record<string, string> = {
7
+ "&amp;": "&",
8
+ "&lt;": "<",
9
+ "&gt;": ">",
10
+ "&quot;": '"',
11
+ "&#39;": "'",
12
+ "&nbsp;": " ",
13
+ };
14
+
15
+ /** Strip tags, decode the common entities, and collapse whitespace. */
16
+ export function htmlToText(html: string | null | undefined): string {
17
+ if (!html) return "";
18
+ return html
19
+ .replace(/<br\s*\/?>|<\/(?:div|p|li|h[1-6])>/gi, "\n")
20
+ .replace(/<[^>]*>/g, "")
21
+ .replace(/&(?:amp|lt|gt|quot|#39|nbsp);/g, (entity) => ENTITIES[entity] ?? entity)
22
+ .replace(/[ \t]+/g, " ")
23
+ .replace(/ ?\n ?/g, "\n")
24
+ .replace(/\n{3,}/g, "\n\n")
25
+ .trim();
26
+ }
@@ -4,6 +4,7 @@ export {
4
4
  postAzureDevOpsPullRequestComment,
5
5
  resetAzureDevOpsBotIdCache,
6
6
  resolveAzureDevOpsBotId,
7
+ resolveAzureDevOpsBotUniqueName,
7
8
  } from "./api";
8
9
  export {
9
10
  AZURE_DEVOPS_BOT_NAME,
@@ -14,6 +15,15 @@ export {
14
15
  resetAzureDevOps,
15
16
  verifyAzureDevOpsWebhook,
16
17
  } from "./auth";
18
+ export {
19
+ handleWorkItemCommented,
20
+ handleWorkItemCreated,
21
+ handleWorkItemDeleted,
22
+ handleWorkItemRestored,
23
+ handleWorkItemUpdated,
24
+ updatedPayloadOf,
25
+ workItemOf,
26
+ } from "./boards-sync";
17
27
  export {
18
28
  commentedPayloadOf,
19
29
  createdPullRequestOf,
@@ -24,4 +34,9 @@ export type {
24
34
  AzureDevOpsWebhookEvent,
25
35
  PullRequestCommentedEvent,
26
36
  PullRequestCreatedEvent,
37
+ WorkItemCommentedEvent,
38
+ WorkItemCreatedEvent,
39
+ WorkItemDeletedEvent,
40
+ WorkItemRestoredEvent,
41
+ WorkItemUpdatedEvent,
27
42
  } from "./types";
@@ -106,3 +106,89 @@ Comment:
106
106
  ],
107
107
  category: "event",
108
108
  });
109
+
110
+ // ============================================================================
111
+ // Azure Boards work-item events
112
+ // ============================================================================
113
+
114
+ registerTemplate({
115
+ eventType: "common.command_suggestions.azure_devops_work_item",
116
+ header: "",
117
+ defaultBody:
118
+ 'Workers have the `az` CLI with the azure-devops extension, authenticated with the swarm PAT. Inspect the work item with `az boards work-item show --id {{work_item_id}}`. To reply on it, run `az boards work-item update --id {{work_item_id}} --discussion "<reply>"`.',
119
+ variables: [{ name: "work_item_id", description: "Work item ID" }],
120
+ category: "common",
121
+ });
122
+
123
+ const WORK_ITEM_VARIABLES = [
124
+ { name: "work_item_id", description: "Work item ID" },
125
+ { name: "work_item_type", description: "Work item type (Task, Bug, Issue, ...)" },
126
+ { name: "title", description: "Work item title" },
127
+ { name: "state", description: "Work item state" },
128
+ { name: "project", description: "Azure DevOps project name" },
129
+ { name: "work_item_url", description: "Work item web URL" },
130
+ {
131
+ name: "username",
132
+ description:
133
+ "Actor identity — resolved canonical name (e.g. 'Luis (azure-devops:luis@example.com)') or the UNKNOWN sentinel",
134
+ },
135
+ ];
136
+
137
+ registerTemplate({
138
+ eventType: "azure-devops.work_item.assigned",
139
+ header: "[Azure DevOps {{work_item_type}} #{{work_item_id}}] {{title}}",
140
+ defaultBody: `Project: {{project}}
141
+ State: {{state}}
142
+ Triggered by: {{username}} ({{trigger}})
143
+ URL: {{work_item_url}}
144
+
145
+ {{description_section}}{{@template[common.command_suggestions.azure_devops_work_item]}}{{@template[common.delegation_instruction.azure_devops]}}`,
146
+ variables: [
147
+ ...WORK_ITEM_VARIABLES,
148
+ { name: "trigger", description: "Why the task was created (assigned to the bot, mentioned)" },
149
+ {
150
+ name: "description_section",
151
+ description: "Description section (HTML stripped) or empty string",
152
+ },
153
+ ],
154
+ category: "event",
155
+ });
156
+
157
+ registerTemplate({
158
+ eventType: "azure-devops.work_item.commented",
159
+ header: "[Azure DevOps {{work_item_type}} #{{work_item_id}}] {{username}} mentioned bot",
160
+ defaultBody: `Project: {{project}}
161
+ Title: {{title}}
162
+ State: {{state}}
163
+ URL: {{work_item_url}}
164
+
165
+ Comment:
166
+ {{context}}{{existing_task_note}}
167
+
168
+ {{@template[common.command_suggestions.azure_devops_work_item]}}{{@template[common.delegation_instruction.azure_devops]}}`,
169
+ variables: [
170
+ ...WORK_ITEM_VARIABLES,
171
+ { name: "context", description: "Comment text with the bot mention removed" },
172
+ { name: "existing_task_note", description: "Note about an active task, or empty string" },
173
+ ],
174
+ category: "event",
175
+ });
176
+
177
+ registerTemplate({
178
+ eventType: "azure-devops.work_item.followup",
179
+ header: "[Azure DevOps {{work_item_type}} #{{work_item_id}}] Follow-up: {{title}}",
180
+ defaultBody: `Project: {{project}}
181
+ State: {{state}}
182
+ Trigger: {{trigger}} by {{username}}
183
+ URL: {{work_item_url}}
184
+
185
+ {{user_message}}
186
+
187
+ {{@template[common.command_suggestions.azure_devops_work_item]}}{{@template[common.delegation_instruction.azure_devops]}}`,
188
+ variables: [
189
+ ...WORK_ITEM_VARIABLES,
190
+ { name: "trigger", description: "What re-engaged the swarm" },
191
+ { name: "user_message", description: "Comment text, or empty string" },
192
+ ],
193
+ category: "event",
194
+ });
@@ -87,4 +87,68 @@ export type PullRequestCommentedEvent = ServiceHookEvent<
87
87
  { comment: AzureDevOpsComment; pullRequest: AzureDevOpsPullRequest }
88
88
  >;
89
89
 
90
- export type AzureDevOpsWebhookEvent = PullRequestCreatedEvent | PullRequestCommentedEvent;
90
+ /**
91
+ * Azure Boards work-item fields (`resource.fields` on created / commented /
92
+ * deleted / restored, `resource.revision.fields` on updated). Identities are
93
+ * strings of the form `"Display Name <unique@name>"`, not identity objects.
94
+ */
95
+ export interface AzureDevOpsWorkItemFields {
96
+ "System.TeamProject"?: string;
97
+ "System.WorkItemType"?: string;
98
+ "System.State"?: string;
99
+ "System.Title"?: string;
100
+ "System.Description"?: string; // HTML
101
+ "System.AssignedTo"?: string;
102
+ "System.ChangedBy"?: string;
103
+ "System.History"?: string; // HTML of the latest comment (commented events only)
104
+ [field: string]: unknown;
105
+ }
106
+
107
+ /** `resource` of created / commented / deleted / restored events. */
108
+ export interface AzureDevOpsWorkItem {
109
+ id: number;
110
+ rev: number;
111
+ fields: AzureDevOpsWorkItemFields;
112
+ url?: string;
113
+ /** Present on `workitem.commented`: identifies the comment and its edit version. */
114
+ commentVersionRef?: { commentId: number; version: number };
115
+ }
116
+
117
+ /**
118
+ * `resource` of `workitem.updated`. `id` is the update id; the work item is
119
+ * `workItemId`. `fields` holds only the changed fields; the full state is in
120
+ * `revision.fields`.
121
+ */
122
+ export interface AzureDevOpsWorkItemUpdate {
123
+ id: number;
124
+ workItemId: number;
125
+ rev: number;
126
+ url?: string;
127
+ revisedBy: AzureDevOpsIdentityRef;
128
+ fields?: Record<string, { oldValue?: unknown; newValue?: unknown }>;
129
+ revision: { id: number; rev: number; fields: AzureDevOpsWorkItemFields };
130
+ }
131
+
132
+ /** `workitem.created` */
133
+ export type WorkItemCreatedEvent = ServiceHookEvent<"workitem.created", AzureDevOpsWorkItem>;
134
+
135
+ /** `workitem.updated` */
136
+ export type WorkItemUpdatedEvent = ServiceHookEvent<"workitem.updated", AzureDevOpsWorkItemUpdate>;
137
+
138
+ /** `workitem.commented`: `commentVersionRef.commentId` identifies the comment. */
139
+ export type WorkItemCommentedEvent = ServiceHookEvent<"workitem.commented", AzureDevOpsWorkItem>;
140
+
141
+ /** `workitem.deleted`: `resource.url` points at the recycle bin. */
142
+ export type WorkItemDeletedEvent = ServiceHookEvent<"workitem.deleted", AzureDevOpsWorkItem>;
143
+
144
+ /** `workitem.restored` */
145
+ export type WorkItemRestoredEvent = ServiceHookEvent<"workitem.restored", AzureDevOpsWorkItem>;
146
+
147
+ export type AzureDevOpsWebhookEvent =
148
+ | PullRequestCreatedEvent
149
+ | PullRequestCommentedEvent
150
+ | WorkItemCreatedEvent
151
+ | WorkItemUpdatedEvent
152
+ | WorkItemCommentedEvent
153
+ | WorkItemDeletedEvent
154
+ | WorkItemRestoredEvent;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Config references: `{ "$config": "KEY" }` leaves inside a JSON config value
3
+ * that point at another config key (usually a secret). Pure, no DB access: the
4
+ * caller supplies the lookup and decides which keys are allowed.
5
+ */
6
+
7
+ export interface ConfigRef {
8
+ $config: string;
9
+ }
10
+
11
+ /** True when `value` is exactly `{ "$config": "<non-empty string>" }`. */
12
+ export function isConfigRef(value: unknown): value is ConfigRef {
13
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
14
+ const keys = Object.keys(value);
15
+ const ref = (value as { $config?: unknown }).$config;
16
+ return keys.length === 1 && keys[0] === "$config" && typeof ref === "string" && ref.length > 0;
17
+ }
18
+
19
+ export class ConfigRefError extends Error {
20
+ constructor(
21
+ message: string,
22
+ readonly missingKeys: string[],
23
+ ) {
24
+ super(message);
25
+ this.name = "ConfigRefError";
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Replace every `{ "$config": "KEY" }` leaf in `value` with `lookup(KEY)`.
31
+ * Walks arrays and plain objects. Missing keys (lookup returns undefined) are
32
+ * collected and reported in one error. A lookup that throws (for example on a
33
+ * forbidden key) propagates as-is.
34
+ */
35
+ export function resolveConfigRefs<T>(
36
+ value: unknown,
37
+ lookup: (key: string) => string | undefined,
38
+ ): T {
39
+ const missing = new Set<string>();
40
+
41
+ const walk = (node: unknown): unknown => {
42
+ if (isConfigRef(node)) {
43
+ const resolved = lookup(node.$config);
44
+ if (resolved === undefined) missing.add(node.$config);
45
+ return resolved;
46
+ }
47
+ if (Array.isArray(node)) return node.map(walk);
48
+ if (node !== null && typeof node === "object") {
49
+ return Object.fromEntries(Object.entries(node).map(([k, v]) => [k, walk(v)]));
50
+ }
51
+ return node;
52
+ };
53
+
54
+ const result = walk(value);
55
+ if (missing.size > 0) {
56
+ const keys = [...missing].sort();
57
+ throw new ConfigRefError(`Unresolved config reference(s): ${keys.join(", ")}`, keys);
58
+ }
59
+ return result as T;
60
+ }
@@ -0,0 +1,15 @@
1
+ import { getDbClient } from "./runtime";
2
+
3
+ /**
4
+ * The page's edit counter, `MAX(page_versions.version) + 1`, or 1 before the
5
+ * first snapshot. Reads one indexed aggregate instead of every snapshot body:
6
+ * loading all versions of a large, often-edited page blocked the event loop
7
+ * for seconds per call.
8
+ */
9
+ export async function getPageEditCounter(pageId: string): Promise<number> {
10
+ const row = await getDbClient().get<{ latest: number | null }>(
11
+ "SELECT MAX(version) AS latest FROM page_versions WHERE pageId = ?",
12
+ [pageId],
13
+ );
14
+ return (row?.latest ?? 0) + 1;
15
+ }
@@ -104,7 +104,34 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
104
104
  db = new Database(dbPath, { create: true });
105
105
  console.log(`Database initialized at ${dbPath}`);
106
106
 
107
- const database = db;
107
+ // Boot must finish before getDb() hands this handle out. A step below can
108
+ // throw SQLITE_BUSY while another connection holds the file, and the
109
+ // db-client retries BUSY by calling getDb() again. With `db` still assigned,
110
+ // that retry got a connection whose encryption key was never resolved, and
111
+ // the server died on "Encryption key not resolved yet". Drop the handle so
112
+ // the retry runs boot again.
113
+ const opened = db;
114
+ try {
115
+ return bootOpenedDb(opened, dbPath, resolvePromptTemplate, autoEncryptLegacyPlaintextSecrets);
116
+ } catch (error) {
117
+ if (db === opened) {
118
+ db = null;
119
+ try {
120
+ opened.close();
121
+ } catch {
122
+ // Already closed by the step that failed.
123
+ }
124
+ }
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ function bootOpenedDb(
130
+ database: Database,
131
+ dbPath: string,
132
+ resolvePromptTemplate: typeof import("../db").resolvePromptTemplate,
133
+ autoEncryptLegacyPlaintextSecrets: typeof import("../db").autoEncryptLegacyPlaintextSecrets,
134
+ ): Database {
108
135
  database.run("PRAGMA journal_mode = WAL;");
109
136
  database.run("PRAGMA busy_timeout = 5000;");
110
137
  database.run("PRAGMA foreign_keys = ON;");
@@ -140,7 +167,7 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
140
167
  // Old databases have CHECK(status IN ('pending','in_progress','completed','failed'))
141
168
  // which blocks 'cancelled', 'paused', 'offered', 'unassigned' statuses
142
169
  try {
143
- const taskSchemaInfo = db
170
+ const taskSchemaInfo = database
144
171
  .prepare<{ sql: string | null }, []>(
145
172
  "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'agent_tasks'",
146
173
  )
@@ -153,7 +180,7 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
153
180
 
154
181
  if (needsStatusMigration) {
155
182
  console.log("[Migration] Removing restrictive CHECK constraint on agent_tasks.status");
156
- db.run("PRAGMA foreign_keys=off");
183
+ database.run("PRAGMA foreign_keys=off");
157
184
 
158
185
  // Migrations run before this compatibility guard, so a legacy table now
159
186
  // carries every current column. Rebuilding from an old hard-coded column
@@ -169,12 +196,12 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
169
196
  if (rebuiltSchemaSql === schemaSql || !/agent_tasks_new/i.test(rebuiltSchemaSql)) {
170
197
  throw new Error("Could not derive agent_tasks schema without legacy status CHECK");
171
198
  }
172
- const columns = db
199
+ const columns = database
173
200
  .prepare<{ name: string }, []>('PRAGMA table_info("agent_tasks")')
174
201
  .all()
175
202
  .map((column) => `"${column.name.replaceAll('"', '""')}"`)
176
203
  .join(", ");
177
- const schemaObjects = db
204
+ const schemaObjects = database
178
205
  .prepare<{ sql: string }, []>(
179
206
  `SELECT sql FROM sqlite_master
180
207
  WHERE tbl_name = 'agent_tasks'
@@ -194,13 +221,13 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
194
221
  for (const sql of schemaObjects) database.run(sql);
195
222
  })();
196
223
 
197
- db.run("PRAGMA foreign_keys=on");
224
+ database.run("PRAGMA foreign_keys=on");
198
225
  console.log("[Migration] Successfully removed CHECK constraint on agent_tasks.status");
199
226
  }
200
227
  } catch (e) {
201
228
  console.error("[Migration] Failed to update agent_tasks CHECK constraint:", e);
202
229
  try {
203
- db.run("PRAGMA foreign_keys=on");
230
+ database.run("PRAGMA foreign_keys=on");
204
231
  } catch (cleanupError) {
205
232
  console.error("[Migration] Failed to re-enable SQLite foreign_keys pragma:", cleanupError);
206
233
  }
@@ -291,7 +318,7 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
291
318
  throw err;
292
319
  }
293
320
 
294
- return db;
321
+ return database;
295
322
  }
296
323
 
297
324
  export function getDb(path?: string): Database {
@@ -0,0 +1,36 @@
1
+ import { getDbClient } from "./runtime";
2
+
3
+ export type SwarmSkillNameResolution =
4
+ | { kind: "found"; skillId: string }
5
+ | { kind: "ambiguous"; skillIds: string[] }
6
+ | { kind: "none" };
7
+
8
+ /**
9
+ * Resolve a swarm-scope skill name for `skill-get`.
10
+ *
11
+ * Migration 019 makes a name unique only per scope + owner, so a swarm name can
12
+ * match one ownerless row plus any number of agent-owned rows. Precedence:
13
+ * the ownerless row, then the caller's own row, then a single owned row.
14
+ * Several foreign-owned rows are ambiguous; the caller must pass a skillId.
15
+ *
16
+ * `getSkillByName(name, "swarm")` stays ownerless-only: the seeder relies on it.
17
+ */
18
+ export async function resolveSwarmSkillByName(
19
+ name: string,
20
+ callerAgentId?: string,
21
+ ): Promise<SwarmSkillNameResolution> {
22
+ const rows = await getDbClient().query<{ id: string; ownerAgentId: string | null }>(
23
+ "SELECT id, ownerAgentId FROM skills WHERE name = ? AND scope = 'swarm' ORDER BY createdAt, id",
24
+ [name],
25
+ );
26
+
27
+ const ownerless = rows.find((row) => !row.ownerAgentId);
28
+ if (ownerless) return { kind: "found", skillId: ownerless.id };
29
+
30
+ const own = callerAgentId ? rows.find((row) => row.ownerAgentId === callerAgentId) : undefined;
31
+ if (own) return { kind: "found", skillId: own.id };
32
+
33
+ if (rows.length === 1) return { kind: "found", skillId: rows[0]!.id };
34
+ if (rows.length > 1) return { kind: "ambiguous", skillIds: rows.map((row) => row.id) };
35
+ return { kind: "none" };
36
+ }