@desplega.ai/agent-swarm 1.173.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 (432) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
  7. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
  8. package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
  15. package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
  16. package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
  17. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  18. package/dist/cli-2m1ktfzz.js +17795 -0
  19. package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
  20. package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
  21. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  22. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  23. package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
  24. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  25. package/dist/cli-6cvfxs8r.js +47 -0
  26. package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
  27. package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
  28. package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
  29. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  30. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  31. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  32. package/dist/cli-9traj3dn.js +638 -0
  33. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  34. package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
  35. package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
  36. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  37. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  38. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  39. package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
  40. package/dist/cli-e3g5mbkz.js +776 -0
  41. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  42. package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
  43. package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
  44. package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
  45. package/dist/cli-f527ce74.js +781 -0
  46. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  47. package/dist/cli-hjwmhcg1.js +10622 -0
  48. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  49. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  50. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  51. package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
  52. package/dist/cli-jta2eyzc.js +619 -0
  53. package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
  54. package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
  55. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  56. package/dist/cli-kceah1eh.js +92 -0
  57. package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
  58. package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
  59. package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
  60. package/dist/cli-n2wq4j7m.js +6985 -0
  61. package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
  62. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  63. package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
  64. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  65. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  66. package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
  67. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  68. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  69. package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
  70. package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
  71. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  72. package/dist/cli-rhs9pz33.js +294 -0
  73. package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
  74. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  75. package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
  76. package/dist/cli-skxvvpap.js +10140 -0
  77. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  78. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  79. package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
  80. package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
  81. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  82. package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
  83. package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
  84. package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
  85. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  86. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  87. package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
  88. package/dist/cli-wmgrkscj.js +23254 -0
  89. package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
  90. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  91. package/dist/cli.js +29 -45
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  99. package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
  100. package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.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-0d4x7gkx.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
  117. package/dist/index-52j1aja4.js +3465 -0
  118. package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
  119. package/dist/index-9gw58n4k.js +193 -0
  120. package/dist/index-9j87mcm8.js +56305 -0
  121. package/dist/index-bsmcresk.js +79424 -0
  122. package/dist/index-mh85qhec.js +475 -0
  123. package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
  124. package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
  125. package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
  126. package/dist/index-we27kd31.js +12810 -0
  127. package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-as9ga0d6.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-y5g1y7ev.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  148. package/openapi.json +523 -344
  149. package/package.json +10 -11
  150. package/src/be/config-refs.ts +60 -0
  151. package/src/be/db/page-versions.ts +15 -0
  152. package/src/be/db/runtime.ts +35 -8
  153. package/src/be/db/skill-name-resolution.ts +36 -0
  154. package/src/be/db/tasks/read.ts +230 -0
  155. package/src/be/db/tasks/write.ts +7 -0
  156. package/src/be/db.ts +52 -7
  157. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  158. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  159. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  160. package/src/be/model-catalog-store.ts +5 -1
  161. package/src/be/model-tier-resolution.ts +12 -1
  162. package/src/be/model-validation.ts +5 -2
  163. package/src/be/models-catalog.ts +26 -1
  164. package/src/be/modelsdev-cache.json +198 -63
  165. package/src/be/modelsdev-cache.ts +9 -1
  166. package/src/be/script-credential-broker.ts +39 -2
  167. package/src/be/scripts/run-saved.ts +1 -0
  168. package/src/be/scripts/typecheck.ts +10 -8
  169. package/src/be/seed/runner.ts +13 -1
  170. package/src/be/seed/state-db.ts +5 -0
  171. package/src/be/seed/types.ts +6 -0
  172. package/src/be/seed-pricing.ts +19 -2
  173. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  174. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  175. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  176. package/src/be/seed-skills/index.ts +104 -3
  177. package/src/be/swarm-config-guard.ts +52 -0
  178. package/src/cli.tsx +0 -16
  179. package/src/commands/onboard/non-interactive.ts +4 -2
  180. package/src/commands/provider-credentials.ts +52 -6
  181. package/src/commands/runner.ts +190 -54
  182. package/src/e2b/env.ts +5 -0
  183. package/src/extensions/contract-types.generated.ts +1 -1
  184. package/src/extensions/contract.ts +45 -0
  185. package/src/extensions/dispatcher.ts +7 -0
  186. package/src/extensions/post-bridge.ts +40 -0
  187. package/src/github/handlers.ts +11 -4
  188. package/src/heartbeat/templates.ts +8 -5
  189. package/src/hooks/hook.ts +0 -26
  190. package/src/http/all-routes.ts +0 -1
  191. package/src/http/config.ts +36 -17
  192. package/src/http/core.ts +4 -0
  193. package/src/http/index.ts +14 -2
  194. package/src/http/integrations.ts +88 -1
  195. package/src/http/mcp.ts +15 -5
  196. package/src/http/page-proxy.ts +2 -4
  197. package/src/http/pages-public.ts +3 -3
  198. package/src/http/pages.ts +3 -13
  199. package/src/http/poll.ts +28 -68
  200. package/src/http/requested-by.ts +54 -0
  201. package/src/http/scripts.ts +1 -0
  202. package/src/http/stats.ts +1 -30
  203. package/src/http/tasks.ts +61 -5
  204. package/src/http/utils.ts +14 -0
  205. package/src/http/webhooks.ts +159 -1
  206. package/src/http/x.ts +1 -0
  207. package/src/integrations/chat-sdk/adapters.ts +129 -0
  208. package/src/integrations/chat-sdk/attachments.ts +232 -0
  209. package/src/integrations/chat-sdk/background.ts +36 -0
  210. package/src/integrations/chat-sdk/commands.ts +194 -0
  211. package/src/integrations/chat-sdk/config.ts +198 -0
  212. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  213. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  214. package/src/integrations/chat-sdk/handlers.ts +28 -0
  215. package/src/integrations/chat-sdk/inbound.ts +426 -0
  216. package/src/integrations/chat-sdk/limits.ts +48 -0
  217. package/src/integrations/chat-sdk/logger.ts +81 -0
  218. package/src/integrations/chat-sdk/outbound.ts +123 -0
  219. package/src/integrations/chat-sdk/relay.ts +172 -0
  220. package/src/integrations/chat-sdk/router.ts +169 -0
  221. package/src/integrations/chat-sdk/runtime.ts +351 -0
  222. package/src/integrations/chat-sdk/schema.ts +245 -0
  223. package/src/integrations/chat-sdk/state.ts +243 -0
  224. package/src/integrations/chat-sdk/status.ts +244 -0
  225. package/src/integrations/chat-sdk/steering.ts +58 -0
  226. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  227. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  228. package/src/integrations/chat-sdk/templates.ts +16 -0
  229. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  230. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  231. package/src/integrations/chat-sdk/typing.ts +65 -0
  232. package/src/pages/version.ts +4 -5
  233. package/src/prompts/base-prompt.ts +111 -45
  234. package/src/providers/acp-target-catalog.ts +6 -0
  235. package/src/providers/acp-targets.ts +55 -2
  236. package/src/providers/amp-adapter.ts +1 -1
  237. package/src/providers/claude-adapter.ts +1 -1
  238. package/src/providers/claude-flag-settings.ts +17 -5
  239. package/src/providers/claude-managed-adapter.ts +1 -1
  240. package/src/providers/claude-session-events.ts +6 -1
  241. package/src/providers/dsh-adapter.ts +1 -1
  242. package/src/providers/opencode-adapter.ts +243 -26
  243. package/src/providers/opencode-swarm-plugin.ts +31 -0
  244. package/src/providers/pi-mono-adapter.ts +398 -153
  245. package/src/providers/pricing-sources.md +33 -2
  246. package/src/providers/reasoning-effort.ts +27 -0
  247. package/src/providers/types.ts +6 -2
  248. package/src/rbac/legacy-policy.ts +8 -2
  249. package/src/rbac/permissions.ts +21 -0
  250. package/src/scheduler/scheduler.ts +4 -2
  251. package/src/scripts-runtime/loader.ts +19 -7
  252. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  253. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  254. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  255. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  256. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  257. package/src/server.ts +52 -18
  258. package/src/slack/event-dedup.ts +1 -1
  259. package/src/tasks/additive-buffer.ts +4 -2
  260. package/src/tasks/context-key.ts +60 -1
  261. package/src/tasks/model-pin.ts +67 -0
  262. package/src/tasks/worker-follow-up.ts +4 -31
  263. package/src/tests/acp-adapter.test.ts +42 -0
  264. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  265. package/src/tests/base-prompt.test.ts +58 -54
  266. package/src/tests/chat-sdk-commands.test.ts +191 -0
  267. package/src/tests/chat-sdk-config.test.ts +334 -0
  268. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  269. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  270. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  271. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  272. package/src/tests/chat-sdk-harness.ts +146 -0
  273. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  274. package/src/tests/chat-sdk-relay.test.ts +470 -0
  275. package/src/tests/chat-sdk-reload.test.ts +128 -0
  276. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  277. package/src/tests/chat-sdk-slack.test.ts +268 -0
  278. package/src/tests/chat-sdk-state.test.ts +163 -0
  279. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  280. package/src/tests/chat-sdk-teams.test.ts +277 -0
  281. package/src/tests/chat-sdk-tools.test.ts +520 -0
  282. package/src/tests/chat-sdk-typing.test.ts +120 -0
  283. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  284. package/src/tests/claude-adapter-binary.test.ts +9 -4
  285. package/src/tests/claude-flag-settings.test.ts +11 -3
  286. package/src/tests/claude-managed-adapter.test.ts +1 -1
  287. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  288. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  289. package/src/tests/claude-worker-parity.test.ts +2 -7
  290. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  291. package/src/tests/config-refs.test.ts +66 -0
  292. package/src/tests/context-key.test.ts +19 -0
  293. package/src/tests/cors-allowlist.test.ts +25 -0
  294. package/src/tests/create-page-tool.test.ts +18 -0
  295. package/src/tests/credential-check.test.ts +139 -0
  296. package/src/tests/credentials.test.ts +21 -0
  297. package/src/tests/db-runtime.test.ts +61 -0
  298. package/src/tests/db-task-read-contract.test.ts +20 -0
  299. package/src/tests/db-task-write-contract.test.ts +10 -0
  300. package/src/tests/docs-index-footprint.test.js +3 -3
  301. package/src/tests/dsh-adapter.test.ts +1 -1
  302. package/src/tests/enabled-capabilities.test.ts +20 -0
  303. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  304. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  305. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  306. package/src/tests/explicit-model-validation.test.ts +105 -1
  307. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  308. package/src/tests/extensions-post-bridge.test.ts +51 -0
  309. package/src/tests/fetch-resolved-env.test.ts +128 -0
  310. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  311. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  312. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  313. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  314. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  315. package/src/tests/github-handlers.test.ts +35 -0
  316. package/src/tests/harness-provider-resolution.test.ts +12 -0
  317. package/src/tests/heartbeat-checklist.test.ts +3 -2
  318. package/src/tests/hook-runtime-close.test.ts +0 -2
  319. package/src/tests/http-api-integration.test.ts +0 -25
  320. package/src/tests/migration-046-budgets.test.ts +17 -16
  321. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  322. package/src/tests/model-tier-resolution.test.ts +93 -0
  323. package/src/tests/models-catalog.test.ts +42 -1
  324. package/src/tests/openai-compatible-providers.test.ts +134 -0
  325. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  326. package/src/tests/opencode-adapter.test.ts +216 -2
  327. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  328. package/src/tests/opencode-shell-env.test.ts +145 -0
  329. package/src/tests/openrouter-base-url.test.ts +101 -151
  330. package/src/tests/page-proxy.test.ts +1 -1
  331. package/src/tests/pages-browser-sdk.test.ts +76 -0
  332. package/src/tests/pages-feedback.test.ts +1 -1
  333. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  334. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  335. package/src/tests/pi-mono-adapter.test.ts +385 -5
  336. package/src/tests/pi-shell-env.test.ts +170 -0
  337. package/src/tests/prompt-template-remaining.test.ts +35 -0
  338. package/src/tests/rbac-engine.test.ts +11 -3
  339. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  340. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  341. package/src/tests/script-connections.test.ts +2 -0
  342. package/src/tests/scripts-http.test.ts +201 -1
  343. package/src/tests/scripts-only-gating.test.ts +2 -3
  344. package/src/tests/scripts-runtime.test.ts +58 -40
  345. package/src/tests/secret-scrubber.test.ts +86 -0
  346. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  347. package/src/tests/seed-skills-retire.test.ts +182 -0
  348. package/src/tests/send-task-requester-gate.test.ts +58 -0
  349. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  350. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  351. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  352. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  353. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  354. package/src/tests/swarm-diff.test.ts +1 -1
  355. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  356. package/src/tests/task-session-prompts.test.ts +132 -0
  357. package/src/tests/task-tool-manifest.test.ts +14 -0
  358. package/src/tests/task-tools-ctx.test.ts +2 -0
  359. package/src/tests/tool-annotations.test.ts +6 -6
  360. package/src/tests/ui-logs-parser.test.ts +43 -0
  361. package/src/tests/used-by-route.test.ts +6 -0
  362. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  363. package/src/tests/workflow-swarm-script.test.ts +38 -0
  364. package/src/tests/workflow-wait-filter.test.ts +5 -3
  365. package/src/tools/chat-access.ts +41 -0
  366. package/src/tools/chat-open-dm.ts +59 -0
  367. package/src/tools/chat-post.ts +74 -0
  368. package/src/tools/chat-read-thread.ts +100 -0
  369. package/src/tools/chat-reply.ts +75 -0
  370. package/src/tools/create-page.ts +3 -13
  371. package/src/tools/model-catalog/index.ts +2 -1
  372. package/src/tools/register-chat-channel.ts +139 -0
  373. package/src/tools/send-task.ts +24 -0
  374. package/src/tools/skills/skill-get.ts +17 -9
  375. package/src/tools/swarm-config/get-config.ts +2 -1
  376. package/src/tools/swarm-config/list-config.ts +2 -1
  377. package/src/tools/swarm-config/set-config.ts +16 -3
  378. package/src/tools/tool-config.ts +8 -6
  379. package/src/types.ts +12 -2
  380. package/src/utils/config-env-export.ts +74 -0
  381. package/src/utils/credentials.test.ts +92 -0
  382. package/src/utils/credentials.ts +52 -4
  383. package/src/utils/harness-provider.ts +15 -6
  384. package/src/utils/runtime-model-catalog.ts +11 -1
  385. package/src/utils/secret-scrubber.ts +71 -2
  386. package/src/utils/task-tool-manifest.ts +19 -2
  387. package/src/workflows/executors/swarm-script.ts +1 -0
  388. package/templates/skills/agent-browser/SKILL.md +1 -1
  389. package/templates/skills/agent-browser/content.md +1 -1
  390. package/templates/skills/apps/SKILL.md +8 -1
  391. package/templates/skills/apps/content.md +8 -1
  392. package/templates/skills/chat-sdk/config.json +14 -0
  393. package/templates/skills/chat-sdk/content.md +84 -0
  394. package/templates/skills/composio-gmail/content.md +7 -0
  395. package/templates/skills/composio-google-docs/content.md +1 -1
  396. package/templates/skills/email-domain-health/config.json +14 -0
  397. package/templates/skills/email-domain-health/content.md +131 -0
  398. package/templates/skills/kv-storage/content.md +2 -2
  399. package/templates/skills/pages/content.md +8 -10
  400. package/templates/skills/swarm-extensions/content.md +2 -0
  401. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  402. package/templates/skills/swarm-scripts/config.json +1 -1
  403. package/templates/skills/swarm-scripts/content.md +4 -2
  404. package/templates/skills/workflow-iterate/content.md +38 -0
  405. package/dist/artifact-eczsknc3.js +0 -3843
  406. package/dist/cli-bd957dgb.js +0 -153
  407. package/dist/cli-q51yprfq.js +0 -1104
  408. package/dist/cli-t1ze64h3.js +0 -811
  409. package/dist/cli-tcc4wd5d.js +0 -56471
  410. package/dist/index-9ch1n761.js +0 -1102
  411. package/src/artifact-sdk/index.ts +0 -2
  412. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  413. package/src/artifact-sdk/port.ts +0 -12
  414. package/src/artifact-sdk/server.ts +0 -183
  415. package/src/artifact-sdk/tunnel.ts +0 -19
  416. package/src/commands/artifact.ts +0 -249
  417. package/src/http/ecosystem.ts +0 -77
  418. package/src/tests/artifact-commands.test.ts +0 -92
  419. package/src/tests/artifact-sdk.test.ts +0 -793
  420. package/src/tests/register-service-security.test.ts +0 -194
  421. package/src/tools/list-services.ts +0 -107
  422. package/src/tools/register-service.ts +0 -177
  423. package/src/tools/unregister-service.ts +0 -73
  424. package/src/tools/update-service-status.ts +0 -96
  425. package/templates/skills/artifacts/config.json +0 -14
  426. package/templates/skills/artifacts/content.md +0 -344
  427. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  428. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  429. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  430. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  431. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  432. /package/src/{artifact-sdk → pages}/feedback-overlay.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.0.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,7 +175,7 @@
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",
@@ -205,13 +200,16 @@
205
200
  "dependencies": {
206
201
  "@agentclientprotocol/sdk": "1.8.0",
207
202
  "@ai-sdk/openai": "^3.0.41",
208
- "@anthropic-ai/claude-agent-sdk": "0.3.299",
209
- "@anthropic-ai/sdk": "^0.133.0",
203
+ "@anthropic-ai/claude-agent-sdk": "0.3.300",
204
+ "@anthropic-ai/sdk": "^0.134.0",
210
205
  "@asteasolutions/zod-to-openapi": "^8.0.0",
211
206
  "@aws-sdk/client-bedrock": "3.1048.0",
207
+ "@chat-adapter/discord": "4.41.1",
208
+ "@chat-adapter/slack": "4.41.1",
209
+ "@chat-adapter/teams": "4.41.1",
210
+ "@chat-adapter/telegram": "4.41.1",
212
211
  "@cursor/sdk": "1.0.37",
213
212
  "@desplega.ai/business-use": "^0.4.2",
214
- "@desplega.ai/localtunnel": "^2.2.0",
215
213
  "@earendil-works/pi-agent-core": "^1.2.0",
216
214
  "@earendil-works/pi-ai": "^1.2.0",
217
215
  "@earendil-works/pi-coding-agent": "^1.2.0",
@@ -230,15 +228,16 @@
230
228
  "@opentelemetry/sdk-node": "^0.218.0",
231
229
  "@opentelemetry/semantic-conventions": "^1.41.1",
232
230
  "@slack/bolt": "^4.6.0",
231
+ "@slack/web-api": "7.13.0",
233
232
  "@types/react": "^19.2.7",
234
233
  "@x402/core": "^2.5.0",
235
234
  "@x402/evm": "^2.5.0",
236
235
  "@x402/fetch": "^2.5.0",
237
236
  "ai": "^6.0.116",
237
+ "chat": "4.41.1",
238
238
  "cron-parser": "^5.4.0",
239
239
  "date-fns": "^4.1.0",
240
240
  "e2b": "2.26.0",
241
- "hono": "^4.12.3",
242
241
  "ink": "^6.5.1",
243
242
  "oauth4webapi": "^3.8.5",
244
243
  "openai": "^6.22.0",
@@ -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
+ }
@@ -20,6 +20,7 @@ import {
20
20
  RoutingAffinitySchema,
21
21
  } from "../../../types";
22
22
  import { parseProviderMeta } from "../../../utils/provider-metadata";
23
+ import { scrubSecrets } from "../../../utils/secret-scrubber";
23
24
  import { getAgentById } from "../agents";
24
25
  import { getDbClient } from "../runtime";
25
26
 
@@ -87,6 +88,7 @@ export type AgentTaskRow = {
87
88
  model: string | null;
88
89
  modelTier: string | null;
89
90
  effort: string | null;
91
+ resolvedEffort?: string | null;
90
92
  resolvedModel?: string | null;
91
93
  modelSource?: string | null;
92
94
  modelAlias?: string | null;
@@ -154,6 +156,7 @@ type AgentTaskSummaryRow = Pick<
154
156
  | "modelSource"
155
157
  | "modelAlias"
156
158
  | "effort"
159
+ | "resolvedEffort"
157
160
  | "provider"
158
161
  | "requestedByUserId"
159
162
  | "progress"
@@ -207,6 +210,7 @@ const LIST_COLUMNS = {
207
210
  agent_tasks.offeredTo, agent_tasks.acceptedAt, agent_tasks.parentTaskId,
208
211
  agent_tasks.scheduleId, agent_tasks.model, agent_tasks.modelTier,
209
212
  agent_tasks.resolvedModel, agent_tasks.modelSource, agent_tasks.modelAlias, agent_tasks.effort,
213
+ agent_tasks.resolvedEffort,
210
214
  agent_tasks.provider, agent_tasks.requestedByUserId, agent_tasks.progress,
211
215
  agent_tasks.createdAt, agent_tasks.lastUpdatedAt, agent_tasks.finishedAt,
212
216
  agent_tasks.peakContextPercent`,
@@ -315,6 +319,9 @@ export function rowToAgentTask(row: AgentTaskRow): AgentTask {
315
319
  effort: ReasoningEffortSchema.safeParse(row.effort).success
316
320
  ? (row.effort as ReasoningEffort)
317
321
  : undefined,
322
+ resolvedEffort: ReasoningEffortSchema.safeParse(row.resolvedEffort).success
323
+ ? (row.resolvedEffort as ReasoningEffort)
324
+ : undefined,
318
325
  scheduleId: row.scheduleId ?? undefined,
319
326
  workflowRunId: row.workflowRunId ?? undefined,
320
327
  workflowRunStepId: row.workflowRunStepId ?? undefined,
@@ -381,6 +388,9 @@ export function rowToAgentTaskSummary(row: AgentTaskSummaryRow): AgentTaskSummar
381
388
  effort: ReasoningEffortSchema.safeParse(row.effort).success
382
389
  ? (row.effort as ReasoningEffort)
383
390
  : undefined,
391
+ resolvedEffort: ReasoningEffortSchema.safeParse(row.resolvedEffort).success
392
+ ? (row.resolvedEffort as ReasoningEffort)
393
+ : undefined,
384
394
  provider: (row.provider as ProviderName | null) ?? undefined,
385
395
  requestedByUserId: row.requestedByUserId ?? undefined,
386
396
  progress: row.progress ?? undefined,
@@ -1035,6 +1045,163 @@ export async function markFinalizedSlackRelaysDelivered(): Promise<number> {
1035
1045
  return result.changes;
1036
1046
  }
1037
1047
 
1048
+ /** Pending Chat SDK relay row: the terminal task plus its obligation bookkeeping. */
1049
+ export interface PendingChatRelay {
1050
+ task: AgentTask;
1051
+ createdAt: string;
1052
+ lastError: string | null;
1053
+ }
1054
+
1055
+ /**
1056
+ * Backoff gate for a chat relay obligation: eligible when never attempted, or
1057
+ * when `min(3 s * 2^attempt_count, 10 min)` passed since `last_attempt_at`.
1058
+ * `attempt_count >= 8` is already past the cap (3 s * 2^8 = 768 s). The one
1059
+ * parameter is the reference time as ISO text.
1060
+ */
1061
+ const CHAT_RELAY_ELIGIBLE_SQL = `(obligation.last_attempt_at IS NULL
1062
+ OR (julianday(?) - julianday(obligation.last_attempt_at)) * 86400000.0 >=
1063
+ CASE WHEN obligation.attempt_count >= 8 THEN 600000
1064
+ ELSE 3000 * (1 << obligation.attempt_count) END)`;
1065
+
1066
+ /**
1067
+ * Terminal chat tasks whose relay obligation is pending, out of backoff at
1068
+ * `now`, and whose `task:chat-sdk:<adapter>:` contextKey names one of
1069
+ * `adapters` (the adapters that are running). Rows of other adapters are left
1070
+ * out of the query, so a stopped adapter cannot fill the window and starve the
1071
+ * healthy ones. Returns no rows when `adapters` is empty. Never-attempted rows
1072
+ * sort first, then the oldest attempts. A contextKey that does not parse as a
1073
+ * chat key is not selected here; `giveUpStaleChatRelays` retires such rows once
1074
+ * they age out.
1075
+ */
1076
+ export async function getPendingChatRelayTasks(
1077
+ now: number = Date.now(),
1078
+ limit = 200,
1079
+ adapters: readonly string[] = [],
1080
+ ): Promise<PendingChatRelay[]> {
1081
+ if (adapters.length === 0) return [];
1082
+ for (const adapter of adapters) {
1083
+ if (!/^[a-z]+$/.test(adapter)) throw new Error(`invalid chat adapter name: ${adapter}`);
1084
+ }
1085
+ const adapterFilter = adapters.map(() => "task.contextKey GLOB ?").join(" OR ");
1086
+ const rows = await getDbClient().query<
1087
+ AgentTaskRow & {
1088
+ relay_created_at: string;
1089
+ relay_last_error: string | null;
1090
+ }
1091
+ >(
1092
+ `SELECT task.*,
1093
+ obligation.created_at AS relay_created_at,
1094
+ obligation.last_error AS relay_last_error
1095
+ FROM chat_relay_obligations obligation
1096
+ JOIN agent_tasks task ON task.id = obligation.task_id
1097
+ WHERE obligation.delivered_at IS NULL
1098
+ AND obligation.given_up_at IS NULL
1099
+ AND task.status IN ('completed', 'failed', 'cancelled')
1100
+ AND (${adapterFilter})
1101
+ AND ${CHAT_RELAY_ELIGIBLE_SQL}
1102
+ ORDER BY obligation.last_attempt_at IS NOT NULL,
1103
+ obligation.last_attempt_at ASC,
1104
+ obligation.created_at ASC
1105
+ LIMIT ?`,
1106
+ [
1107
+ ...adapters.map((adapter) => `task:chat-sdk:${adapter}:*`),
1108
+ new Date(now).toISOString(),
1109
+ limit,
1110
+ ],
1111
+ );
1112
+ return rows.map((row) => ({
1113
+ task: rowToAgentTask(row),
1114
+ createdAt: row.relay_created_at,
1115
+ lastError: row.relay_last_error,
1116
+ }));
1117
+ }
1118
+
1119
+ /**
1120
+ * Give up every pending chat relay obligation created more than `olderThanMs`
1121
+ * before `now`, whatever the adapter state. Returns how many rows were retired.
1122
+ */
1123
+ export async function giveUpStaleChatRelays(now: number, olderThanMs: number): Promise<number> {
1124
+ const at = new Date(now).toISOString();
1125
+ const cutoff = new Date(now - olderThanMs).toISOString();
1126
+ const result = await getDbClient().run(
1127
+ `UPDATE chat_relay_obligations
1128
+ SET given_up_at = ?, last_error = 'gave up: older than 24h', updated_at = ?
1129
+ WHERE delivered_at IS NULL AND given_up_at IS NULL AND created_at < ?`,
1130
+ [at, at, cutoff],
1131
+ );
1132
+ return result.changes;
1133
+ }
1134
+
1135
+ /**
1136
+ * Claim a chat relay row before the send: bump `attempt_count` and set
1137
+ * `last_attempt_at`. Conditional on the row still being pending and out of
1138
+ * backoff, so two concurrent ticks cannot both send. Returns true when claimed.
1139
+ */
1140
+ export async function markChatRelayAttempted(
1141
+ taskId: string,
1142
+ now: number = Date.now(),
1143
+ ): Promise<boolean> {
1144
+ const at = new Date(now).toISOString();
1145
+ const result = await getDbClient().run(
1146
+ `UPDATE chat_relay_obligations AS obligation
1147
+ SET attempt_count = attempt_count + 1,
1148
+ last_attempt_at = ?,
1149
+ updated_at = ?
1150
+ WHERE task_id = ?
1151
+ AND delivered_at IS NULL
1152
+ AND given_up_at IS NULL
1153
+ AND ${CHAT_RELAY_ELIGIBLE_SQL}`,
1154
+ [at, at, taskId, at],
1155
+ );
1156
+ return result.changes > 0;
1157
+ }
1158
+
1159
+ /** Record why the last chat relay attempt failed. The text is scrubbed before it is stored. */
1160
+ export async function recordChatRelayError(
1161
+ taskId: string,
1162
+ error: string,
1163
+ now: number = Date.now(),
1164
+ ): Promise<boolean> {
1165
+ const result = await getDbClient().run(
1166
+ `UPDATE chat_relay_obligations
1167
+ SET last_error = ?, updated_at = ?
1168
+ WHERE task_id = ? AND delivered_at IS NULL`,
1169
+ [scrubSecrets(error), new Date(now).toISOString(), taskId],
1170
+ );
1171
+ return result.changes > 0;
1172
+ }
1173
+
1174
+ /** Mark a chat relay obligation delivered after the platform accepted every chunk. Idempotent. */
1175
+ export async function markChatRelayDelivered(
1176
+ taskId: string,
1177
+ now: number = Date.now(),
1178
+ ): Promise<boolean> {
1179
+ const at = new Date(now).toISOString();
1180
+ const result = await getDbClient().run(
1181
+ `UPDATE chat_relay_obligations
1182
+ SET delivered_at = ?, updated_at = ?
1183
+ WHERE task_id = ? AND delivered_at IS NULL`,
1184
+ [at, at, taskId],
1185
+ );
1186
+ return result.changes > 0;
1187
+ }
1188
+
1189
+ /** Stop retrying a chat relay obligation. The reason is scrubbed before it is stored. */
1190
+ export async function markChatRelayGivenUp(
1191
+ taskId: string,
1192
+ error: string,
1193
+ now: number = Date.now(),
1194
+ ): Promise<boolean> {
1195
+ const at = new Date(now).toISOString();
1196
+ const result = await getDbClient().run(
1197
+ `UPDATE chat_relay_obligations
1198
+ SET given_up_at = ?, last_error = ?, updated_at = ?
1199
+ WHERE task_id = ? AND delivered_at IS NULL AND given_up_at IS NULL`,
1200
+ [at, scrubSecrets(error), at, taskId],
1201
+ );
1202
+ return result.changes > 0;
1203
+ }
1204
+
1038
1205
  /**
1039
1206
  * Get tasks that were recently finished (completed/failed) by workers (non-lead agents).
1040
1207
  * Used by leads to know when workers complete tasks.
@@ -1334,6 +1501,69 @@ export async function getLatestLeadTaskInThread(
1334
1501
  return row ? rowToAgentTask(row) : null;
1335
1502
  }
1336
1503
 
1504
+ /**
1505
+ * ContextKey twins of the Slack thread lookups above, for channels that key a
1506
+ * conversation by `contextKey` only (Chat SDK). `source` narrows to the
1507
+ * channel's ingress tasks, as the Slack twins do with `source = 'slack'`:
1508
+ * follow-ups and delegated worker tasks inherit the parent's contextKey.
1509
+ * All three use `idx_agent_tasks_context_key_status`.
1510
+ */
1511
+ export async function getAgentWorkingOnContextKey(
1512
+ contextKey: string,
1513
+ source?: string,
1514
+ ): Promise<Agent | null> {
1515
+ const taskRow = await getDbClient().get<AgentTaskRow>(
1516
+ `SELECT agentId FROM agent_tasks
1517
+ WHERE contextKey = ?
1518
+ AND (? IS NULL OR source = ?)
1519
+ ORDER BY createdAt DESC, rowid DESC
1520
+ LIMIT 1`,
1521
+ [contextKey, source ?? null, source ?? null],
1522
+ );
1523
+
1524
+ if (taskRow?.agentId) return getAgentById(taskRow.agentId);
1525
+
1526
+ return null;
1527
+ }
1528
+
1529
+ /** Latest `in_progress` or `pending` task on a contextKey (dependency chaining). */
1530
+ export async function getLatestActiveTaskByContextKey(
1531
+ contextKey: string,
1532
+ source?: string,
1533
+ ): Promise<AgentTask | null> {
1534
+ const row = await getDbClient().get<AgentTaskRow>(
1535
+ `SELECT * FROM agent_tasks
1536
+ WHERE contextKey = ?
1537
+ AND (? IS NULL OR source = ?)
1538
+ AND status IN ('in_progress', 'pending')
1539
+ ORDER BY createdAt DESC, rowid DESC
1540
+ LIMIT 1`,
1541
+ [contextKey, source ?? null, source ?? null],
1542
+ );
1543
+
1544
+ return row ? rowToAgentTask(row) : null;
1545
+ }
1546
+
1547
+ /** Latest task assigned to a lead agent on a contextKey (the steering target). */
1548
+ export async function getLatestLeadTaskByContextKey(
1549
+ contextKey: string,
1550
+ source?: string,
1551
+ ): Promise<AgentTask | null> {
1552
+ const row = await getDbClient().get<AgentTaskRow>(
1553
+ `SELECT t.*
1554
+ FROM agent_tasks t
1555
+ JOIN agents a ON a.id = t.agentId
1556
+ WHERE t.contextKey = ?
1557
+ AND (? IS NULL OR t.source = ?)
1558
+ AND a.isLead = 1
1559
+ ORDER BY t.createdAt DESC, t.rowid DESC
1560
+ LIMIT 1`,
1561
+ [contextKey, source ?? null, source ?? null],
1562
+ );
1563
+
1564
+ return row ? rowToAgentTask(row) : null;
1565
+ }
1566
+
1337
1567
  /**
1338
1568
  * Find the most recent task in a Slack thread, regardless of source or status.
1339
1569
  * Unlike getAgentWorkingOnThread (which filters source='slack'), this finds ALL tasks
@@ -8,6 +8,7 @@ import type {
8
8
  AgentTask,
9
9
  AgentTaskSource,
10
10
  ProviderName,
11
+ ReasoningEffort,
11
12
  RuntimePool,
12
13
  TaskAttachment,
13
14
  } from "../../../types";
@@ -312,6 +313,8 @@ export async function updateTaskClaudeSessionId(
312
313
  model?: string,
313
314
  harnessVariant?: string,
314
315
  harnessVariantMeta?: Record<string, unknown>,
316
+ /** Effort the session runs at; null records that the harness default applied. */
317
+ resolvedEffort?: ReasoningEffort | null,
315
318
  ): Promise<AgentTask | null> {
316
319
  const setClauses = ["claudeSessionId = ?", "lastUpdatedAt = ?"];
317
320
  const params: (string | null)[] = [claudeSessionId, new Date().toISOString()];
@@ -336,6 +339,10 @@ export async function updateTaskClaudeSessionId(
336
339
  setClauses.push("harnessVariantMeta = ?");
337
340
  params.push(JSON.stringify(harnessVariantMeta));
338
341
  }
342
+ if (resolvedEffort !== undefined) {
343
+ setClauses.push("resolvedEffort = ?");
344
+ params.push(resolvedEffort);
345
+ }
339
346
 
340
347
  params.push(taskId);
341
348