@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
@@ -9,6 +9,7 @@ import {
9
9
  getWorkflowRun,
10
10
  getWorkflowRunStepsByRunId,
11
11
  initDb,
12
+ upsertSwarmConfig,
12
13
  } from "../be/db";
13
14
  import { upsertScriptConnection } from "../be/script-connections";
14
15
  import { getScript, upsertScriptByName } from "../be/scripts/db";
@@ -31,6 +32,7 @@ import {
31
32
  } from "../workflows/executors/swarm-script";
32
33
  import { interpolate } from "../workflows/template";
33
34
  import { SKIP_SANDBOX_SPAWN_TESTS } from "./sandbox-spawn-test-helpers";
35
+ import { CHILD_PROCESS_TEST_BUDGET_MS } from "./test-proc";
34
36
 
35
37
  const skip = test.skipIf(SKIP_SANDBOX_SPAWN_TESTS);
36
38
 
@@ -370,6 +372,42 @@ describe("SwarmScriptExecutor", () => {
370
372
  expect(result.output?.result).toEqual({ apiKeys: ["workflowVendor"] });
371
373
  });
372
374
 
375
+ skip(
376
+ "swarm-script node reads swarm config through ctx.swarm.config.get",
377
+ async () => {
378
+ await upsertSwarmConfig({ scope: "global", key: "WORKFLOW_CFG_PLAIN", value: "node-value" });
379
+ await saveScript(
380
+ "ctx-config-get",
381
+ `export default async (_args, ctx) => {
382
+ const value = ctx.swarm.config.get("WORKFLOW_CFG_PLAIN");
383
+ return {
384
+ value: value === undefined ? null : ctx.stdlib.Redacted.value(value),
385
+ missing: ctx.swarm.config.get("WORKFLOW_CFG_NOT_SET") ?? "fallback",
386
+ };
387
+ };`,
388
+ );
389
+
390
+ const executor = new SwarmScriptExecutor(deps);
391
+ const wf = await makeWorkflow({ nodes: [] });
392
+ const result = await executor.run({
393
+ config: { scriptName: "ctx-config-get" },
394
+ context: {},
395
+ meta: {
396
+ runId: crypto.randomUUID(),
397
+ stepId: crypto.randomUUID(),
398
+ nodeId: "script",
399
+ workflowId: wf.id,
400
+ dryRun: false,
401
+ },
402
+ });
403
+ await getDbClient().run("DELETE FROM swarm_config WHERE key = 'WORKFLOW_CFG_PLAIN'");
404
+
405
+ expect(result.status).toBe("success");
406
+ expect(result.output?.result).toEqual({ value: "node-value", missing: "fallback" });
407
+ },
408
+ CHILD_PROCESS_TEST_BUDGET_MS,
409
+ );
410
+
373
411
  skip("pinHash correctly resolves to a historic script_versions row", async () => {
374
412
  const first = await saveScript("versioned", `export default async () => ({ version: "old" });`);
375
413
  await saveScript("versioned", `export default async () => ({ version: "new" });`);
@@ -154,12 +154,14 @@ describe("matchesFilter — sandbox penetration (must all return false, never th
154
154
  });
155
155
 
156
156
  test("DoS: pathological regex terminates within timeout", async () => {
157
- const filter = "(p) => /^(a+)+$/.test('a'.repeat(30) + 'X')";
157
+ // Still exponential backtracking, but 24 characters finish in ~0.1 s. The
158
+ // 50 ms timer cannot pre-empt a synchronous regex, so the elapsed time is
159
+ // the engine's own: 30 characters took 2058 ms on a loaded CI runner and
160
+ // failed the bound below without the matcher doing anything wrong.
161
+ const filter = "(p) => /^(a+)+$/.test('a'.repeat(24) + 'X')";
158
162
  const t0 = Date.now();
159
163
  const result = await matchesFilter({}, filter);
160
164
  const elapsed = Date.now() - t0;
161
- // The regex would eventually return false on its own, but if it stalls
162
- // the timeout catches it.
163
165
  expect(typeof result).toBe("boolean");
164
166
  expect(elapsed).toBeLessThan(2000);
165
167
  });
@@ -0,0 +1,41 @@
1
+ import { getAgentById, getAgentCurrentTask, getTaskById } from "@/be/db";
2
+ import { can } from "@/rbac";
3
+ import { chatThreadOfContextKey } from "@/tasks/context-key";
4
+ import type { RequestInfo } from "@/tools/utils";
5
+
6
+ /** Chat SDK verbs; the legacy policy grants them to the lead only. */
7
+ export type ChatVerb = "integration.chat.post" | "integration.chat.read" | "integration.chat.dm";
8
+
9
+ /**
10
+ * The Chat SDK thread of the task the caller works on: the task named by the
11
+ * X-Source-Task-ID header, else the agent's current task. Null when the caller
12
+ * neither owns nor created that task, or when the task did not come from a
13
+ * chat thread.
14
+ */
15
+ export async function callerChatThread(
16
+ requestInfo: RequestInfo,
17
+ ): Promise<{ adapter: string; threadId: string } | null> {
18
+ if (!requestInfo.agentId) return null;
19
+ const task = requestInfo.sourceTaskId
20
+ ? await getTaskById(requestInfo.sourceTaskId)
21
+ : await getAgentCurrentTask(requestInfo.agentId);
22
+ if (!task) return null;
23
+ // Creator is safe: a non-lead agent can only create a chat-bound task under a parent it owns or created.
24
+ if (task.agentId !== requestInfo.agentId && task.creatorAgentId !== requestInfo.agentId) {
25
+ return null;
26
+ }
27
+ return chatThreadOfContextKey(task.contextKey);
28
+ }
29
+
30
+ /** RBAC decision for a chat verb, with the caller's lead flag read from the database. */
31
+ export async function canUseChatVerb(requestInfo: RequestInfo, verb: ChatVerb): Promise<boolean> {
32
+ if (!requestInfo.agentId) return false;
33
+ const agent = await getAgentById(requestInfo.agentId);
34
+ if (!agent) return false;
35
+ return can({
36
+ principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
37
+ verb,
38
+ resource: { kind: "none" },
39
+ source: "mcp",
40
+ }).allow;
41
+ }
@@ -0,0 +1,59 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { CHAT_SDK_ADAPTER_NAMES } from "@/integrations/chat-sdk/config";
4
+ import { errorMessage } from "@/integrations/chat-sdk/logger";
5
+ import { getChatInstance } from "@/integrations/chat-sdk/runtime";
6
+ import { assertTrustedTeamsServiceUrl } from "@/integrations/chat-sdk/teams-origin";
7
+ import { canUseChatVerb } from "@/tools/chat-access";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
+
10
+ export const registerChatOpenDmTool = (server: McpServer) => {
11
+ createToolRegistrar(server)(
12
+ "chat-open-dm",
13
+ {
14
+ title: "Open a direct message on a chat platform",
15
+ description:
16
+ "Lead only. Open (or find) the direct-message thread with a user on a Chat SDK adapter and return its threadId. Post to it with chat-post. The userId is the platform user id from a chat message (Telegram numeric id, Slack U..., Discord snowflake, Teams 29:...). Telegram bots can message only users who started a chat with the bot.",
17
+ annotations: { openWorldHint: true },
18
+ inputSchema: z.object({
19
+ adapter: z.enum(CHAT_SDK_ADAPTER_NAMES).describe("Chat SDK adapter name."),
20
+ userId: z.string().min(1).describe("Platform user id."),
21
+ }),
22
+ outputSchema: swarmToolOutputSchema({
23
+ yourAgentId: z.string().optional(),
24
+ adapter: z.string().optional(),
25
+ threadId: z.string().optional(),
26
+ }),
27
+ },
28
+ async ({ adapter, userId }, requestInfo) => {
29
+ const data = { yourAgentId: requestInfo.agentId };
30
+ if (!(await canUseChatVerb(requestInfo, "integration.chat.dm"))) {
31
+ return toolErr("Permission denied. Only the lead can open a direct message.", { data });
32
+ }
33
+ const chat = getChatInstance(adapter);
34
+ if (!chat) return toolErr(`Chat SDK adapter '${adapter}' is not running.`, { data });
35
+ // Call the adapter directly: Chat.openDM guesses the adapter from the
36
+ // user id format, and numeric ids are ambiguous.
37
+ const platform = chat.getAdapter(adapter);
38
+ if (!platform?.openDM) {
39
+ return toolErr(`The ${adapter} adapter does not support direct messages.`, { data });
40
+ }
41
+ try {
42
+ if (adapter === "teams") {
43
+ // Teams opens the DM at the service URL cached from the user's last
44
+ // activity (adapter state key). The webhook route refuses untrusted
45
+ // ones; check again before the bot token goes out.
46
+ const cached = await chat.getState().get<string>(`teams:serviceUrl:${userId}`);
47
+ if (cached) assertTrustedTeamsServiceUrl(cached);
48
+ }
49
+ const threadId = await platform.openDM(userId);
50
+ return toolOk(`Direct-message thread with ${userId} on ${adapter}: ${threadId}.`, {
51
+ data: { ...data, adapter, threadId },
52
+ });
53
+ } catch (error) {
54
+ const message = errorMessage(error);
55
+ return toolErr(`Could not open a direct message: ${message}`, { data });
56
+ }
57
+ },
58
+ );
59
+ };
@@ -0,0 +1,74 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { CHAT_SDK_ADAPTER_NAMES } from "@/integrations/chat-sdk/config";
4
+ import { errorMessage } from "@/integrations/chat-sdk/logger";
5
+ import { adapterOfChatId, postChatMessage } from "@/integrations/chat-sdk/outbound";
6
+ import { canUseChatVerb } from "@/tools/chat-access";
7
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
8
+
9
+ export const registerChatPostTool = (server: McpServer) => {
10
+ createToolRegistrar(server)(
11
+ "chat-post",
12
+ {
13
+ title: "Post to a chat thread or channel",
14
+ description:
15
+ "Lead only. Post a message to any Chat SDK thread or channel (Telegram, Slack, Discord, Teams), outside the thread of your current task. Give threadId to reply in a thread, or channelId to start a new message in a channel. Ids carry the adapter prefix, such as telegram:12345. To answer the user of your current chat task, use chat-reply instead.",
16
+ annotations: { openWorldHint: true },
17
+ inputSchema: z.object({
18
+ adapter: z.enum(CHAT_SDK_ADAPTER_NAMES).describe("Chat SDK adapter name."),
19
+ threadId: z
20
+ .string()
21
+ .min(1)
22
+ .optional()
23
+ .describe("Full SDK thread id, such as telegram:12345 or slack:C123:1700000000.000100."),
24
+ channelId: z
25
+ .string()
26
+ .min(1)
27
+ .optional()
28
+ .describe("Full SDK channel id, such as slack:C123. Ignored when threadId is set."),
29
+ text: z.string().min(1).max(20_000).describe("Markdown text to post."),
30
+ }),
31
+ outputSchema: swarmToolOutputSchema({
32
+ yourAgentId: z.string().optional(),
33
+ messageId: z.string().optional(),
34
+ adapter: z.string().optional(),
35
+ threadId: z.string().optional(),
36
+ channelId: z.string().optional(),
37
+ }),
38
+ },
39
+ async ({ adapter, threadId, channelId, text }, requestInfo) => {
40
+ const data = { yourAgentId: requestInfo.agentId };
41
+ const target = threadId ?? channelId;
42
+ if (!target) return toolErr("Give a threadId or a channelId.", { data });
43
+ if (adapterOfChatId(target) !== adapter) {
44
+ return toolErr(`The id ${target} does not belong to the ${adapter} adapter.`, { data });
45
+ }
46
+ if (!(await canUseChatVerb(requestInfo, "integration.chat.post"))) {
47
+ return toolErr(
48
+ "Permission denied. Only the lead can post outside the thread of its task. Use chat-reply for the thread of your current task.",
49
+ { data },
50
+ );
51
+ }
52
+ try {
53
+ const sent = await postChatMessage({
54
+ adapter,
55
+ ...(threadId ? { threadId } : { channelId }),
56
+ text,
57
+ taskId: requestInfo.sourceTaskId,
58
+ });
59
+ return toolOk(`Posted to ${adapter} ${threadId ? "thread" : "channel"} ${target}.`, {
60
+ data: {
61
+ ...data,
62
+ messageId: sent.messageIds.at(-1),
63
+ adapter: sent.adapter,
64
+ threadId: sent.threadId,
65
+ channelId: sent.channelId,
66
+ },
67
+ });
68
+ } catch (error) {
69
+ const message = errorMessage(error);
70
+ return toolErr(`Could not post: ${message}`, { data });
71
+ }
72
+ },
73
+ );
74
+ };
@@ -0,0 +1,100 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { errorMessage } from "@/integrations/chat-sdk/logger";
4
+ import { adapterOfChatId } from "@/integrations/chat-sdk/outbound";
5
+ import { getChatInstance } from "@/integrations/chat-sdk/runtime";
6
+ import { assertTrustedChatTarget } from "@/integrations/chat-sdk/teams-origin";
7
+ import { callerChatThread, canUseChatVerb } from "@/tools/chat-access";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
+ import { scrubSecrets } from "@/utils/secret-scrubber";
10
+
11
+ const DEFAULT_LIMIT = 30;
12
+
13
+ export const registerChatReadThreadTool = (server: McpServer) => {
14
+ createToolRegistrar(server)(
15
+ "chat-read-thread",
16
+ {
17
+ title: "Read recent messages of a chat thread",
18
+ description:
19
+ "Read the most recent messages of a Chat SDK thread, oldest first. Every agent can read the thread of its current task. Other threads are lead only. Limits per platform: Telegram has no history API, so you get the history the SDK stored in the swarm database, capped at 100 messages and 7 days. Teams needs the Chat.Read.* Graph permission. Slack and Discord return platform history.",
20
+ annotations: { readOnlyHint: true, openWorldHint: true },
21
+ inputSchema: z.object({
22
+ threadId: z
23
+ .string()
24
+ .min(1)
25
+ .describe("Full SDK thread id with the adapter prefix, such as telegram:12345."),
26
+ limit: z
27
+ .number()
28
+ .int()
29
+ .min(1)
30
+ .max(100)
31
+ .optional()
32
+ .describe(`Number of messages to return (default ${DEFAULT_LIMIT}).`),
33
+ }),
34
+ outputSchema: swarmToolOutputSchema({
35
+ yourAgentId: z.string().optional(),
36
+ threadId: z.string().optional(),
37
+ messages: z
38
+ .array(
39
+ z.looseObject({
40
+ id: z.string().optional(),
41
+ userId: z.string().optional(),
42
+ userName: z.string().optional(),
43
+ isBot: z.boolean().optional(),
44
+ sentAt: z.string().optional(),
45
+ text: z.string().optional(),
46
+ }),
47
+ )
48
+ .optional(),
49
+ }),
50
+ },
51
+ async ({ threadId, limit }, requestInfo) => {
52
+ const data = { yourAgentId: requestInfo.agentId };
53
+ // A worker reads only the thread its task came from; any other thread
54
+ // needs the lead verb, as slack-read does for direct channel reads.
55
+ const own = await callerChatThread(requestInfo);
56
+ if (own?.threadId !== threadId) {
57
+ if (!(await canUseChatVerb(requestInfo, "integration.chat.read"))) {
58
+ return toolErr(
59
+ own
60
+ ? `Permission denied. You can read only the thread of your current task, ${own.threadId}. Other threads need lead privileges.`
61
+ : "Permission denied. Reading a chat thread that is not the thread of your current task needs lead privileges.",
62
+ { data },
63
+ );
64
+ }
65
+ }
66
+ const adapter = adapterOfChatId(threadId);
67
+ const chat = getChatInstance(adapter);
68
+ if (!chat) return toolErr(`Chat SDK adapter '${adapter}' is not running.`, { data });
69
+ try {
70
+ // Teams reads history with the bot token from the service URL in the id.
71
+ assertTrustedChatTarget(threadId);
72
+ const page = await chat.history.thread.list(threadId, {
73
+ limit: limit ?? DEFAULT_LIMIT,
74
+ direction: "backward",
75
+ });
76
+ const messages = page.messages.map((message) => ({
77
+ id: message.id,
78
+ userId: message.author.userId,
79
+ userName: message.author.userName || message.author.fullName || undefined,
80
+ isBot: message.author.isBot === true,
81
+ sentAt: message.metadata?.dateSent?.toISOString(),
82
+ text: scrubSecrets(message.text ?? ""),
83
+ }));
84
+ const rendered = messages
85
+ .map(
86
+ (m) =>
87
+ `[${m.sentAt ?? "?"}] ${m.userName ?? m.userId}${m.isBot ? " (bot)" : ""}: ${m.text}`,
88
+ )
89
+ .join("\n");
90
+ return toolOk(`${messages.length} message(s) from ${threadId}.`, {
91
+ details: rendered || undefined,
92
+ data: { ...data, threadId, messages },
93
+ });
94
+ } catch (error) {
95
+ const message = errorMessage(error);
96
+ return toolErr(`Could not read the thread: ${message}`, { data });
97
+ }
98
+ },
99
+ );
100
+ };
@@ -0,0 +1,75 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { getAgentCurrentTask, getTaskById } from "@/be/db";
4
+ import { errorMessage } from "@/integrations/chat-sdk/logger";
5
+ import { ChatAdapterUnavailableError, postChatMessage } from "@/integrations/chat-sdk/outbound";
6
+ import { chatThreadOfContextKey } from "@/tasks/context-key";
7
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
8
+
9
+ export const registerChatReplyTool = (server: McpServer) => {
10
+ createToolRegistrar(server)(
11
+ "chat-reply",
12
+ {
13
+ title: "Reply in the chat thread of this task",
14
+ description:
15
+ "Post a message to the Telegram, Slack, Discord or Teams thread your current task came from. Works only on a task whose contextKey starts with task:chat-sdk:. The task's final output is posted to the thread automatically when the task ends, so use this only for a question to the user or a short progress note on long work. Markdown is supported; long text is split into several messages.",
16
+ annotations: { openWorldHint: true },
17
+ inputSchema: z.object({
18
+ text: z.string().min(1).max(20_000).describe("Markdown text to post to the thread."),
19
+ }),
20
+ outputSchema: swarmToolOutputSchema({
21
+ yourAgentId: z.string().optional(),
22
+ messageId: z.string().optional(),
23
+ adapter: z.string().optional(),
24
+ threadId: z.string().optional(),
25
+ }),
26
+ },
27
+ async ({ text }, requestInfo) => {
28
+ if (!requestInfo.agentId) return toolErr("Agent ID not found.");
29
+ const data = { yourAgentId: requestInfo.agentId };
30
+
31
+ // The header names the task this session works on; without it, the
32
+ // agent's current in-progress task.
33
+ const task = requestInfo.sourceTaskId
34
+ ? await getTaskById(requestInfo.sourceTaskId)
35
+ : await getAgentCurrentTask(requestInfo.agentId);
36
+ if (!task) return toolErr("No current task found for chat-reply.", { data });
37
+ // Creator is safe: a non-lead agent can only create a chat-bound task under a parent it owns or created.
38
+ if (task.agentId !== requestInfo.agentId && task.creatorAgentId !== requestInfo.agentId) {
39
+ return toolErr("You don't have context for this task.", { data });
40
+ }
41
+ const thread = chatThreadOfContextKey(task.contextKey);
42
+ if (!thread) {
43
+ return toolErr(
44
+ `Task ${task.id} did not come from a chat thread (no task:chat-sdk contextKey). Use the tool of the channel the task came from.`,
45
+ { data },
46
+ );
47
+ }
48
+
49
+ try {
50
+ const sent = await postChatMessage({
51
+ adapter: thread.adapter,
52
+ threadId: thread.threadId,
53
+ text,
54
+ taskId: task.id,
55
+ });
56
+ return toolOk(`Posted to ${thread.adapter} thread ${sent.threadId}.`, {
57
+ data: {
58
+ ...data,
59
+ messageId: sent.messageIds.at(-1),
60
+ adapter: sent.adapter,
61
+ threadId: sent.threadId,
62
+ },
63
+ });
64
+ } catch (error) {
65
+ const message = errorMessage(error);
66
+ return toolErr(
67
+ error instanceof ChatAdapterUnavailableError
68
+ ? `${message}. The final task output is still posted when the adapter is back.`
69
+ : `Could not post to the chat thread: ${message}`,
70
+ { data },
71
+ );
72
+ }
73
+ },
74
+ );
75
+ };
@@ -21,7 +21,8 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
21
21
  import * as z from "zod";
22
22
  import { authorizeAssetKeyWrite } from "@/be/asset-key-auth";
23
23
  import { resolveTaskAuditUserId } from "@/be/audit-user";
24
- import { createPage, getPage, getPageBySlug, getPageVersions, updatePage } from "@/be/db";
24
+ import { createPage, getPage, getPageBySlug, updatePage } from "@/be/db";
25
+ import { getPageEditCounter } from "@/be/db/page-versions";
25
26
  import { snapshotPage } from "@/pages/version";
26
27
  import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
27
28
  import { AssetKeySchema, PageAuthModeSchema, PageContentTypeSchema } from "@/types";
@@ -45,17 +46,6 @@ function getAppBaseUrl(): string {
45
46
  return getAppUrl();
46
47
  }
47
48
 
48
- /**
49
- * Edit counter for a page — `MAX(page_versions.version) + 1`. Returned to
50
- * the agent as `version` so they have a monotonic "this page has been
51
- * edited N times" signal. Mirrors the value returned by
52
- * `PUT /api/pages/:id` (see src/http/pages.ts:pageEditCounter).
53
- */
54
- async function pageEditCounter(pageId: string): Promise<number> {
55
- const versions = await getPageVersions(pageId);
56
- return versions.length > 0 ? versions[0]!.version + 1 : 1;
57
- }
58
-
59
49
  export const registerCreatePageTool = (server: McpServer) => {
60
50
  createToolRegistrar(server)(
61
51
  "create_page",
@@ -217,7 +207,7 @@ export const registerCreatePageTool = (server: McpServer) => {
217
207
 
218
208
  const apiUrl = `${getApiBaseUrl()}/p/${id}`;
219
209
  const appUrl = `${getAppBaseUrl()}/pages/${id}`;
220
- const version = await pageEditCounter(id);
210
+ const version = await getPageEditCounter(id);
221
211
 
222
212
  return toolOk(`Page "${input.title}" saved (slug=${finalSlug}, version=${version}).`, {
223
213
  details: `API: ${apiUrl}\n App: ${appUrl}`,
@@ -1,3 +1,4 @@
1
+ import { openAICompatibleProviderIds } from "@desplega/model-catalog";
1
2
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
3
  import * as z from "zod";
3
4
  import { getAgentById } from "@/be/db";
@@ -86,7 +87,7 @@ export const registerModelCatalogOverlayUpsertTool = (server: McpServer) => {
86
87
  .string()
87
88
  .min(1)
88
89
  .describe(
89
- "models.dev provider id: anthropic, openai, openrouter, amazon-bedrock, opencode.",
90
+ `models.dev provider id: anthropic, openai, openrouter, amazon-bedrock, opencode, ${openAICompatibleProviderIds().join(", ")}.`,
90
91
  ),
91
92
  modelId: z
92
93
  .string()
@@ -0,0 +1,54 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { listModelProfiles } from "@/be/db/model-profiles";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolOk } from "@/tools/utils";
5
+ import { ProviderNameSchema } from "@/types";
6
+
7
+ export const registerListModelProfilesTool = (server: McpServer) => {
8
+ createToolRegistrar(server)(
9
+ "list-model-profiles",
10
+ {
11
+ title: "List Model Profiles",
12
+ description:
13
+ "List model profiles: named harness + model + effort combinations. Pass a profile's name as `profile` on send-task to run that task on it, or set it as an agent's default through PATCH /api/agents/{id}/runtime. A profile runs only on its own harness. Filter by `harness` to see what an agent on that harness can run.",
14
+ annotations: { readOnlyHint: true },
15
+ inputSchema: z.object({
16
+ harness: ProviderNameSchema.optional().describe(
17
+ "Only profiles for this harness (claude, codex, pi, ...).",
18
+ ),
19
+ }),
20
+ outputSchema: swarmToolOutputSchema({
21
+ profiles: z
22
+ .array(
23
+ z.looseObject({
24
+ name: z.string().optional(),
25
+ harness: z.string().optional(),
26
+ model: z.string().optional(),
27
+ effort: z.string().nullable().optional(),
28
+ description: z.string().nullable().optional(),
29
+ }),
30
+ )
31
+ .optional(),
32
+ }),
33
+ },
34
+ async ({ harness }) => {
35
+ const profiles = await listModelProfiles({ harness });
36
+ const lines = profiles.map(
37
+ (p) =>
38
+ `${p.name}: ${p.harness} / ${p.model}${p.effort ? ` / effort ${p.effort}` : ""}${p.description ? ` (${p.description})` : ""}`,
39
+ );
40
+ return toolOk(`${profiles.length} model profile(s)${harness ? ` for ${harness}` : ""}.`, {
41
+ details: lines.join("\n"),
42
+ data: {
43
+ profiles: profiles.map((p) => ({
44
+ name: p.name,
45
+ harness: p.harness,
46
+ model: p.model,
47
+ effort: p.effort,
48
+ description: p.description,
49
+ })),
50
+ },
51
+ });
52
+ },
53
+ );
54
+ };
@@ -0,0 +1,139 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { getAgentById, getWorkflow } from "@/be/db";
4
+ import { CHAT_SDK_ADAPTER_NAMES } from "@/integrations/chat-sdk/config";
5
+ import { errorMessage } from "@/integrations/chat-sdk/logger";
6
+ import {
7
+ type ChatChannelMapping,
8
+ chatChannelKey,
9
+ deleteChatChannelMapping,
10
+ putChatChannelMapping,
11
+ } from "@/integrations/chat-sdk/router";
12
+ import { can } from "@/rbac";
13
+ import {
14
+ createToolRegistrar,
15
+ type RequestInfo,
16
+ swarmToolOutputSchema,
17
+ toolErr,
18
+ toolOk,
19
+ } from "@/tools/utils";
20
+
21
+ async function canManageChatChannels(requestInfo: RequestInfo): Promise<boolean> {
22
+ const caller = requestInfo.agentId ? await getAgentById(requestInfo.agentId) : null;
23
+ return can({
24
+ principal: {
25
+ kind: "agent",
26
+ agentId: requestInfo.agentId ?? "",
27
+ isLead: caller?.isLead ?? false,
28
+ },
29
+ verb: "integration.chat.manage",
30
+ resource: { kind: "none" },
31
+ source: "mcp",
32
+ }).allow;
33
+ }
34
+
35
+ export const registerRegisterChatChannelTool = (server: McpServer) => {
36
+ createToolRegistrar(server)(
37
+ "register-chat-channel",
38
+ {
39
+ title: "Route a chat channel to an agent or workflow",
40
+ annotations: { idempotentHint: true },
41
+ description:
42
+ "Lead-only. Route every swarm-addressed message from one Chat SDK channel (a Telegram group, Slack channel, Discord channel or Teams chat) to a fixed agent or workflow instead of the lead. Give exactly one of agentId or workflowId. A swarm#<agentId> token in a message still wins over the mapping. Remove it with unregister-chat-channel.",
43
+ inputSchema: z.object({
44
+ adapter: z.enum(CHAT_SDK_ADAPTER_NAMES).describe("Chat SDK adapter name."),
45
+ channelId: z
46
+ .string()
47
+ .min(1)
48
+ .describe("SDK channel id, such as telegram:-100123 or slack:C123 (prefix optional)."),
49
+ agentId: z.string().optional().describe("Agent that receives the channel's tasks."),
50
+ workflowId: z
51
+ .string()
52
+ .optional()
53
+ .describe("Workflow whose webhook trigger receives the channel's messages."),
54
+ name: z.string().max(200).optional().describe("Display name for the mapping."),
55
+ }),
56
+ outputSchema: swarmToolOutputSchema({
57
+ yourAgentId: z.string().optional(),
58
+ key: z.string().optional(),
59
+ mapping: z
60
+ .looseObject({
61
+ agentId: z.string().optional(),
62
+ workflowId: z.string().optional(),
63
+ name: z.string().optional(),
64
+ createdAt: z.string().optional(),
65
+ })
66
+ .optional(),
67
+ }),
68
+ },
69
+ async ({ adapter, channelId, agentId, workflowId, name }, requestInfo) => {
70
+ const data = { yourAgentId: requestInfo.agentId };
71
+ try {
72
+ if (!(await canManageChatChannels(requestInfo))) {
73
+ return toolErr("Permission denied. Only the lead can register a chat channel.", {
74
+ data,
75
+ });
76
+ }
77
+ if (Boolean(agentId) === Boolean(workflowId)) {
78
+ return toolErr("Give exactly one of agentId or workflowId.", { data });
79
+ }
80
+ if (agentId && !(await getAgentById(agentId))) {
81
+ return toolErr(`Agent ${agentId} not found.`, { data });
82
+ }
83
+ if (workflowId && !(await getWorkflow(workflowId))) {
84
+ return toolErr(`Workflow ${workflowId} not found.`, { data });
85
+ }
86
+ const mapping: ChatChannelMapping = {
87
+ ...(agentId ? { agentId } : {}),
88
+ ...(workflowId ? { workflowId } : {}),
89
+ ...(name ? { name } : {}),
90
+ createdAt: new Date().toISOString(),
91
+ };
92
+ const key = await putChatChannelMapping(adapter, channelId, mapping);
93
+ return toolOk(
94
+ `Registered chat channel ${key} -> ${workflowId ? `workflow ${workflowId}` : `agent ${agentId}`}.`,
95
+ { data: { ...data, key, mapping } },
96
+ );
97
+ } catch (error) {
98
+ return toolErr(errorMessage(error), { data });
99
+ }
100
+ },
101
+ );
102
+ };
103
+
104
+ export const registerUnregisterChatChannelTool = (server: McpServer) => {
105
+ createToolRegistrar(server)(
106
+ "unregister-chat-channel",
107
+ {
108
+ title: "Remove a chat channel route",
109
+ annotations: { idempotentHint: true },
110
+ description:
111
+ "Lead-only. Remove the channel-to-agent or channel-to-workflow route that register-chat-channel stored. Messages from the channel go back to the default route (thread owner, then the lead).",
112
+ inputSchema: z.object({
113
+ adapter: z.enum(CHAT_SDK_ADAPTER_NAMES).describe("Chat SDK adapter name."),
114
+ channelId: z.string().min(1).describe("SDK channel id (prefix optional)."),
115
+ }),
116
+ outputSchema: swarmToolOutputSchema({
117
+ yourAgentId: z.string().optional(),
118
+ key: z.string().optional(),
119
+ }),
120
+ },
121
+ async ({ adapter, channelId }, requestInfo) => {
122
+ const data = { yourAgentId: requestInfo.agentId };
123
+ try {
124
+ if (!(await canManageChatChannels(requestInfo))) {
125
+ return toolErr("Permission denied. Only the lead can unregister a chat channel.", {
126
+ data,
127
+ });
128
+ }
129
+ const key = chatChannelKey(adapter, channelId);
130
+ const deleted = await deleteChatChannelMapping(adapter, channelId);
131
+ return deleted
132
+ ? toolOk(`Unregistered chat channel ${key}.`, { data: { ...data, key } })
133
+ : toolErr(`No mapping found for chat channel ${key}.`, { data: { ...data, key } });
134
+ } catch (error) {
135
+ return toolErr(errorMessage(error), { data });
136
+ }
137
+ },
138
+ );
139
+ };