@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
@@ -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
@@ -109,6 +110,7 @@ value:NOT_EXTENSION_AGENT_SQL
109
110
  value:ReservedAgentRoleError
110
111
  value:SESSION_ACTIVITY_REFRESH_MIN_INTERVAL_MS
111
112
  value:SKILL_FILE_LIMITS
113
+ value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
112
114
  value:UNATTRIBUTED_USER_ID
113
115
  value:__resetSqliteVecExtensionPathCacheForTests
114
116
  value:abandonSlackOutcomeDelivery
@@ -237,6 +239,7 @@ value:getAgentMcpServers
237
239
  value:getAgentSkills
238
240
  value:getAgentStatusCounts
239
241
  value:getAgentWithTasks
242
+ value:getAgentWorkingOnContextKey
240
243
  value:getAgentWorkingOnThread
241
244
  value:getAllAgentMailInboxMappings
242
245
  value:getAllAgents
@@ -298,8 +301,10 @@ value:getKv
298
301
  value:getLastReadAt
299
302
  value:getLastRunStart
300
303
  value:getLastSuccessfulRun
304
+ value:getLatestActiveTaskByContextKey
301
305
  value:getLatestActiveTaskInThread
302
306
  value:getLatestContextVersion
307
+ value:getLatestLeadTaskByContextKey
303
308
  value:getLatestLeadTaskInThread
304
309
  value:getLatestPageBySlug
305
310
  value:getLatestScriptRunStepTaskByContextKey
@@ -329,6 +334,7 @@ value:getPageVersion
329
334
  value:getPageVersions
330
335
  value:getPausedTasksForAgent
331
336
  value:getPendingApprovalVoteState
337
+ value:getPendingChatRelayTasks
332
338
  value:getPendingEventWaitNames
333
339
  value:getPendingSlackRelayTasks
334
340
  value:getPendingSteeringForAgent
@@ -426,6 +432,7 @@ value:getWorkflowRunStepsByRunId
426
432
  value:getWorkflowVersion
427
433
  value:getWorkflowVersions
428
434
  value:getWorkflowsByScheduleId
435
+ value:giveUpStaleChatRelays
429
436
  value:hasBudgetRefusalNotificationToday
430
437
  value:hasCapacity
431
438
  value:hasFirstCompletedTask
@@ -477,6 +484,9 @@ value:listUserFavorites
477
484
  value:listWorkflowRuns
478
485
  value:listWorkflowRunsPage
479
486
  value:listWorkflows
487
+ value:markChatRelayAttempted
488
+ value:markChatRelayDelivered
489
+ value:markChatRelayGivenUp
480
490
  value:markFinalizedSlackRelaysDelivered
481
491
  value:markInboxMessageDelegated
482
492
  value:markInboxMessageRead
@@ -505,6 +515,7 @@ value:promoteDraftTask
505
515
  value:reassociateSessionLogs
506
516
  value:recordApprovalVotes
507
517
  value:recordBudgetRefusalNotification
518
+ value:recordChatRelayError
508
519
  value:recordInlineScriptRun
509
520
  value:recordKeyAuthFailure
510
521
  value:recordKeyRateLimitWindows
@@ -598,24 +609,30 @@ value:upsertSwarmConfig
598
609
  value:withFavoriteFlags`.split("\n");
599
610
  const selected = `type:ExistingTrackerContextWork
600
611
  type:ExistingTrackerContextWorkReason
612
+ type:PendingChatRelay
601
613
  type:TaskFilters
614
+ value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
602
615
  value:findCompletedTaskInThread
603
616
  value:findExistingLinearTrackerContextWork
604
617
  value:findRecentCancelledTaskInThread
605
618
  value:findTaskByGitHub
606
619
  value:findTaskByVcs
607
620
  value:getAgentCurrentTask
621
+ value:getAgentWorkingOnContextKey
608
622
  value:getAgentWorkingOnThread
609
623
  value:getAllTasks
610
624
  value:getChildTasks
611
625
  value:getCompletedSlackTasks
612
626
  value:getInProgressSlackTasks
613
627
  value:getInProgressTasksByContextKey
628
+ value:getLatestActiveTaskByContextKey
614
629
  value:getLatestActiveTaskInThread
630
+ value:getLatestLeadTaskByContextKey
615
631
  value:getLatestLeadTaskInThread
616
632
  value:getLatestScriptRunStepTaskByContextKey
617
633
  value:getLatestTaskByContextKey
618
634
  value:getMostRecentTaskInThread
635
+ value:getPendingChatRelayTasks
619
636
  value:getPendingSlackRelayTasks
620
637
  value:getRecentlyFinishedWorkerTasks
621
638
  value:getSlackTasksMissingTree
@@ -626,13 +643,18 @@ value:getTaskStats
626
643
  value:getTasksByAgentId
627
644
  value:getTasksByStatus
628
645
  value:getTasksCount
646
+ value:giveUpStaleChatRelays
629
647
  value:hasNonTerminalRerouteDecisionChild
630
648
  value:hasNonTerminalResumeChild
631
649
  value:isLinearTrackerContextKey
650
+ value:markChatRelayAttempted
651
+ value:markChatRelayDelivered
652
+ value:markChatRelayGivenUp
632
653
  value:markFinalizedSlackRelaysDelivered
633
654
  value:markSlackRelayAttempted
634
655
  value:markSlackRelayDelivered
635
656
  value:markTasksNotified
657
+ value:recordChatRelayError
636
658
  value:resetTasksNotified`.split("\n");
637
659
  const internal = [
638
660
  "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
@@ -132,6 +133,7 @@ value:NOT_EXTENSION_AGENT_SQL
132
133
  value:ReservedAgentRoleError
133
134
  value:SESSION_ACTIVITY_REFRESH_MIN_INTERVAL_MS
134
135
  value:SKILL_FILE_LIMITS
136
+ value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
135
137
  value:UNATTRIBUTED_USER_ID
136
138
  value:__resetSqliteVecExtensionPathCacheForTests
137
139
  value:abandonSlackOutcomeDelivery
@@ -260,6 +262,7 @@ value:getAgentMcpServers
260
262
  value:getAgentSkills
261
263
  value:getAgentStatusCounts
262
264
  value:getAgentWithTasks
265
+ value:getAgentWorkingOnContextKey
263
266
  value:getAgentWorkingOnThread
264
267
  value:getAllAgentMailInboxMappings
265
268
  value:getAllAgents
@@ -321,8 +324,10 @@ value:getKv
321
324
  value:getLastReadAt
322
325
  value:getLastRunStart
323
326
  value:getLastSuccessfulRun
327
+ value:getLatestActiveTaskByContextKey
324
328
  value:getLatestActiveTaskInThread
325
329
  value:getLatestContextVersion
330
+ value:getLatestLeadTaskByContextKey
326
331
  value:getLatestLeadTaskInThread
327
332
  value:getLatestPageBySlug
328
333
  value:getLatestScriptRunStepTaskByContextKey
@@ -352,6 +357,7 @@ value:getPageVersion
352
357
  value:getPageVersions
353
358
  value:getPausedTasksForAgent
354
359
  value:getPendingApprovalVoteState
360
+ value:getPendingChatRelayTasks
355
361
  value:getPendingEventWaitNames
356
362
  value:getPendingSlackRelayTasks
357
363
  value:getPendingSteeringForAgent
@@ -449,6 +455,7 @@ value:getWorkflowRunStepsByRunId
449
455
  value:getWorkflowVersion
450
456
  value:getWorkflowVersions
451
457
  value:getWorkflowsByScheduleId
458
+ value:giveUpStaleChatRelays
452
459
  value:hasBudgetRefusalNotificationToday
453
460
  value:hasCapacity
454
461
  value:hasFirstCompletedTask
@@ -500,6 +507,9 @@ value:listUserFavorites
500
507
  value:listWorkflowRuns
501
508
  value:listWorkflowRunsPage
502
509
  value:listWorkflows
510
+ value:markChatRelayAttempted
511
+ value:markChatRelayDelivered
512
+ value:markChatRelayGivenUp
503
513
  value:markFinalizedSlackRelaysDelivered
504
514
  value:markInboxMessageDelegated
505
515
  value:markInboxMessageRead
@@ -528,6 +538,7 @@ value:promoteDraftTask
528
538
  value:reassociateSessionLogs
529
539
  value:recordApprovalVotes
530
540
  value:recordBudgetRefusalNotification
541
+ value:recordChatRelayError
531
542
  value:recordInlineScriptRun
532
543
  value:recordKeyAuthFailure
533
544
  value:recordKeyRateLimitWindows
@@ -21,6 +21,10 @@ import {
21
21
  supersedeTask,
22
22
  updateScheduledTask,
23
23
  } from "../be/db";
24
+ import { installExtension, setExtensionState } from "../be/extensions/db";
25
+ import { listRegistered, registerLoaded, unregister } from "../extensions/dispatcher";
26
+ import { ensureExtensionAgent } from "../extensions/identity";
27
+ import { loadExtension } from "../extensions/loader";
24
28
  import {
25
29
  dispatchDeferredTaskWait,
26
30
  initDeferredTaskWaits,
@@ -32,6 +36,7 @@ import { dispatchScheduleTarget, executeSchedule } from "../scheduler/scheduler"
32
36
  import { createResumeFollowUp } from "../tasks/worker-follow-up";
33
37
  import { registerDeferTaskTool } from "../tools/defer-task";
34
38
  import type { AgentTask } from "../types";
39
+ import { loadBundleFixture } from "./fixtures/extensions/load";
35
40
 
36
41
  type Result = { structuredContent: { success: boolean; message: string; scheduleId?: string } };
37
42
  type Tool = {
@@ -69,7 +74,13 @@ beforeAll(async () => {
69
74
  "defer-task"
70
75
  ]!;
71
76
  });
72
- afterEach(() => stopDeferredTaskWaits());
77
+ afterEach(async () => {
78
+ stopDeferredTaskWaits();
79
+ for (const loaded of listRegistered()) {
80
+ unregister(loaded.record.id);
81
+ await loaded.dispose();
82
+ }
83
+ });
73
84
  afterAll(async () => {
74
85
  closeDb();
75
86
  templateGlobals.__testMigrationTemplate = savedTemplate;
@@ -172,6 +183,72 @@ async function multiFixture(mode?: "all" | "any", event: TaskWakeEvent = "settle
172
183
  }
173
184
 
174
185
  describe("defer-task wakeOn", () => {
186
+ async function ceilingHook(action: string) {
187
+ const bundle = await loadBundleFixture("minimal");
188
+ bundle.manifest = { ...bundle.manifest, name: `ceiling-${crypto.randomUUID()}` };
189
+ bundle.files["hooks.ts"] = `
190
+ import { block, modify } from "swarm-extension";
191
+ export default (api) => {
192
+ api.on("pre.task.create", () => ${action});
193
+ };
194
+ `;
195
+ const installed = await installExtension(bundle);
196
+ const record = await setExtensionState(installed.extension.id, {
197
+ agentId: await ensureExtensionAgent(installed.extension.name),
198
+ });
199
+ const loaded = await loadExtension({ record: record!, ...bundle });
200
+ registerLoaded(loaded);
201
+ return loaded;
202
+ }
203
+
204
+ test("ceiling honours pre.task.create blocks and leaves the wait pending", async () => {
205
+ const { schedule } = await fixture();
206
+ await ceilingHook('block("ceiling denied")');
207
+ await executeSchedule(schedule);
208
+ expect(await children(schedule.id)).toHaveLength(0);
209
+ expect(await waitState(schedule.id)).toMatchObject({ status: "pending", childTaskId: null });
210
+ expect(await getScheduledTaskById(schedule.id)).toMatchObject({
211
+ consecutiveErrors: 1,
212
+ lastErrorMessage: expect.stringContaining("ceiling denied"),
213
+ });
214
+ });
215
+
216
+ test("ceiling applies pre.task.create payload modifications", async () => {
217
+ const { schedule } = await fixture();
218
+ await ceilingHook('modify({ description: "modified ceiling wake" })');
219
+ await executeSchedule(schedule);
220
+ const found = await children(schedule.id);
221
+ expect(found).toHaveLength(1);
222
+ expect(found[0]!.task).toBe("modified ceiling wake");
223
+ expect(await waitState(schedule.id)).toMatchObject({ status: "fired", firedBy: "ceiling" });
224
+ });
225
+
226
+ test("a ceiling moved during pre-hook preparation cannot fire the stale occurrence", async () => {
227
+ const { schedule } = await fixture();
228
+ const nextRunAt = new Date(Date.now() + 120_000).toISOString();
229
+ const loaded = await ceilingHook('modify({ description: "stale preparation" })');
230
+ const handler = loaded.handlers[0]!;
231
+ const original = handler.handler;
232
+ handler.handler = async (event, ctx) => {
233
+ await updateScheduledTask(schedule.id, { nextRunAt });
234
+ return original(event, ctx);
235
+ };
236
+ await executeSchedule(schedule);
237
+ expect(await children(schedule.id)).toHaveLength(0);
238
+ expect(await waitState(schedule.id)).toMatchObject({ status: "pending" });
239
+ expect(await getScheduledTaskById(schedule.id)).toMatchObject({ enabled: true, nextRunAt });
240
+ });
241
+
242
+ test("a stale ceiling snapshot cannot fire after re-defer moves nextRunAt", async () => {
243
+ const { schedule } = await fixture();
244
+ const nextRunAt = new Date(Date.now() + 120_000).toISOString();
245
+ await updateScheduledTask(schedule.id, { nextRunAt });
246
+ await executeSchedule(schedule);
247
+ expect(await children(schedule.id)).toHaveLength(0);
248
+ expect(await waitState(schedule.id)).toMatchObject({ status: "pending" });
249
+ expect(await getScheduledTaskById(schedule.id)).toMatchObject({ enabled: true, nextRunAt });
250
+ });
251
+
175
252
  test.each([
176
253
  "completed",
177
254
  "failed",
@@ -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(430);
107
+ expect(generatedKeys).toHaveLength(430);
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.5",
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");