@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
@@ -1,7 +1,9 @@
1
1
  import { describe, expect, mock, test } from "bun:test";
2
+ import { OPENAI_COMPATIBLE_PROVIDERS } from "@desplega/model-catalog";
2
3
  import {
3
4
  buildCredStatusReport,
4
5
  CREDENTIAL_PROVIDER_CHECKERS,
6
+ checkOpenAICompatibleProvider,
5
7
  checkProviderCredentials,
6
8
  isBedrockMode,
7
9
  isCredCheckDisabled,
@@ -652,6 +654,37 @@ describe("checkOpencodeCredentials", () => {
652
654
  ).toBe(true);
653
655
  });
654
656
 
657
+ test("strict: nan/* needs NAN_API_KEY or its HELMCODE_API_NAN alias", () => {
658
+ const env = { MODEL_OVERRIDE: "nan/glm5.3-flash", OPENROUTER_API_KEY: "x" };
659
+ const missing = checkOpencodeCredentials(env, { homeDir: HOME, fs: noFiles });
660
+ expect(missing.ready).toBe(false);
661
+ expect(missing.missing).toContain("NAN_API_KEY");
662
+ for (const name of ["NAN_API_KEY", "HELMCODE_API_NAN"]) {
663
+ expect(
664
+ checkOpencodeCredentials({ ...env, [name]: "x" }, { homeDir: HOME, fs: noFiles }).ready,
665
+ ).toBe(true);
666
+ }
667
+ });
668
+
669
+ test("strict: helmcode/* needs HELMCODE_API_KEY, not the NaN alias", () => {
670
+ const env = { MODEL_OVERRIDE: "helmcode/glm5.3", HELMCODE_API_NAN: "x" };
671
+ const missing = checkOpencodeCredentials(env, { homeDir: HOME, fs: noFiles });
672
+ expect(missing.ready).toBe(false);
673
+ expect(missing.missing).toContain("HELMCODE_API_KEY");
674
+ expect(
675
+ checkOpencodeCredentials({ ...env, HELMCODE_API_KEY: "x" }, { homeDir: HOME, fs: noFiles })
676
+ .ready,
677
+ ).toBe(true);
678
+ });
679
+
680
+ test("permissive: a NaN or Helmcode key alone is enough", () => {
681
+ for (const name of ["NAN_API_KEY", "HELMCODE_API_NAN", "HELMCODE_API_KEY"]) {
682
+ expect(checkOpencodeCredentials({ [name]: "x" }, { homeDir: HOME, fs: noFiles }).ready).toBe(
683
+ true,
684
+ );
685
+ }
686
+ });
687
+
655
688
  test("not ready when nothing is set", () => {
656
689
  const status = checkOpencodeCredentials({}, { homeDir: HOME, fs: noFiles });
657
690
  expect(status.ready).toBe(false);
@@ -923,6 +956,112 @@ describe("REQUIRED_CRED_VARS_BY_PROVIDER", () => {
923
956
  // acp is credential-free by design: the target process owns its own auth.
924
957
  expect(REQUIRED_CRED_VARS_BY_PROVIDER.acp).toEqual([]);
925
958
  });
959
+
960
+ test("pi and opencode list the NaN (both names) and Helmcode key vars", () => {
961
+ for (const p of ["pi", "opencode"] as const) {
962
+ expect(REQUIRED_CRED_VARS_BY_PROVIDER[p]).toEqual(
963
+ expect.arrayContaining(["NAN_API_KEY", "HELMCODE_API_NAN", "HELMCODE_API_KEY"]),
964
+ );
965
+ }
966
+ });
967
+ });
968
+
969
+ describe("NaN and Helmcode live probes", () => {
970
+ const envReady = { ready: true, missing: [], satisfiedBy: "env" as const };
971
+
972
+ function fakeProvider(status: number) {
973
+ const seen: Array<{ path: string; authorization: string | null }> = [];
974
+ const server = Bun.serve({
975
+ port: 0,
976
+ fetch(req) {
977
+ seen.push({
978
+ path: new URL(req.url).pathname,
979
+ authorization: req.headers.get("authorization"),
980
+ });
981
+ return new Response(status === 200 ? '{"data":[]}' : '{"error":"Invalid API key."}', {
982
+ status,
983
+ });
984
+ },
985
+ });
986
+ return { server, seen, baseUrl: `${server.url.toString().replace(/\/$/, "")}/v1` };
987
+ }
988
+
989
+ for (const [name, probe] of [
990
+ [
991
+ "nan",
992
+ (key: string, url: string) =>
993
+ checkOpenAICompatibleProvider(OPENAI_COMPATIBLE_PROVIDERS.nan, key, url),
994
+ ],
995
+ [
996
+ "helmcode",
997
+ (key: string, url: string) =>
998
+ checkOpenAICompatibleProvider(OPENAI_COMPATIBLE_PROVIDERS.helmcode, key, url),
999
+ ],
1000
+ ] as const) {
1001
+ test(`${name} probe lists models with a Bearer key at the injected base URL`, async () => {
1002
+ const ok = fakeProvider(200);
1003
+ const bad = fakeProvider(401);
1004
+ try {
1005
+ expect(await probe("fixture-key", `${ok.baseUrl}/`)).toMatchObject({ ok: true });
1006
+ expect(ok.seen).toEqual([{ path: "/v1/models", authorization: "Bearer fixture-key" }]);
1007
+ const failure = await probe("fixture-key", bad.baseUrl);
1008
+ expect(failure.ok).toBe(false);
1009
+ expect(failure.error).toContain("HTTP 401");
1010
+ } finally {
1011
+ ok.server.stop(true);
1012
+ bad.server.stop(true);
1013
+ }
1014
+ });
1015
+ }
1016
+
1017
+ for (const provider of ["pi", "opencode"] as const) {
1018
+ for (const [env, url] of [
1019
+ [{ NAN_API_KEY: "fixture-nan" }, "https://api.nan.builders/v1/models"],
1020
+ [{ HELMCODE_API_NAN: "fixture-nan" }, "https://api.nan.builders/v1/models"],
1021
+ [{ HELMCODE_API_KEY: "fixture-helm" }, "https://api.helmcode.com/v1/models"],
1022
+ // `<ID>_BASE_URL` moves the probe with the provider.
1023
+ [
1024
+ { NAN_API_KEY: "fixture-nan", NAN_BASE_URL: "https://nan-gw.example.test/v1/" },
1025
+ "https://nan-gw.example.test/v1/models",
1026
+ ],
1027
+ ] as const) {
1028
+ test(`${provider}: ${Object.keys(env).join("+")} is probed at ${url}`, async () => {
1029
+ const realFetch = globalThis.fetch;
1030
+ const fetchMock = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
1031
+ return new Response("{}", { status: 200 });
1032
+ });
1033
+ globalThis.fetch = fetchMock as typeof fetch;
1034
+ try {
1035
+ const result = await validateProviderCredentials(provider, { ...env }, envReady);
1036
+ expect(result.ok).toBe(true);
1037
+ expect(fetchMock).toHaveBeenCalledTimes(1);
1038
+ expect(String(fetchMock.mock.calls[0]?.[0])).toBe(url);
1039
+ const headers = fetchMock.mock.calls[0]?.[1]?.headers as Record<string, string>;
1040
+ expect(headers.Authorization).toBe(`Bearer ${Object.values(env)[0]}`);
1041
+ } finally {
1042
+ globalThis.fetch = realFetch;
1043
+ }
1044
+ });
1045
+ }
1046
+
1047
+ test(`${provider}: an OpenRouter key keeps the probe ahead of a NaN key`, async () => {
1048
+ const realFetch = globalThis.fetch;
1049
+ const fetchMock = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
1050
+ return new Response("{}", { status: 200 });
1051
+ });
1052
+ globalThis.fetch = fetchMock as typeof fetch;
1053
+ try {
1054
+ await validateProviderCredentials(
1055
+ provider,
1056
+ { OPENROUTER_API_KEY: "fixture-or", NAN_API_KEY: "fixture-nan" },
1057
+ envReady,
1058
+ );
1059
+ expect(String(fetchMock.mock.calls[0]?.[0])).not.toContain("nan.builders");
1060
+ } finally {
1061
+ globalThis.fetch = realFetch;
1062
+ }
1063
+ });
1064
+ }
926
1065
  });
927
1066
 
928
1067
  // ─── Migration 055: report composition + opt-out ────────────────────────────
@@ -84,4 +84,25 @@ describe("validateOpencodeCredentials", () => {
84
84
  validateOpencodeCredentials({ OPENROUTER_API_KEY: "sk-or", ANTHROPIC_API_KEY: "sk-ant" }),
85
85
  ).toBe("openrouter_api_key");
86
86
  });
87
+
88
+ test("accepts a NaN key under either name", () => {
89
+ expect(validateOpencodeCredentials({ NAN_API_KEY: "example-nan" })).toBe("nan_api_key");
90
+ expect(validateOpencodeCredentials({ HELMCODE_API_NAN: "example-nan" })).toBe("nan_api_key");
91
+ });
92
+
93
+ test("accepts a Helmcode key", () => {
94
+ expect(validateOpencodeCredentials({ HELMCODE_API_KEY: "example-helm" })).toBe(
95
+ "helmcode_api_key",
96
+ );
97
+ });
98
+
99
+ test("keeps OpenRouter ahead of NaN and Helmcode", () => {
100
+ expect(
101
+ validateOpencodeCredentials({
102
+ OPENROUTER_API_KEY: "sk-or",
103
+ NAN_API_KEY: "example-nan",
104
+ HELMCODE_API_KEY: "example-helm",
105
+ }),
106
+ ).toBe("openrouter_api_key");
107
+ });
87
108
  });
@@ -1,6 +1,11 @@
1
+ import { Database } from "bun:sqlite";
1
2
  import { afterEach, describe, expect, test } from "bun:test";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
2
6
  import * as facade from "../be/db";
3
7
  import * as runtime from "../be/db/runtime";
8
+ import { CHILD_PROCESS_TEST_BUDGET_MS, expectChildOk, runChild } from "./test-proc";
4
9
 
5
10
  const runtimeExports = [
6
11
  "__resetSqliteVecExtensionPathCacheForTests",
@@ -67,4 +72,60 @@ describe("database runtime facade", () => {
67
72
  globals.__testMigrationTemplate = template;
68
73
  }
69
74
  });
75
+
76
+ // rbac-admission-e2e restarted its server on an existing DB and the server
77
+ // died with "Encryption key not resolved yet": boot hit SQLITE_BUSY before
78
+ // resolving the key, and the db-client's BUSY retry reused the half-booted
79
+ // handle instead of booting again. Child processes boot cold (no test
80
+ // template) and leave no in-process connection blocking the lock below.
81
+ test(
82
+ "a cold boot that hits SQLITE_BUSY boots again before serving queries",
83
+ async () => {
84
+ const dir = mkdtempSync(join(tmpdir(), "db-runtime-busy-"));
85
+ const dbPath = join(dir, "boot.sqlite");
86
+ const child = (source: string) =>
87
+ runChild([process.execPath, "--eval", source], {
88
+ cwd: new URL("../../", import.meta.url).pathname,
89
+ env: { ...process.env, DATABASE_PATH: dbPath, AGENT_SWARM_TELEMETRY_DISABLED: "1" },
90
+ });
91
+ try {
92
+ expectChildOk(
93
+ await child(`
94
+ const db = await import("./src/be/db");
95
+ db.initDb(process.env.DATABASE_PATH);
96
+ await db.upsertSwarmConfig({ scope: "global", key: "BOOT_BUSY_SECRET", value: "boot-busy-value", isSecret: true });
97
+ db.closeDb();
98
+ `),
99
+ "seed",
100
+ );
101
+
102
+ const holder = new Database(dbPath);
103
+ holder.run("PRAGMA locking_mode = EXCLUSIVE");
104
+ holder.run("BEGIN EXCLUSIVE");
105
+ let released = false;
106
+ const releaseLock = () => {
107
+ if (released) return;
108
+ released = true;
109
+ holder.run("COMMIT");
110
+ holder.close();
111
+ };
112
+ const release = setTimeout(releaseLock, 500);
113
+ try {
114
+ const read = await child(`
115
+ const db = await import("./src/be/db");
116
+ const configs = await db.getInjectableGlobalConfigs();
117
+ console.log("VALUE=" + configs.find((c) => c.key === "BOOT_BUSY_SECRET")?.value);
118
+ `);
119
+ expect(read.stderr).not.toContain("Encryption key not resolved yet");
120
+ expect(read.stdout).toContain("VALUE=boot-busy-value");
121
+ } finally {
122
+ clearTimeout(release);
123
+ releaseLock();
124
+ }
125
+ } finally {
126
+ rmSync(dir, { recursive: true, force: true });
127
+ }
128
+ },
129
+ CHILD_PROCESS_TEST_BUDGET_MS * 2,
130
+ );
70
131
  });
@@ -72,6 +72,7 @@ type:McpServerFilters
72
72
  type:McpServerInsert
73
73
  type:MentionPreview
74
74
  type:PageListOptions
75
+ type:PendingChatRelay
75
76
  type:PlanSource
76
77
  type:RecordBudgetRefusalNotificationInput
77
78
  type:ScheduledTaskFilters
@@ -237,6 +238,7 @@ value:getAgentMcpServers
237
238
  value:getAgentSkills
238
239
  value:getAgentStatusCounts
239
240
  value:getAgentWithTasks
241
+ value:getAgentWorkingOnContextKey
240
242
  value:getAgentWorkingOnThread
241
243
  value:getAllAgentMailInboxMappings
242
244
  value:getAllAgents
@@ -298,8 +300,10 @@ value:getKv
298
300
  value:getLastReadAt
299
301
  value:getLastRunStart
300
302
  value:getLastSuccessfulRun
303
+ value:getLatestActiveTaskByContextKey
301
304
  value:getLatestActiveTaskInThread
302
305
  value:getLatestContextVersion
306
+ value:getLatestLeadTaskByContextKey
303
307
  value:getLatestLeadTaskInThread
304
308
  value:getLatestPageBySlug
305
309
  value:getLatestScriptRunStepTaskByContextKey
@@ -329,6 +333,7 @@ value:getPageVersion
329
333
  value:getPageVersions
330
334
  value:getPausedTasksForAgent
331
335
  value:getPendingApprovalVoteState
336
+ value:getPendingChatRelayTasks
332
337
  value:getPendingEventWaitNames
333
338
  value:getPendingSlackRelayTasks
334
339
  value:getPendingSteeringForAgent
@@ -426,6 +431,7 @@ value:getWorkflowRunStepsByRunId
426
431
  value:getWorkflowVersion
427
432
  value:getWorkflowVersions
428
433
  value:getWorkflowsByScheduleId
434
+ value:giveUpStaleChatRelays
429
435
  value:hasBudgetRefusalNotificationToday
430
436
  value:hasCapacity
431
437
  value:hasFirstCompletedTask
@@ -477,6 +483,9 @@ value:listUserFavorites
477
483
  value:listWorkflowRuns
478
484
  value:listWorkflowRunsPage
479
485
  value:listWorkflows
486
+ value:markChatRelayAttempted
487
+ value:markChatRelayDelivered
488
+ value:markChatRelayGivenUp
480
489
  value:markFinalizedSlackRelaysDelivered
481
490
  value:markInboxMessageDelegated
482
491
  value:markInboxMessageRead
@@ -505,6 +514,7 @@ value:promoteDraftTask
505
514
  value:reassociateSessionLogs
506
515
  value:recordApprovalVotes
507
516
  value:recordBudgetRefusalNotification
517
+ value:recordChatRelayError
508
518
  value:recordInlineScriptRun
509
519
  value:recordKeyAuthFailure
510
520
  value:recordKeyRateLimitWindows
@@ -598,6 +608,7 @@ value:upsertSwarmConfig
598
608
  value:withFavoriteFlags`.split("\n");
599
609
  const selected = `type:ExistingTrackerContextWork
600
610
  type:ExistingTrackerContextWorkReason
611
+ type:PendingChatRelay
601
612
  type:TaskFilters
602
613
  value:findCompletedTaskInThread
603
614
  value:findExistingLinearTrackerContextWork
@@ -605,17 +616,21 @@ value:findRecentCancelledTaskInThread
605
616
  value:findTaskByGitHub
606
617
  value:findTaskByVcs
607
618
  value:getAgentCurrentTask
619
+ value:getAgentWorkingOnContextKey
608
620
  value:getAgentWorkingOnThread
609
621
  value:getAllTasks
610
622
  value:getChildTasks
611
623
  value:getCompletedSlackTasks
612
624
  value:getInProgressSlackTasks
613
625
  value:getInProgressTasksByContextKey
626
+ value:getLatestActiveTaskByContextKey
614
627
  value:getLatestActiveTaskInThread
628
+ value:getLatestLeadTaskByContextKey
615
629
  value:getLatestLeadTaskInThread
616
630
  value:getLatestScriptRunStepTaskByContextKey
617
631
  value:getLatestTaskByContextKey
618
632
  value:getMostRecentTaskInThread
633
+ value:getPendingChatRelayTasks
619
634
  value:getPendingSlackRelayTasks
620
635
  value:getRecentlyFinishedWorkerTasks
621
636
  value:getSlackTasksMissingTree
@@ -626,13 +641,18 @@ value:getTaskStats
626
641
  value:getTasksByAgentId
627
642
  value:getTasksByStatus
628
643
  value:getTasksCount
644
+ value:giveUpStaleChatRelays
629
645
  value:hasNonTerminalRerouteDecisionChild
630
646
  value:hasNonTerminalResumeChild
631
647
  value:isLinearTrackerContextKey
648
+ value:markChatRelayAttempted
649
+ value:markChatRelayDelivered
650
+ value:markChatRelayGivenUp
632
651
  value:markFinalizedSlackRelaysDelivered
633
652
  value:markSlackRelayAttempted
634
653
  value:markSlackRelayDelivered
635
654
  value:markTasksNotified
655
+ value:recordChatRelayError
636
656
  value:resetTasksNotified`.split("\n");
637
657
  const internal = [
638
658
  "type:AgentTaskRow",
@@ -95,6 +95,7 @@ type:McpServerFilters
95
95
  type:McpServerInsert
96
96
  type:MentionPreview
97
97
  type:PageListOptions
98
+ type:PendingChatRelay
98
99
  type:PlanSource
99
100
  type:RecordBudgetRefusalNotificationInput
100
101
  type:ScheduledTaskFilters
@@ -260,6 +261,7 @@ value:getAgentMcpServers
260
261
  value:getAgentSkills
261
262
  value:getAgentStatusCounts
262
263
  value:getAgentWithTasks
264
+ value:getAgentWorkingOnContextKey
263
265
  value:getAgentWorkingOnThread
264
266
  value:getAllAgentMailInboxMappings
265
267
  value:getAllAgents
@@ -321,8 +323,10 @@ value:getKv
321
323
  value:getLastReadAt
322
324
  value:getLastRunStart
323
325
  value:getLastSuccessfulRun
326
+ value:getLatestActiveTaskByContextKey
324
327
  value:getLatestActiveTaskInThread
325
328
  value:getLatestContextVersion
329
+ value:getLatestLeadTaskByContextKey
326
330
  value:getLatestLeadTaskInThread
327
331
  value:getLatestPageBySlug
328
332
  value:getLatestScriptRunStepTaskByContextKey
@@ -352,6 +356,7 @@ value:getPageVersion
352
356
  value:getPageVersions
353
357
  value:getPausedTasksForAgent
354
358
  value:getPendingApprovalVoteState
359
+ value:getPendingChatRelayTasks
355
360
  value:getPendingEventWaitNames
356
361
  value:getPendingSlackRelayTasks
357
362
  value:getPendingSteeringForAgent
@@ -449,6 +454,7 @@ value:getWorkflowRunStepsByRunId
449
454
  value:getWorkflowVersion
450
455
  value:getWorkflowVersions
451
456
  value:getWorkflowsByScheduleId
457
+ value:giveUpStaleChatRelays
452
458
  value:hasBudgetRefusalNotificationToday
453
459
  value:hasCapacity
454
460
  value:hasFirstCompletedTask
@@ -500,6 +506,9 @@ value:listUserFavorites
500
506
  value:listWorkflowRuns
501
507
  value:listWorkflowRunsPage
502
508
  value:listWorkflows
509
+ value:markChatRelayAttempted
510
+ value:markChatRelayDelivered
511
+ value:markChatRelayGivenUp
503
512
  value:markFinalizedSlackRelaysDelivered
504
513
  value:markInboxMessageDelegated
505
514
  value:markInboxMessageRead
@@ -528,6 +537,7 @@ value:promoteDraftTask
528
537
  value:reassociateSessionLogs
529
538
  value:recordApprovalVotes
530
539
  value:recordBudgetRefusalNotification
540
+ value:recordChatRelayError
531
541
  value:recordInlineScriptRun
532
542
  value:recordKeyAuthFailure
533
543
  value:recordKeyRateLimitWindows
@@ -78,7 +78,7 @@ describe("docs API index footprint", () => {
78
78
  expect(tasks).toContain('"path":"/api/task-templates","method":"get"');
79
79
  });
80
80
 
81
- test("OpenAPI and generated pages keep all 400 operations", async () => {
81
+ test("OpenAPI and generated pages keep all 425 operations", async () => {
82
82
  const spec = await Bun.file(resolve(repoRoot, "openapi.json")).json();
83
83
  const operationKeys = [];
84
84
  const tags = new Set();
@@ -103,8 +103,8 @@ describe("docs API index footprint", () => {
103
103
  expect(spec.paths["/api/agents/{id}/runtime"]?.get).toBeDefined();
104
104
 
105
105
  const generatedKeys = await generatedOperationKeys();
106
- expect(operationKeys).toHaveLength(424);
107
- expect(generatedKeys).toHaveLength(424);
106
+ expect(operationKeys).toHaveLength(425);
107
+ expect(generatedKeys).toHaveLength(425);
108
108
  expect(new Set(generatedKeys)).toEqual(new Set(operationKeys));
109
109
  });
110
110
 
@@ -376,7 +376,7 @@ describe("dsh harness", () => {
376
376
  await rename(join(config.cwd, "dsh"), join(config.cwd, "npx"));
377
377
  config.env = { ...config.env, DSH_BINARY: "", PATH: config.cwd };
378
378
  await expect(new DshAdapter().createSession(config)).rejects.toThrow(
379
- "dsh CLI not found. Install @deepseek-ai/dsh@0.2.1-alpha.3",
379
+ "dsh CLI not found. Install @deepseek-ai/dsh@0.2.1-alpha.4",
380
380
  );
381
381
  expect(await Bun.file(join(config.cwd, "invocation.json")).exists()).toBe(false);
382
382
  });
@@ -0,0 +1,20 @@
1
+ import { afterEach, expect, test } from "bun:test";
2
+ import { ALL_CAPABILITIES, getEnabledCapabilities, hasCapability } from "../server";
3
+
4
+ const originalCapabilities = process.env.CAPABILITIES;
5
+
6
+ afterEach(() => {
7
+ if (originalCapabilities === undefined) delete process.env.CAPABILITIES;
8
+ else process.env.CAPABILITIES = originalCapabilities;
9
+ });
10
+
11
+ // `services` was a real capability until the PM2 service registry was removed.
12
+ // A deployment that still lists it must keep registering agents: the register
13
+ // response validates enabledCapabilities against ALL_CAPABILITIES.
14
+ test("enabledCapabilities drops retired and free-form names", () => {
15
+ process.env.CAPABILITIES = "core,services,artifacts,pages";
16
+
17
+ expect(ALL_CAPABILITIES as readonly string[]).not.toContain("services");
18
+ expect(getEnabledCapabilities()).toEqual(["core", "pages"]);
19
+ expect(hasCapability("pages")).toBe(true);
20
+ });
@@ -221,28 +221,34 @@ describe("wait_for_api_ready: immediate success", () => {
221
221
 
222
222
  describe("wait_for_api_ready: transient failure then success", () => {
223
223
  test("retries roughly once per second until the server recovers", async () => {
224
- let requestCount = 0;
224
+ // A per-test path prefix: an ephemeral port can be reused from another test
225
+ // process whose poller still hits /health, and those stray requests used
226
+ // to count as retries (3 requests in 1033 ms on CI).
227
+ const prefix = `/${crypto.randomUUID()}`;
228
+ const requestTimes: number[] = [];
225
229
  server = Bun.serve({
226
230
  port: 0,
227
231
  hostname: "127.0.0.1",
228
232
  fetch(req) {
229
233
  const url = new URL(req.url);
230
- if (url.pathname !== "/health") return new Response("not found", { status: 404 });
231
- requestCount += 1;
232
- if (requestCount < 3) return new Response("unavailable", { status: 503 });
234
+ if (url.pathname !== `${prefix}/health`) return new Response("not found", { status: 404 });
235
+ requestTimes.push(performance.now());
236
+ if (requestTimes.length < 3) return new Response("unavailable", { status: 503 });
233
237
  return new Response("ok", { status: 200 });
234
238
  },
235
239
  });
236
240
 
237
- const result = await runWaitForApiReady(server.url.toString(), {
241
+ const result = await runWaitForApiReady(`${server.url.origin}${prefix}/`, {
238
242
  WORKER_API_READY_TIMEOUT_SECONDS: "10",
239
243
  });
240
244
 
241
245
  expect(result.exitCode).toBe(0);
242
- expect(requestCount).toBeGreaterThanOrEqual(3);
246
+ expect(requestTimes).toHaveLength(3);
243
247
  expect(result.stdout).toContain("API ready at");
244
- // Two ~1s retries before success — bounded, not instant, not stalled.
245
- expect(result.durationMs).toBeGreaterThanOrEqual(1500);
248
+ // Each retry waits out `sleep 1` after the previous response, so the
249
+ // server-side gap cannot shrink under load: paced, not a hot loop.
250
+ expect(requestTimes[1]! - requestTimes[0]!).toBeGreaterThanOrEqual(900);
251
+ expect(requestTimes[2]! - requestTimes[1]!).toBeGreaterThanOrEqual(900);
246
252
  expect(result.durationMs).toBeLessThan(15000);
247
253
  }, 20000);
248
254
  });
@@ -93,7 +93,10 @@ function extractExportFilter(lineMarker: string): string {
93
93
  const line = script.split("\n").find((l) => l.includes(lineMarker) && l.includes("jq -r '"));
94
94
  if (!line) throw new Error(`Could not locate the jq line containing ${lineMarker}`);
95
95
  const start = line.indexOf("jq -r '") + "jq -r '".length;
96
- const end = line.indexOf("' /tmp/swarm_config.json", start);
96
+ // Inline programs read /tmp/swarm_config.json; the swarm_config_env_lines
97
+ // function reads its "$1" argument.
98
+ const ends = ["' /tmp/swarm_config.json", '\' "$1"'].map((tail) => line.indexOf(tail, start));
99
+ const end = ends.find((index) => index !== -1) ?? -1;
97
100
  if (end === -1) throw new Error(`Could not locate the end of the jq program for ${lineMarker}`);
98
101
  return line.slice(start, end);
99
102
  }
@@ -127,7 +130,7 @@ describe("entrypoint config env export: real jq filter", () => {
127
130
  test(
128
131
  "never exports codex_oauth or codex_oauth_<n>, nor the runner-resolved keys",
129
132
  async () => {
130
- const lines = await runJq(extractExportFilter("> /tmp/swarm_config.env"), configs);
133
+ const lines = await runJq(extractExportFilter("(.value | @sh)"), configs);
131
134
  const keys = lines.map((l) => l.slice(0, l.indexOf("=")));
132
135
  expect(keys).toEqual(["NORMAL", "codex_oauth_extra"]);
133
136
  expect(lines.join("\n")).not.toContain("rt.secret");
@@ -0,0 +1,78 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { CHILD_PROCESS_TEST_BUDGET_MS, expectChildOk, runChild } from "./test-proc";
6
+
7
+ /**
8
+ * Runs the real swarm_config_env block from docker-entrypoint.sh (extracted
9
+ * between its BEGIN/END markers) against a /api/config/resolved fixture, the
10
+ * same way the entrypoint sources it, and reads the resulting env.
11
+ */
12
+
13
+ const entrypoint = readFileSync("docker-entrypoint.sh", "utf8");
14
+ const block = entrypoint.slice(
15
+ entrypoint.indexOf("# BEGIN swarm_config_env"),
16
+ entrypoint.indexOf("# END swarm_config_env"),
17
+ );
18
+
19
+ let dir = "";
20
+
21
+ beforeAll(() => {
22
+ dir = mkdtempSync(join(tmpdir(), "entrypoint-export-"));
23
+ });
24
+ afterAll(() => {
25
+ rmSync(dir, { recursive: true, force: true });
26
+ });
27
+
28
+ async function exportedEnv(configs: unknown[], containerEnv: Record<string, string> = {}) {
29
+ const fixture = join(dir, `${crypto.randomUUID()}.json`);
30
+ writeFileSync(fixture, JSON.stringify({ configs }));
31
+ const script = `${block}
32
+ swarm_config_env_lines "$1" > "$1.env"
33
+ set -a; . "$1.env"; set +a
34
+ for withheld_key in $(swarm_config_withheld_keys "$1"); do unset "$withheld_key"; done
35
+ env
36
+ `;
37
+ const result = await runChild(["bash", "-c", script, "bash", fixture], {
38
+ env: { PATH: process.env.PATH ?? "/usr/bin:/bin", ...containerEnv },
39
+ timeoutMs: CHILD_PROCESS_TEST_BUDGET_MS,
40
+ });
41
+ expectChildOk(result, "entrypoint export block");
42
+ return Object.fromEntries(
43
+ result.stdout
44
+ .split("\n")
45
+ .filter((line) => line.includes("="))
46
+ .map((line) => [line.slice(0, line.indexOf("=")), line.slice(line.indexOf("=") + 1)]),
47
+ );
48
+ }
49
+
50
+ describe("docker-entrypoint.sh swarm_config_env", () => {
51
+ test("markers are present", () => {
52
+ expect(block).toContain("swarm_config_env_lines()");
53
+ expect(block).toContain("swarm_config_withheld_keys()");
54
+ });
55
+
56
+ test("exports exportToEnv rows and drops withheld ones, including container copies", async () => {
57
+ const env = await exportedEnv(
58
+ [
59
+ { key: "GITHUB_TOKEN", value: "row-github", isSecret: true, exportToEnv: true },
60
+ { key: "VERCEL_TOKEN", value: "row-vercel", isSecret: true, exportToEnv: false },
61
+ { key: "QUOTED", value: "it's $HOME", isSecret: false, exportToEnv: true },
62
+ // A pre-215 API sends no flag: the row keeps exporting.
63
+ { key: "LEGACY", value: "row-legacy", isSecret: true },
64
+ { key: "CF-Access-Client-Id", value: "x", isSecret: true, exportToEnv: true },
65
+ { key: "codex_oauth_1", value: "x", isSecret: true, exportToEnv: true },
66
+ { key: "HARNESS_PROVIDER", value: "pi", isSecret: false, exportToEnv: true },
67
+ ],
68
+ { VERCEL_TOKEN: "container-vercel" },
69
+ );
70
+ expect(env.GITHUB_TOKEN).toBe("row-github");
71
+ expect(env.QUOTED).toBe("it's $HOME");
72
+ expect(env.LEGACY).toBe("row-legacy");
73
+ expect(env.VERCEL_TOKEN).toBeUndefined();
74
+ expect(env.codex_oauth_1).toBeUndefined();
75
+ expect(env.HARNESS_PROVIDER).toBeUndefined();
76
+ expect(Object.values(env)).not.toContain("row-vercel");
77
+ });
78
+ });