@desplega.ai/agent-swarm 1.173.0 → 2.1.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 (511) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
  7. package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
  8. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
  15. package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
  16. package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
  17. package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
  18. package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
  19. package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
  20. package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
  21. package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
  22. package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
  23. package/dist/cli-2m1ktfzz.js +17795 -0
  24. package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
  25. package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
  26. package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
  27. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  28. package/dist/cli-6cvfxs8r.js +47 -0
  29. package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
  30. package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
  31. package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
  32. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  33. package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
  34. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  35. package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
  36. package/dist/cli-9traj3dn.js +638 -0
  37. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  38. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  39. package/dist/cli-cvv9k0hh.js +10143 -0
  40. package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/cli-f527ce74.js +781 -0
  43. package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
  44. package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
  45. package/dist/cli-hjwmhcg1.js +10622 -0
  46. package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
  47. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  48. package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
  49. package/dist/cli-jta2eyzc.js +619 -0
  50. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  51. package/dist/cli-kceah1eh.js +92 -0
  52. package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
  53. package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
  54. package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
  55. package/dist/cli-n2wq4j7m.js +6985 -0
  56. package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
  57. package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
  58. package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
  59. package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
  60. package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
  61. package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
  62. package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
  63. package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
  64. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  65. package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
  66. package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
  67. package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
  68. package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
  69. package/dist/cli-rhs9pz33.js +294 -0
  70. package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
  71. package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
  72. package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
  73. package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
  74. package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
  75. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  76. package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
  77. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  78. package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
  79. package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
  80. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  81. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  82. package/dist/cli-wfnemr1t.js +86 -0
  83. package/dist/cli-wmgrkscj.js +23254 -0
  84. package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
  85. package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
  86. package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
  87. package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
  88. package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
  89. package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
  90. package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
  91. package/dist/cli.js +34 -49
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
  99. package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
  100. package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-s9ncp9vy.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
  117. package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
  118. package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
  121. package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
  122. package/dist/index-9gw58n4k.js +193 -0
  123. package/dist/index-9j87mcm8.js +56305 -0
  124. package/dist/index-bsmcresk.js +79424 -0
  125. package/dist/index-mh85qhec.js +475 -0
  126. package/dist/{index-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
  127. package/dist/index-we27kd31.js +12810 -0
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
  148. package/openapi.json +1279 -329
  149. package/package.json +15 -15
  150. package/src/agentmail/handlers.ts +21 -11
  151. package/src/apps/definition.ts +16 -0
  152. package/src/azure-devops/api.ts +52 -2
  153. package/src/azure-devops/boards-sync.ts +50 -20
  154. package/src/azure-devops/index.ts +1 -0
  155. package/src/azure-devops/outbound.ts +95 -0
  156. package/src/be/boot-seeds.ts +11 -0
  157. package/src/be/config-refs.ts +60 -0
  158. package/src/be/db/model-profiles.ts +315 -0
  159. package/src/be/db/page-versions.ts +15 -0
  160. package/src/be/db/runtime.ts +35 -8
  161. package/src/be/db/skill-name-resolution.ts +36 -0
  162. package/src/be/db/tasks/read.ts +237 -0
  163. package/src/be/db/tasks/write.ts +7 -0
  164. package/src/be/db.ts +87 -36
  165. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  166. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  167. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  168. package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
  169. package/src/be/migrations/220_model_profiles.sql +137 -0
  170. package/src/be/model-catalog-store.ts +5 -1
  171. package/src/be/model-tier-resolution.ts +155 -10
  172. package/src/be/model-validation.ts +12 -2
  173. package/src/be/models-catalog.ts +26 -1
  174. package/src/be/modelsdev-cache.json +198 -63
  175. package/src/be/modelsdev-cache.ts +9 -1
  176. package/src/be/script-credential-broker.ts +39 -2
  177. package/src/be/scripts/run-saved.ts +1 -0
  178. package/src/be/scripts/typecheck.ts +22 -8
  179. package/src/be/secret-registry.ts +6 -2
  180. package/src/be/seed/runner.ts +13 -1
  181. package/src/be/seed/state-db.ts +5 -0
  182. package/src/be/seed/types.ts +6 -0
  183. package/src/be/seed-model-profiles.ts +41 -0
  184. package/src/be/seed-pricing.ts +19 -2
  185. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
  186. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  187. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  188. package/src/be/seed-scripts/index.ts +9 -0
  189. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  190. package/src/be/seed-skills/index.ts +104 -3
  191. package/src/be/swarm-config-guard.ts +53 -0
  192. package/src/cli.tsx +0 -16
  193. package/src/commands/onboard/non-interactive.ts +4 -2
  194. package/src/commands/provider-credentials.ts +52 -6
  195. package/src/commands/runner.ts +193 -71
  196. package/src/e2b/env.ts +5 -0
  197. package/src/extensions/catalog.generated.json +17 -0
  198. package/src/extensions/contract-types.generated.ts +1 -1
  199. package/src/extensions/contract.ts +45 -0
  200. package/src/extensions/dispatcher.ts +7 -0
  201. package/src/extensions/loader.ts +10 -5
  202. package/src/extensions/post-bridge.ts +40 -0
  203. package/src/github/handlers.ts +11 -4
  204. package/src/heartbeat/heartbeat.ts +2 -1
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/agents.ts +49 -2
  208. package/src/http/all-routes.ts +1 -1
  209. package/src/http/apps.ts +30 -3
  210. package/src/http/config.ts +62 -19
  211. package/src/http/core.ts +8 -0
  212. package/src/http/index.ts +25 -3
  213. package/src/http/integrations.ts +88 -1
  214. package/src/http/mcp.ts +15 -5
  215. package/src/http/model-profiles.ts +303 -0
  216. package/src/http/page-proxy.ts +2 -4
  217. package/src/http/pages-public.ts +13 -7
  218. package/src/http/pages.ts +3 -13
  219. package/src/http/poll.ts +42 -71
  220. package/src/http/requested-by.ts +54 -0
  221. package/src/http/script-runs.ts +1 -1
  222. package/src/http/scripts.ts +26 -2
  223. package/src/http/stats.ts +1 -30
  224. package/src/http/tasks.ts +79 -5
  225. package/src/http/utils.ts +14 -0
  226. package/src/http/webhooks.ts +160 -3
  227. package/src/http/x.ts +1 -0
  228. package/src/integrations/chat-sdk/adapters.ts +129 -0
  229. package/src/integrations/chat-sdk/attachments.ts +232 -0
  230. package/src/integrations/chat-sdk/background.ts +36 -0
  231. package/src/integrations/chat-sdk/commands.ts +194 -0
  232. package/src/integrations/chat-sdk/config.ts +198 -0
  233. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  234. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  235. package/src/integrations/chat-sdk/handlers.ts +28 -0
  236. package/src/integrations/chat-sdk/inbound.ts +426 -0
  237. package/src/integrations/chat-sdk/limits.ts +48 -0
  238. package/src/integrations/chat-sdk/logger.ts +81 -0
  239. package/src/integrations/chat-sdk/outbound.ts +123 -0
  240. package/src/integrations/chat-sdk/relay.ts +172 -0
  241. package/src/integrations/chat-sdk/router.ts +169 -0
  242. package/src/integrations/chat-sdk/runtime.ts +351 -0
  243. package/src/integrations/chat-sdk/schema.ts +245 -0
  244. package/src/integrations/chat-sdk/state.ts +243 -0
  245. package/src/integrations/chat-sdk/status.ts +244 -0
  246. package/src/integrations/chat-sdk/steering.ts +58 -0
  247. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  248. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  249. package/src/integrations/chat-sdk/templates.ts +16 -0
  250. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  251. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  252. package/src/integrations/chat-sdk/typing.ts +65 -0
  253. package/src/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
  254. package/src/pages/version.ts +4 -5
  255. package/src/prompts/base-prompt.ts +111 -45
  256. package/src/providers/acp-target-catalog.ts +6 -0
  257. package/src/providers/acp-targets.ts +56 -3
  258. package/src/providers/amp-adapter.ts +1 -1
  259. package/src/providers/claude-adapter.ts +1 -1
  260. package/src/providers/claude-flag-settings.ts +17 -5
  261. package/src/providers/claude-managed-adapter.ts +1 -1
  262. package/src/providers/claude-session-events.ts +6 -1
  263. package/src/providers/dsh-adapter.ts +1 -1
  264. package/src/providers/opencode-adapter.ts +243 -26
  265. package/src/providers/opencode-swarm-plugin.ts +31 -0
  266. package/src/providers/pi-mono-adapter.ts +398 -153
  267. package/src/providers/pi-mono-extension.ts +17 -3
  268. package/src/providers/pricing-sources.md +33 -2
  269. package/src/providers/reasoning-effort.ts +37 -0
  270. package/src/providers/types.ts +6 -2
  271. package/src/rbac/legacy-policy.ts +16 -2
  272. package/src/rbac/permissions.ts +26 -0
  273. package/src/scheduler/deferred-task-waits.ts +18 -5
  274. package/src/scheduler/scheduler.ts +11 -3
  275. package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
  276. package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
  277. package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
  278. package/src/scripts-runtime/extract-args-schema.ts +9 -8
  279. package/src/scripts-runtime/loader.ts +19 -7
  280. package/src/scripts-runtime/quickjs-compat.ts +464 -0
  281. package/src/scripts-runtime/sdk-allowlist.ts +12 -6
  282. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  283. package/src/scripts-runtime/types/stdlib.d.ts +39 -15
  284. package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
  285. package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
  286. package/src/server.ts +62 -19
  287. package/src/slack/blocks.ts +4 -2
  288. package/src/slack/event-dedup.ts +1 -1
  289. package/src/slack/reaction-shortcode.ts +14 -0
  290. package/src/slack/render-v2.ts +12 -7
  291. package/src/slack/responses.ts +5 -4
  292. package/src/slack/thread-buffer.ts +41 -22
  293. package/src/tasks/additive-buffer.ts +4 -2
  294. package/src/tasks/context-key.ts +60 -1
  295. package/src/tasks/model-pin.ts +67 -0
  296. package/src/tasks/worker-follow-up.ts +15 -33
  297. package/src/tests/acp-adapter.test.ts +42 -0
  298. package/src/tests/agentmail-filters.test.ts +47 -0
  299. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  300. package/src/tests/apps-spike2.test.ts +35 -0
  301. package/src/tests/azure-devops-outbound.test.ts +420 -0
  302. package/src/tests/base-prompt.test.ts +58 -54
  303. package/src/tests/chat-sdk-commands.test.ts +191 -0
  304. package/src/tests/chat-sdk-config.test.ts +334 -0
  305. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  306. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  307. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  308. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  309. package/src/tests/chat-sdk-harness.ts +146 -0
  310. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  311. package/src/tests/chat-sdk-relay.test.ts +470 -0
  312. package/src/tests/chat-sdk-reload.test.ts +128 -0
  313. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  314. package/src/tests/chat-sdk-slack.test.ts +268 -0
  315. package/src/tests/chat-sdk-state.test.ts +163 -0
  316. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  317. package/src/tests/chat-sdk-teams.test.ts +277 -0
  318. package/src/tests/chat-sdk-tools.test.ts +520 -0
  319. package/src/tests/chat-sdk-typing.test.ts +120 -0
  320. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  321. package/src/tests/claude-adapter-binary.test.ts +9 -4
  322. package/src/tests/claude-flag-settings.test.ts +11 -3
  323. package/src/tests/claude-managed-adapter.test.ts +1 -1
  324. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  325. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  326. package/src/tests/claude-worker-parity.test.ts +2 -7
  327. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  328. package/src/tests/config-refs.test.ts +66 -0
  329. package/src/tests/context-key.test.ts +19 -0
  330. package/src/tests/cors-allowlist.test.ts +25 -0
  331. package/src/tests/create-page-tool.test.ts +18 -0
  332. package/src/tests/credential-check.test.ts +139 -0
  333. package/src/tests/credentials.test.ts +21 -0
  334. package/src/tests/db-runtime.test.ts +61 -0
  335. package/src/tests/db-task-read-contract.test.ts +22 -0
  336. package/src/tests/db-task-write-contract.test.ts +11 -0
  337. package/src/tests/deferred-task-wake.test.ts +78 -1
  338. package/src/tests/docs-index-footprint.test.js +3 -3
  339. package/src/tests/dsh-adapter.test.ts +1 -1
  340. package/src/tests/enabled-capabilities.test.ts +20 -0
  341. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  342. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  343. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  344. package/src/tests/env-flag.test.ts +1 -0
  345. package/src/tests/explicit-model-validation.test.ts +105 -1
  346. package/src/tests/extensions-catalog.test.ts +1 -0
  347. package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
  348. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  349. package/src/tests/extensions-loader.test.ts +15 -0
  350. package/src/tests/extensions-post-bridge.test.ts +51 -0
  351. package/src/tests/fetch-resolved-env.test.ts +128 -0
  352. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  353. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  354. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  355. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  356. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  357. package/src/tests/github-handlers.test.ts +35 -0
  358. package/src/tests/harness-provider-resolution.test.ts +12 -0
  359. package/src/tests/heartbeat-checklist.test.ts +3 -2
  360. package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
  361. package/src/tests/heartbeat-jev-gate.test.ts +385 -0
  362. package/src/tests/hook-runtime-close.test.ts +0 -2
  363. package/src/tests/http-api-integration.test.ts +0 -25
  364. package/src/tests/migration-046-budgets.test.ts +17 -16
  365. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  366. package/src/tests/model-profile-backcompat.test.ts +327 -0
  367. package/src/tests/model-profile-providers.test.ts +302 -0
  368. package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
  369. package/src/tests/model-profiles-api.test.ts +614 -0
  370. package/src/tests/model-tier-resolution.test.ts +93 -0
  371. package/src/tests/models-catalog.test.ts +42 -1
  372. package/src/tests/openai-compatible-providers.test.ts +134 -0
  373. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  374. package/src/tests/opencode-adapter.test.ts +216 -2
  375. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  376. package/src/tests/opencode-shell-env.test.ts +145 -0
  377. package/src/tests/openrouter-base-url.test.ts +101 -151
  378. package/src/tests/page-proxy.test.ts +1 -1
  379. package/src/tests/pages-browser-sdk.test.ts +76 -0
  380. package/src/tests/pages-feedback-keys.test.ts +121 -0
  381. package/src/tests/pages-feedback.test.ts +4 -1
  382. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  383. package/src/tests/pi-context-compaction.test.ts +193 -0
  384. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  385. package/src/tests/pi-mono-adapter.test.ts +385 -5
  386. package/src/tests/pi-shell-env.test.ts +170 -0
  387. package/src/tests/profile-claim.test.ts +269 -0
  388. package/src/tests/prompt-template-remaining.test.ts +35 -0
  389. package/src/tests/quickjs-compat.test.ts +213 -0
  390. package/src/tests/rbac-engine.test.ts +32 -3
  391. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  392. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  393. package/src/tests/script-connections.test.ts +6 -0
  394. package/src/tests/script-executor-conformance.test.ts +252 -1
  395. package/src/tests/script-executor-quickjs.test.ts +96 -0
  396. package/src/tests/script-runs-http.test.ts +89 -0
  397. package/src/tests/scripts-http.test.ts +217 -1
  398. package/src/tests/scripts-only-gating.test.ts +2 -3
  399. package/src/tests/scripts-runtime.test.ts +58 -40
  400. package/src/tests/secret-scrubber.test.ts +96 -0
  401. package/src/tests/seed-model-profiles.test.ts +174 -0
  402. package/src/tests/seed-scripts.test.ts +2 -2
  403. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  404. package/src/tests/seed-skills-retire.test.ts +182 -0
  405. package/src/tests/send-task-requester-gate.test.ts +58 -0
  406. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  407. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  408. package/src/tests/skill-invocations.test.ts +4 -2
  409. package/src/tests/slack-blocks.test.ts +36 -0
  410. package/src/tests/slack-inline-output.test.ts +14 -0
  411. package/src/tests/slack-reaction-config.test.ts +20 -0
  412. package/src/tests/slack-render-v2.test.ts +37 -0
  413. package/src/tests/slack-thread-buffer.test.ts +98 -1
  414. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  415. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  416. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  417. package/src/tests/swarm-diff.test.ts +1 -1
  418. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  419. package/src/tests/task-session-prompts.test.ts +132 -0
  420. package/src/tests/task-tool-manifest.test.ts +14 -0
  421. package/src/tests/task-tools-ctx.test.ts +2 -0
  422. package/src/tests/tool-annotations.test.ts +6 -6
  423. package/src/tests/ui-logs-parser.test.ts +43 -0
  424. package/src/tests/used-by-route.test.ts +6 -0
  425. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  426. package/src/tests/workflow-llm.test.ts +61 -0
  427. package/src/tests/workflow-swarm-script.test.ts +38 -0
  428. package/src/tests/workflow-wait-filter.test.ts +5 -3
  429. package/src/tools/chat-access.ts +41 -0
  430. package/src/tools/chat-open-dm.ts +59 -0
  431. package/src/tools/chat-post.ts +74 -0
  432. package/src/tools/chat-read-thread.ts +100 -0
  433. package/src/tools/chat-reply.ts +75 -0
  434. package/src/tools/create-page.ts +3 -13
  435. package/src/tools/model-catalog/index.ts +2 -1
  436. package/src/tools/model-profiles/list-model-profiles.ts +54 -0
  437. package/src/tools/register-chat-channel.ts +139 -0
  438. package/src/tools/request-human-input.ts +11 -3
  439. package/src/tools/script-apis.ts +5 -1
  440. package/src/tools/script-upsert.ts +4 -1
  441. package/src/tools/send-task.ts +48 -1
  442. package/src/tools/skills/skill-get.ts +17 -9
  443. package/src/tools/swarm-config/get-config.ts +2 -1
  444. package/src/tools/swarm-config/list-config.ts +2 -1
  445. package/src/tools/swarm-config/set-config.ts +16 -3
  446. package/src/tools/system-one-decide.ts +104 -0
  447. package/src/tools/task-action.ts +22 -1
  448. package/src/tools/tool-config.ts +14 -6
  449. package/src/types.ts +48 -3
  450. package/src/utils/config-env-export.ts +74 -0
  451. package/src/utils/credentials.test.ts +92 -0
  452. package/src/utils/credentials.ts +52 -4
  453. package/src/utils/harness-provider.ts +15 -6
  454. package/src/utils/runtime-model-catalog.ts +11 -1
  455. package/src/utils/secret-scrubber.ts +86 -10
  456. package/src/utils/task-tool-manifest.ts +19 -2
  457. package/src/utils/vcs-repo-match.ts +29 -0
  458. package/src/workflows/executors/raw-llm.ts +8 -3
  459. package/src/workflows/executors/swarm-script.ts +1 -0
  460. package/src/workflows/executors/system-one-decision.ts +21 -11
  461. package/src/workflows/executors/workflow-llm.ts +22 -0
  462. package/src/workflows/index.ts +21 -12
  463. package/templates/extensions/README.md +1 -0
  464. package/templates/extensions/comms-adherence-gate/README.md +88 -0
  465. package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
  466. package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
  467. package/templates/skills/agent-browser/SKILL.md +1 -1
  468. package/templates/skills/agent-browser/content.md +1 -1
  469. package/templates/skills/apps/SKILL.md +8 -1
  470. package/templates/skills/apps/content.md +8 -1
  471. package/templates/skills/chat-sdk/config.json +14 -0
  472. package/templates/skills/chat-sdk/content.md +84 -0
  473. package/templates/skills/composio-gmail/content.md +7 -0
  474. package/templates/skills/composio-google-docs/content.md +1 -1
  475. package/templates/skills/email-domain-health/config.json +14 -0
  476. package/templates/skills/email-domain-health/content.md +131 -0
  477. package/templates/skills/kv-storage/content.md +2 -2
  478. package/templates/skills/pages/content.md +8 -10
  479. package/templates/skills/swarm-extensions/content.md +2 -0
  480. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  481. package/templates/skills/swarm-scripts/config.json +1 -1
  482. package/templates/skills/swarm-scripts/content.md +4 -2
  483. package/templates/skills/workflow-iterate/content.md +38 -0
  484. package/dist/artifact-eczsknc3.js +0 -3843
  485. package/dist/cli-42qhyvwx.js +0 -33
  486. package/dist/cli-bd957dgb.js +0 -153
  487. package/dist/cli-q51yprfq.js +0 -1104
  488. package/dist/cli-t1ze64h3.js +0 -811
  489. package/dist/cli-tcc4wd5d.js +0 -56471
  490. package/dist/index-9ch1n761.js +0 -1102
  491. package/src/artifact-sdk/index.ts +0 -2
  492. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  493. package/src/artifact-sdk/port.ts +0 -12
  494. package/src/artifact-sdk/server.ts +0 -183
  495. package/src/artifact-sdk/tunnel.ts +0 -19
  496. package/src/commands/artifact.ts +0 -249
  497. package/src/http/ecosystem.ts +0 -77
  498. package/src/tests/artifact-commands.test.ts +0 -92
  499. package/src/tests/artifact-sdk.test.ts +0 -793
  500. package/src/tests/register-service-security.test.ts +0 -194
  501. package/src/tools/list-services.ts +0 -107
  502. package/src/tools/register-service.ts +0 -177
  503. package/src/tools/unregister-service.ts +0 -73
  504. package/src/tools/update-service-status.ts +0 -96
  505. package/templates/skills/artifacts/config.json +0 -14
  506. package/templates/skills/artifacts/content.md +0 -344
  507. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  508. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  509. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  510. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  511. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.173.0",
3
+ "version": "2.1.0",
4
4
  "description": "agent-swarm.dev — an open-source operating system for AI work: a lead agent breaks goals into tasks, routes them to specialized workers such as Claude Code or Codex, runs each worker in an isolated container, and preserves shared memory, tools, schedules, and review gates so delegated work compounds across sessions.",
5
5
  "keywords": [
6
6
  "agent-swarm.dev",
@@ -168,11 +168,6 @@
168
168
  "docs:mcp": "bun scripts/generate-mcp-docs.ts",
169
169
  "docs:openapi": "bun scripts/generate-openapi.ts",
170
170
  "docs:business-use": "bun scripts/generate-business-use-docs.ts",
171
- "pm2-start": "pm2 start ecosystem.config.cjs",
172
- "pm2-stop": "pm2 stop ecosystem.config.cjs",
173
- "pm2-restart": "pm2 restart ecosystem.config.cjs",
174
- "pm2-logs": "pm2 logs swarm-api swarm-ui swarm-lead swarm-worker",
175
- "pm2-status": "pm2 status swarm-api swarm-ui swarm-lead swarm-worker",
176
171
  "seed": "bun scripts/seed.ts",
177
172
  "seed:clean": "bun scripts/seed.ts --clean",
178
173
  "seed:people": "bun scripts/seed-people.ts",
@@ -180,16 +175,17 @@
180
175
  },
181
176
  "devDependencies": {
182
177
  "@biomejs/biome": "^2.3.10",
183
- "@desplega.ai/slack-mock": "^0.4.0",
178
+ "@desplega.ai/slack-mock": "^0.4.1",
184
179
  "@desplega/model-catalog": "workspace:*",
185
180
  "@desplega/model-routing": "workspace:*",
186
181
  "@faker-js/faker": "^10.4.0",
187
- "@opencode/plugin": "2.0.29",
188
- "@opencode/sdk": "2.0.29",
182
+ "@opencode/plugin": "2.0.31",
183
+ "@opencode/sdk": "2.0.31",
189
184
  "@oxlint/plugins": "1.78.0",
190
185
  "@types/bun": "latest",
191
186
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
192
187
  "dependency-cruiser": "^18.0.0",
188
+ "happy-dom": "^20.14.5",
193
189
  "konsistent": "1.0.0-beta.1",
194
190
  "openapi-typescript": "^7",
195
191
  "oxlint": "1.78.0",
@@ -203,15 +199,18 @@
203
199
  "bun": ">=1.3.12"
204
200
  },
205
201
  "dependencies": {
206
- "@agentclientprotocol/sdk": "1.8.0",
202
+ "@agentclientprotocol/sdk": "1.9.0",
207
203
  "@ai-sdk/openai": "^3.0.41",
208
- "@anthropic-ai/claude-agent-sdk": "0.3.299",
209
- "@anthropic-ai/sdk": "^0.133.0",
204
+ "@anthropic-ai/claude-agent-sdk": "0.3.301",
205
+ "@anthropic-ai/sdk": "^0.135.0",
210
206
  "@asteasolutions/zod-to-openapi": "^8.0.0",
211
207
  "@aws-sdk/client-bedrock": "3.1048.0",
208
+ "@chat-adapter/discord": "4.41.1",
209
+ "@chat-adapter/slack": "4.41.1",
210
+ "@chat-adapter/teams": "4.41.1",
211
+ "@chat-adapter/telegram": "4.41.1",
212
212
  "@cursor/sdk": "1.0.37",
213
213
  "@desplega.ai/business-use": "^0.4.2",
214
- "@desplega.ai/localtunnel": "^2.2.0",
215
214
  "@earendil-works/pi-agent-core": "^1.2.0",
216
215
  "@earendil-works/pi-ai": "^1.2.0",
217
216
  "@earendil-works/pi-coding-agent": "^1.2.0",
@@ -219,7 +218,7 @@
219
218
  "@jitl/quickjs-singlefile-mjs-release-sync": "0.32.0",
220
219
  "@linear/sdk": "^77.0.0",
221
220
  "@modelcontextprotocol/sdk": "1.31.0",
222
- "@openai/codex": "0.162.1",
221
+ "@openai/codex": "0.163.0",
223
222
  "@openfort/openfort-node": "^0.9.1",
224
223
  "@opentelemetry/api": "^1.9.1",
225
224
  "@opentelemetry/api-logs": "^0.218.0",
@@ -230,15 +229,16 @@
230
229
  "@opentelemetry/sdk-node": "^0.218.0",
231
230
  "@opentelemetry/semantic-conventions": "^1.41.1",
232
231
  "@slack/bolt": "^4.6.0",
232
+ "@slack/web-api": "7.13.0",
233
233
  "@types/react": "^19.2.7",
234
234
  "@x402/core": "^2.5.0",
235
235
  "@x402/evm": "^2.5.0",
236
236
  "@x402/fetch": "^2.5.0",
237
237
  "ai": "^6.0.116",
238
+ "chat": "4.41.1",
238
239
  "cron-parser": "^5.4.0",
239
240
  "date-fns": "^4.1.0",
240
241
  "e2b": "2.26.0",
241
- "hono": "^4.12.3",
242
242
  "ink": "^6.5.1",
243
243
  "oauth4webapi": "^3.8.5",
244
244
  "openai": "^6.22.0",
@@ -91,32 +91,42 @@ function extractNameFromField(from: string): string | undefined {
91
91
  }
92
92
 
93
93
  /**
94
- * Check if an inbox domain is allowed by the filter.
95
- * Returns true if no filter is set or the inbox domain matches.
94
+ * Check an email address against comma-separated filter entries.
95
+ * An entry containing "@" matches that exact address; a bare entry matches any
96
+ * address on that domain. Comparison is case-insensitive.
97
+ */
98
+ function matchesAddressFilter(email: string, filter: string): boolean {
99
+ const address = email.trim().toLowerCase();
100
+ const domain = address.split("@")[1];
101
+ if (!domain) return false;
102
+ return filter
103
+ .split(",")
104
+ .map((entry) => entry.trim().toLowerCase())
105
+ .some((entry) => (entry.includes("@") ? entry === address : entry === domain));
106
+ }
107
+
108
+ /**
109
+ * Check if an inbox address is allowed by the filter (full addresses or domains).
110
+ * Returns true if no filter is set or the inbox matches an entry.
96
111
  */
97
112
  export function isInboxAllowed(inboxId: string, filter: string | undefined): boolean {
98
113
  if (!filter) return true;
99
- const allowedDomains = filter.split(",").map((d) => d.trim().toLowerCase());
100
- const inboxDomain = inboxId.split("@")[1]?.toLowerCase();
101
- return !!inboxDomain && allowedDomains.includes(inboxDomain);
114
+ return matchesAddressFilter(inboxId, filter);
102
115
  }
103
116
 
104
117
  /**
105
- * Check if a sender domain is allowed by the filter.
106
- * Returns true if no filter is set or at least one sender domain matches.
118
+ * Check if a sender address is allowed by the filter (full addresses or domains).
119
+ * Returns true if no filter is set or at least one sender matches an entry.
107
120
  */
108
121
  export function isSenderAllowed(
109
122
  from: AgentMailMessage["from_"],
110
123
  filter: string | undefined,
111
124
  ): boolean {
112
125
  if (!filter) return true;
113
- const allowedDomains = filter.split(",").map((d) => d.trim().toLowerCase());
114
126
  const fromAddresses = Array.isArray(from) ? from : [from || ""];
115
127
  return fromAddresses.some((addr) => {
116
128
  const emailMatch = addr.match(/<([^>]+)>/);
117
- const email = emailMatch?.[1] ?? addr;
118
- const domain = email.split("@")[1]?.toLowerCase();
119
- return !!domain && allowedDomains.includes(domain);
129
+ return matchesAddressFilter(emailMatch?.[1] ?? addr, filter);
120
130
  });
121
131
  }
122
132
 
@@ -2,6 +2,7 @@ import * as z from "zod";
2
2
  import { listScriptConnections } from "../be/script-connections";
3
3
  import { getScriptById } from "../be/scripts/db";
4
4
  import { getSavedScriptOwnerAgentId } from "../be/scripts/run-saved";
5
+ import { checkQuickJSCompat, quickJSCompatWarnings } from "../scripts-runtime/quickjs-compat";
5
6
  import catalog from "./catalog.generated.json";
6
7
  import {
7
8
  crossPageDefinitionIssues,
@@ -592,6 +593,21 @@ export function collectScriptReferences(definition: unknown): Map<string, string
592
593
  return references;
593
594
  }
594
595
 
596
+ /**
597
+ * Advisory quickjs compatibility warnings for every script that the
598
+ * definition's actions and sync sources run. Never a validation issue.
599
+ */
600
+ export async function scriptCompatWarnings(definition: unknown): Promise<string[]> {
601
+ const warnings: string[] = [];
602
+ for (const [scriptId, paths] of collectScriptReferences(definition)) {
603
+ const script = await getScriptById(scriptId);
604
+ if (!script) continue;
605
+ const report = checkQuickJSCompat(script.source, { fsMode: script.fsMode });
606
+ warnings.push(...quickJSCompatWarnings(report, paths.join(", ")));
607
+ }
608
+ return warnings;
609
+ }
610
+
595
611
  /**
596
612
  * Is this agent-scoped script foreign to the writing principal? Operators
597
613
  * (no writer identity at all) may wire anything; an agent may wire its own;
@@ -3,6 +3,7 @@
3
3
  * and resolving the bot identity behind the PAT. Uses raw fetch, no SDK.
4
4
  */
5
5
 
6
+ import { scrubSecrets } from "../utils/secret-scrubber";
6
7
  import {
7
8
  getAzureDevOpsOrgUrl,
8
9
  getAzureDevOpsToken,
@@ -163,7 +164,9 @@ export async function likeAzureDevOpsComment(
163
164
  try {
164
165
  const resp = await fetch(url, { method: "POST", headers: headers() });
165
166
  if (!resp.ok) {
166
- console.error(`[AzureDevOps] Failed to like comment: ${resp.status} ${await resp.text()}`);
167
+ console.error(
168
+ `[AzureDevOps] Failed to like comment: ${resp.status} ${scrubSecrets(await resp.text())}`,
169
+ );
167
170
  }
168
171
  } catch (err) {
169
172
  console.error(`[AzureDevOps] Error liking comment:`, err);
@@ -189,9 +192,56 @@ export async function postAzureDevOpsPullRequestComment(
189
192
  }),
190
193
  });
191
194
  if (!resp.ok) {
192
- console.error(`[AzureDevOps] Failed to post comment: ${resp.status} ${await resp.text()}`);
195
+ console.error(
196
+ `[AzureDevOps] Failed to post comment: ${resp.status} ${scrubSecrets(await resp.text())}`,
197
+ );
193
198
  }
194
199
  } catch (err) {
195
200
  console.error(`[AzureDevOps] Error posting comment:`, err);
196
201
  }
197
202
  }
203
+
204
+ /** One JSON Patch operation on a work item. */
205
+ export interface WorkItemFieldOp {
206
+ op: "add" | "replace";
207
+ path: string; // e.g. "/fields/System.State"
208
+ value: unknown;
209
+ }
210
+
211
+ /**
212
+ * Update work-item fields with one JSON Patch call. Returns whether it
213
+ * succeeded; failures are logged, never thrown, since callers are
214
+ * fire-and-forget lifecycle mirrors. The project segment is optional here.
215
+ * @see https://learn.microsoft.com/rest/api/azure/devops/wit/work-items/update
216
+ */
217
+ export async function updateWorkItemFields(
218
+ orgBaseUrl: string,
219
+ workItemId: number,
220
+ ops: WorkItemFieldOp[],
221
+ ): Promise<boolean> {
222
+ const orgUrl = credentialedOrgUrl(orgBaseUrl);
223
+ if (!orgUrl) return false;
224
+ try {
225
+ const resp = await fetch(
226
+ `${orgUrl}/_apis/wit/workitems/${workItemId}?api-version=${API_VERSION}`,
227
+ {
228
+ method: "PATCH",
229
+ headers: { ...headers(), "Content-Type": "application/json-patch+json" },
230
+ body: JSON.stringify(ops),
231
+ redirect: "error",
232
+ },
233
+ );
234
+ if (!resp.ok) {
235
+ console.error(
236
+ `[AzureDevOps] Failed to update work item #${workItemId}: ${resp.status} ${scrubSecrets(await resp.text())}`,
237
+ );
238
+ }
239
+ return resp.ok;
240
+ } catch (err) {
241
+ console.error(
242
+ `[AzureDevOps] Error updating work item #${workItemId}:`,
243
+ scrubSecrets(err instanceof Error ? err.message : String(err)),
244
+ );
245
+ return false;
246
+ }
247
+ }
@@ -37,6 +37,7 @@ import { azureDevOpsWorkItemContextKey } from "../tasks/context-key";
37
37
  import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
38
38
  import type { TrackerSync } from "../tracker/types";
39
39
  import { isTerminalTaskStatus } from "../types";
40
+ import { workflowEventBus } from "../workflows/event-bus";
40
41
  import { resolveAzureDevOpsBotIdentity } from "./api";
41
42
  import {
42
43
  AZURE_DEVOPS_BOT_NAME,
@@ -561,7 +562,7 @@ export async function handleWorkItemUpdated(event: WorkItemUpdatedEvent): Promis
561
562
  const org = orgFor(event);
562
563
  if (!org) return dropUntrustedOrg(event.eventType);
563
564
 
564
- return await handleDelivery(org, event.id, update.workItemId, async () => {
565
+ const routeAssignment = async (): Promise<HandlerResult> => {
565
566
  const bot = await resolveBot(org.url);
566
567
  if (!isBotString(change.newValue, bot) || isBotString(change.oldValue, bot)) {
567
568
  return { created: false };
@@ -579,6 +580,21 @@ export async function handleWorkItemUpdated(event: WorkItemUpdatedEvent): Promis
579
580
  },
580
581
  "assigned",
581
582
  );
583
+ };
584
+
585
+ return await handleDelivery(org, event.id, update.workItemId, async () => {
586
+ const result = await routeAssignment();
587
+ // Published inside the receipt and lock so a retried delivery cannot repeat it.
588
+ workflowEventBus.emit("azure-devops.work_item.assigned", {
589
+ work_item_id: update.workItemId,
590
+ title: update.revision.fields["System.Title"],
591
+ project: update.revision.fields["System.TeamProject"],
592
+ action: "assigned",
593
+ assigned_to: change.newValue,
594
+ previous_assignee: change.oldValue,
595
+ user_login: update.revisedBy?.uniqueName ?? update.revisedBy?.id,
596
+ });
597
+ return result;
582
598
  });
583
599
  }
584
600
 
@@ -598,31 +614,45 @@ export async function handleWorkItemCommented(
598
614
  const org = orgFor(event);
599
615
  if (!org) return dropUntrustedOrg(event.eventType);
600
616
 
617
+ const routeComment = async (): Promise<HandlerResult> => {
618
+ const bot = await resolveBot(org.url);
619
+ const actor = parseIdentityString(item.fields["System.ChangedBy"]);
620
+ if (isBotIdentity(actor, bot)) return { created: false };
621
+
622
+ const { mentioned, text } = inspectMention(item.fields["System.History"], bot);
623
+ if (!mentioned) return { created: false };
624
+
625
+ return await routeToTask(
626
+ {
627
+ eventId: event.id,
628
+ eventType: event.eventType,
629
+ workItemId: item.id,
630
+ fields: item.fields,
631
+ org,
632
+ actor,
633
+ },
634
+ "mentioned_comment",
635
+ text,
636
+ );
637
+ };
638
+
601
639
  const commentKey = ref ? `comment:${trackerKey(org, item.id)}:${ref.commentId}` : undefined;
602
640
  return await handleDelivery(
603
641
  org,
604
642
  event.id,
605
643
  item.id,
606
644
  async () => {
607
- const bot = await resolveBot(org.url);
608
- const actor = parseIdentityString(item.fields["System.ChangedBy"]);
609
- if (isBotIdentity(actor, bot)) return { created: false };
610
-
611
- const { mentioned, text } = inspectMention(item.fields["System.History"], bot);
612
- if (!mentioned) return { created: false };
613
-
614
- return await routeToTask(
615
- {
616
- eventId: event.id,
617
- eventType: event.eventType,
618
- workItemId: item.id,
619
- fields: item.fields,
620
- org,
621
- actor,
622
- },
623
- "mentioned_comment",
624
- text,
625
- );
645
+ const result = await routeComment();
646
+ // Published inside the receipt and lock so a retried delivery cannot repeat it.
647
+ workflowEventBus.emit("azure-devops.work_item.commented", {
648
+ work_item_id: item.id,
649
+ title: item.fields["System.Title"],
650
+ project: item.fields["System.TeamProject"],
651
+ action: "commented",
652
+ comment: item.fields["System.History"],
653
+ user_login: item.fields["System.ChangedBy"],
654
+ });
655
+ return result;
626
656
  },
627
657
  commentKey,
628
658
  );
@@ -30,6 +30,7 @@ export {
30
30
  handlePullRequestCommented,
31
31
  handlePullRequestCreated,
32
32
  } from "./handlers";
33
+ export { initAzureDevOpsOutbound, teardownAzureDevOpsOutbound } from "./outbound";
33
34
  export type {
34
35
  AzureDevOpsWebhookEvent,
35
36
  PullRequestCommentedEvent,
@@ -0,0 +1,95 @@
1
+ import { getDbClient } from "../be/db";
2
+ import { getTrackerSync } from "../be/db-queries/tracker";
3
+ import { onTaskStarted } from "../be/task-lifecycle-events";
4
+ import type { AgentTask } from "../types";
5
+ import { scrubSecrets } from "../utils/secret-scrubber";
6
+ import { workflowEventBus } from "../workflows/event-bus";
7
+ import { updateWorkItemFields } from "./api";
8
+
9
+ const PROVIDER = "azure-devops";
10
+
11
+ /**
12
+ * Target state names are process-specific (Agile Active/Closed, Scrum
13
+ * Committed/Done, Basic Doing/Done), so they are operator-set. Unset or blank
14
+ * means the swarm never changes the work item state.
15
+ */
16
+ export function getWorkItemState(which: "STARTED" | "DONE"): string | null {
17
+ return process.env[`AZURE_DEVOPS_WORKITEM_STATE_${which}`]?.trim() || null;
18
+ }
19
+
20
+ /** Move the work item linked to `taskId` to `state`. Tasks without a link are ignored. */
21
+ async function transitionLinkedWorkItem(taskId: string, state: string): Promise<void> {
22
+ const sync = await getTrackerSync(PROVIDER, "task", taskId);
23
+ if (!sync) return;
24
+ // externalId is `{orgKey}#{workItemId}`, orgKey `dev.azure.com/{org}` or `{org}.visualstudio.com`.
25
+ const [orgKey, id] = sync.externalId.split("#");
26
+ const workItemId = Number(id);
27
+ if (!orgKey || !Number.isInteger(workItemId)) return;
28
+ const ok = await updateWorkItemFields(`https://${orgKey}`, workItemId, [
29
+ { op: "add", path: "/fields/System.State", value: state },
30
+ ]);
31
+ if (ok) {
32
+ console.log(
33
+ scrubSecrets(`[AzureDevOps] Work item #${workItemId} -> ${state} (task ${taskId})`),
34
+ );
35
+ }
36
+ }
37
+
38
+ const transitionChains = new Map<string, Promise<void>>();
39
+
40
+ /**
41
+ * Run transitions for one task in the order they were requested, so a slow
42
+ * "Doing" PATCH cannot land after "Done". Callers never wait on the chain.
43
+ */
44
+ function enqueueTransition(taskId: string, state: string, eventName: string): void {
45
+ const run = (transitionChains.get(taskId) ?? Promise.resolve())
46
+ .then(() => transitionLinkedWorkItem(taskId, state))
47
+ .catch((error) => report(eventName, error));
48
+ transitionChains.set(taskId, run);
49
+ void run.then(() => {
50
+ if (transitionChains.get(taskId) === run) transitionChains.delete(taskId);
51
+ });
52
+ }
53
+
54
+ function report(eventName: string, error: unknown): void {
55
+ console.error(
56
+ `[AzureDevOps Outbound] ${eventName} handler failed:`,
57
+ scrubSecrets(error instanceof Error ? error.message : String(error)),
58
+ );
59
+ }
60
+
61
+ function handleTaskStarted(task: AgentTask): void {
62
+ const state = getWorkItemState("STARTED");
63
+ if (state) enqueueTransition(task.id, state, "task.started");
64
+ }
65
+
66
+ const onTaskCompleted = (data: unknown): void => {
67
+ const { taskId } = (data ?? {}) as { taskId?: string };
68
+ const state = getWorkItemState("DONE");
69
+ if (taskId && state) enqueueTransition(taskId, state, "task.completed");
70
+ };
71
+
72
+ let subscribed = false;
73
+ let startedHookRegistered = false;
74
+
75
+ /** Subscribe work-item state transitions to task lifecycle events. Idempotent, API-server only. */
76
+ export function initAzureDevOpsOutbound(): void {
77
+ if (subscribed) return;
78
+ subscribed = true;
79
+ // onTaskStarted has no unregister, so register once and gate on `subscribed`.
80
+ if (!startedHookRegistered) {
81
+ startedHookRegistered = true;
82
+ onTaskStarted((task) => {
83
+ // startTask/claimTask can emit inside a transaction; a rollback must not reach Azure.
84
+ if (subscribed) getDbClient().afterCommit(() => handleTaskStarted(task));
85
+ });
86
+ }
87
+ workflowEventBus.on("task.completed", onTaskCompleted);
88
+ }
89
+
90
+ /** Unsubscribe from lifecycle events (tests, config reloads). */
91
+ export function teardownAzureDevOpsOutbound(): void {
92
+ if (!subscribed) return;
93
+ subscribed = false;
94
+ workflowEventBus.off("task.completed", onTaskCompleted);
95
+ }
@@ -18,10 +18,12 @@
18
18
  import type { Database } from "bun:sqlite";
19
19
  import { getDb } from "./db";
20
20
  import { ensureRbacSeedsSynced } from "./rbac-roles";
21
+ import { seedModelProfiles } from "./seed-model-profiles";
21
22
  import { seedPricingFromModelsDev } from "./seed-pricing";
22
23
 
23
24
  const pricingSeededDbs = new WeakSet<Database>();
24
25
  const rbacSyncedDbs = new WeakSet<Database>();
26
+ const modelProfilesSeededDbs = new WeakSet<Database>();
25
27
 
26
28
  /** Seed the pricing table from the vendored models.dev snapshot. */
27
29
  export function ensurePricingSeeded(): void {
@@ -38,3 +40,12 @@ export function ensureRbacSeeded(): void {
38
40
  ensureRbacSeedsSynced();
39
41
  rbacSyncedDbs.add(db);
40
42
  }
43
+
44
+ /** Seed the shipped `<harness>-<tier>` model profiles (src/be/seed-model-profiles.ts). */
45
+ export async function ensureModelProfilesSeeded(): Promise<void> {
46
+ const db = getDb();
47
+ if (modelProfilesSeededDbs.has(db)) return;
48
+ const inserted = await seedModelProfiles();
49
+ if (inserted > 0) console.log(`[startup] Seeded ${inserted} model profiles`);
50
+ modelProfilesSeededDbs.add(db);
51
+ }
@@ -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
+ }