@desplega.ai/agent-swarm 1.172.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 (525) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-fpecx1g1.js → acp-adapter-sj2g0m84.js} +18 -17
  3. package/dist/{actions-pw87a1a8.js → actions-fegk44gr.js} +65 -35
  4. package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
  6. package/dist/{app-wmf0m843.js → app-kfvgbvzy.js} +19 -13
  7. package/dist/{assistant-emp1e108.js → assistant-z7zdy9ja.js} +65 -38
  8. package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-5c2z0mbe.js} +11 -9
  9. package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-j8xa181e.js} +11 -9
  10. package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-hnkn3rgt.js} +12 -10
  11. package/dist/{boot-seeds-8q637aew.js → boot-seeds-kwjq4d2z.js} +15 -13
  12. package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-9vmvpvgd.js} +20 -19
  13. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  14. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  15. package/dist/{claude-sdk-session-gsfsh662.js → claude-sdk-session-9jye6cxr.js} +12698 -11995
  16. package/dist/{cli-k46r9hmk.js → cli-03gedhng.js} +16 -14
  17. package/dist/{cli-kb4ehzx5.js → cli-1e4b6bzh.js} +7 -7
  18. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  19. package/dist/cli-2m1ktfzz.js +17795 -0
  20. package/dist/{cli-8vs9trd4.js → cli-3dj8k4vy.js} +1 -1
  21. package/dist/{cli-zxm9p3x4.js → cli-3fbmmawm.js} +5 -5
  22. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  23. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  24. package/dist/{cli-6jb2rf77.js → cli-5t4f29ez.js} +3 -3
  25. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  26. package/dist/cli-6cvfxs8r.js +47 -0
  27. package/dist/{cli-pj6x9qce.js → cli-6jyj59nb.js} +2 -2
  28. package/dist/{cli-388egyf6.js → cli-6razj6t4.js} +2 -2
  29. package/dist/{cli-qsztpfry.js → cli-74ahz61x.js} +2 -2
  30. package/dist/{cli-4nz8p1p5.js → cli-7x0g4sn1.js} +15474 -16082
  31. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  32. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  33. package/dist/cli-9traj3dn.js +638 -0
  34. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  35. package/dist/{cli-bf7485pm.js → cli-apx87jne.js} +2 -2
  36. package/dist/{cli-ztrv9y21.js → cli-bzszjf2p.js} +3689 -1081
  37. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  38. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  39. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  40. package/dist/{cli-1hgat6pa.js → cli-d5f9ed5c.js} +5 -5
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  43. package/dist/{cli-j6kpj9az.js → cli-ee9gtz70.js} +1553 -68
  44. package/dist/{cli-e0a9sk00.js → cli-eejsgvm4.js} +4 -4
  45. package/dist/{cli-tgpfnm6r.js → cli-ef1hg1wv.js} +518 -59
  46. package/dist/cli-f527ce74.js +781 -0
  47. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  48. package/dist/cli-hjwmhcg1.js +10622 -0
  49. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  50. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  51. package/dist/{cli-4fnb6884.js → cli-hvqbtna4.js} +7 -1259
  52. package/dist/{cli-pstq9888.js → cli-j0pfptdp.js} +220 -103
  53. package/dist/cli-jta2eyzc.js +619 -0
  54. package/dist/{cli-esnpehf5.js → cli-k382tshb.js} +12 -13
  55. package/dist/cli-k3dp5tpf.js +263 -0
  56. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  57. package/dist/cli-kceah1eh.js +92 -0
  58. package/dist/{cli-prrt9fa0.js → cli-ke896fcf.js} +10 -10
  59. package/dist/{cli-nv4zd0b9.js → cli-kt6zqg0e.js} +6 -6
  60. package/dist/{cli-g6t9texm.js → cli-m50t4309.js} +5 -18
  61. package/dist/cli-n2wq4j7m.js +6985 -0
  62. package/dist/{cli-c2pq2rjb.js → cli-n3hzg89b.js} +46 -15
  63. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  64. package/dist/{cli-5rv1j9ga.js → cli-ne236afy.js} +53 -7
  65. package/dist/cli-p0sg24zp.js +16 -0
  66. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  67. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  68. package/dist/{cli-g4f5pj26.js → cli-q0ke1yww.js} +111 -25
  69. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  70. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  71. package/dist/{cli-gj81e47t.js → cli-r2pnvj8g.js} +2 -2
  72. package/dist/{cli-r8smj8b8.js → cli-r62a35ea.js} +19 -3
  73. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  74. package/dist/cli-rhs9pz33.js +294 -0
  75. package/dist/{cli-nyhb1r6x.js → cli-s2sp957d.js} +52 -29
  76. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  77. package/dist/{cli-xczpdp9e.js → cli-se8w6wny.js} +6 -6
  78. package/dist/cli-skxvvpap.js +10140 -0
  79. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  80. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  81. package/dist/{cli-cf69s3ew.js → cli-tr4sxp68.js} +3 -3
  82. package/dist/{cli-8hzfb6xz.js → cli-tsde1hdr.js} +15 -15
  83. package/dist/{cli-cceskcvt.js → cli-txqb9s38.js} +8 -8
  84. package/dist/{cli-aspcaef3.js → cli-v1yb0rqb.js} +6 -6
  85. package/dist/{cli-j1pmc07z.js → cli-vatyhshy.js} +268 -40
  86. package/dist/{cli-04emfe2q.js → cli-vdach5a9.js} +5 -5
  87. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  88. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  89. package/dist/{cli-hwwvy36w.js → cli-wayw93nt.js} +3 -3
  90. package/dist/cli-wmgrkscj.js +23254 -0
  91. package/dist/{cli-gp1rtyvk.js → cli-z5jjpaxw.js} +29 -6
  92. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  93. package/dist/cli.js +32 -48
  94. package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-y81sssk0.js} +16 -15
  95. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  96. package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-8vx4j6a9.js} +16 -15
  97. package/dist/{commands-n0hshcvs.js → commands-kcpvpcrr.js} +21 -10
  98. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  99. package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-kwpvj3rf.js} +45 -38
  100. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  101. package/dist/{db-hkqvrdcb.js → db-k8ankj20.js} +13 -11
  102. package/dist/{db-4w7tmt1g.js → db-ww5d7jq3.js} +29 -9
  103. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  104. package/dist/{dispatcher-5sd69y14.js → dispatcher-e8gq5m68.js} +41 -34
  105. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  106. package/dist/{dsh-adapter-2h8m4cab.js → dsh-adapter-3epjm3af.js} +7 -6
  107. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  108. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  109. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  110. package/dist/external-channel-h3359cwn.js +52 -0
  111. package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-g8db7x0f.js} +5 -3
  112. package/dist/{google-vertex-3f1jk41f.js → google-vertex-r2692q0r.js} +5 -3
  113. package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-0d4x7gkx.js} +19 -18
  114. package/dist/{handlers-s20xr10r.js → handlers-an1gzv21.js} +44 -37
  115. package/dist/{heartbeat-3k31q4g2.js → heartbeat-6dakeb61.js} +46 -39
  116. package/dist/{hook-gdn6a2fn.js → hook-6v6cv6d5.js} +12 -24
  117. package/dist/{hook-ygwerrdh.js → hook-sa5xye4y.js} +13 -12
  118. package/dist/{http-jdteeg81.js → http-r7f3eky0.js} +1598 -279
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-e0snym4t.js → index-7ep4gzdk.js} +41 -34
  121. package/dist/index-9gw58n4k.js +193 -0
  122. package/dist/index-9j87mcm8.js +56305 -0
  123. package/dist/index-bsmcresk.js +79424 -0
  124. package/dist/index-mh85qhec.js +475 -0
  125. package/dist/{index-vm3bm9t7.js → index-rmpw9a8q.js} +41 -34
  126. package/dist/{index-6ksewd5y.js → index-s2wxgzr0.js} +1015 -732
  127. package/dist/{index-tsszt7g9.js → index-v1v6n7hg.js} +46 -39
  128. package/dist/index-we27kd31.js +12810 -0
  129. package/dist/{index-ffqzqgwb.js → index-zk96aq1k.js} +11 -9
  130. package/dist/{keepalive-jekc896t.js → keepalive-be9vwy5h.js} +13 -11
  131. package/dist/{lead-4k9s9szx.js → lead-0861cwrf.js} +64 -57
  132. package/dist/{maintenance-shazch4n.js → maintenance-mzzgc8bn.js} +11 -9
  133. package/dist/node-c9we95xf.js +1 -1
  134. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  135. package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-cpdnfwdj.js} +12 -10
  136. package/dist/{onboard-ydrbnvww.js → onboard-fh8qs4r3.js} +11 -3
  137. package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-nm42wwj8.js} +19 -14
  138. package/dist/{otel-impl-6pf5cv86.js → otel-impl-x6c1c9g4.js} +248 -869
  139. package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-9n2ya240.js} +245 -107
  140. package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-qb0rv0ap.js} +13 -11
  141. package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-1gzdwbh1.js} +12 -10
  142. package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-3mraey52.js} +13 -11
  143. package/dist/{render-v2-pd1rg54q.js → render-v2-agpa45zz.js} +19 -13
  144. package/dist/{resume-gw4a2c6v.js → resume-x0ydy9s9.js} +27 -21
  145. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  146. package/dist/{setup-jda1ksdh.js → setup-as9ga0d6.js} +2 -2
  147. package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-9gkxqrap.js} +7 -6
  148. package/dist/{work-objects-tb7ycye8.js → work-objects-y5g1y7ev.js} +49 -37
  149. package/dist/{worker-njjccz5d.js → worker-860c3e38.js} +64 -57
  150. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  151. package/openapi.json +951 -356
  152. package/package.json +12 -13
  153. package/src/apps/catalog.generated.json +1309 -53
  154. package/src/apps/page-validator.ts +93 -31
  155. package/src/azure-devops/api.ts +94 -18
  156. package/src/azure-devops/auth.ts +32 -0
  157. package/src/azure-devops/boards-sync.ts +708 -0
  158. package/src/azure-devops/handlers.ts +52 -8
  159. package/src/azure-devops/html.ts +26 -0
  160. package/src/azure-devops/index.ts +15 -0
  161. package/src/azure-devops/templates.ts +86 -0
  162. package/src/azure-devops/types.ts +65 -1
  163. package/src/be/config-refs.ts +60 -0
  164. package/src/be/db/page-versions.ts +15 -0
  165. package/src/be/db/runtime.ts +35 -8
  166. package/src/be/db/skill-name-resolution.ts +36 -0
  167. package/src/be/db/tasks/read.ts +230 -0
  168. package/src/be/db/tasks/write.ts +7 -0
  169. package/src/be/db.ts +74 -13
  170. package/src/be/migrations/213_script_connections_sql.sql +79 -0
  171. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  172. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  173. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  174. package/src/be/model-catalog-store.ts +5 -1
  175. package/src/be/model-tier-resolution.ts +12 -1
  176. package/src/be/model-validation.ts +5 -2
  177. package/src/be/models-catalog.ts +26 -1
  178. package/src/be/modelsdev-cache.json +198 -63
  179. package/src/be/modelsdev-cache.ts +9 -1
  180. package/src/be/script-connections.ts +47 -8
  181. package/src/be/script-credential-broker.ts +39 -2
  182. package/src/be/scripts/run-saved.ts +1 -0
  183. package/src/be/scripts/typecheck.ts +10 -8
  184. package/src/be/seed/runner.ts +13 -1
  185. package/src/be/seed/state-db.ts +5 -0
  186. package/src/be/seed/types.ts +6 -0
  187. package/src/be/seed-pricing.ts +19 -2
  188. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  189. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  190. package/src/be/seed-skills/bundled-files.generated.json +10 -18
  191. package/src/be/seed-skills/index.ts +107 -3
  192. package/src/be/sql-connections.ts +118 -0
  193. package/src/be/swarm-config-guard.ts +53 -0
  194. package/src/cli.tsx +0 -16
  195. package/src/commands/onboard/non-interactive.ts +4 -2
  196. package/src/commands/profile-sync.ts +5 -3
  197. package/src/commands/provider-credentials.ts +52 -6
  198. package/src/commands/runner.ts +196 -56
  199. package/src/e2b/env.ts +5 -0
  200. package/src/extensions/contract-types.generated.ts +1 -1
  201. package/src/extensions/contract.ts +45 -0
  202. package/src/extensions/dispatcher.ts +7 -0
  203. package/src/extensions/post-bridge.ts +40 -0
  204. package/src/github/handlers.ts +11 -4
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/all-routes.ts +0 -1
  208. package/src/http/config.ts +36 -17
  209. package/src/http/core.ts +4 -0
  210. package/src/http/index.ts +14 -2
  211. package/src/http/integrations.ts +88 -1
  212. package/src/http/mcp-oauth.ts +6 -13
  213. package/src/http/mcp.ts +15 -5
  214. package/src/http/page-proxy.ts +2 -4
  215. package/src/http/pages-public.ts +6 -5
  216. package/src/http/pages.ts +5 -14
  217. package/src/http/poll.ts +28 -68
  218. package/src/http/requested-by.ts +54 -0
  219. package/src/http/route-def.ts +7 -6
  220. package/src/http/script-connection-proxy.ts +51 -4
  221. package/src/http/script-connections.ts +5 -2
  222. package/src/http/scripts.ts +1 -0
  223. package/src/http/session-cost-recompute.ts +12 -2
  224. package/src/http/session-data.ts +4 -0
  225. package/src/http/stats.ts +1 -30
  226. package/src/http/tasks.ts +61 -5
  227. package/src/http/utils.ts +14 -0
  228. package/src/http/webhooks.ts +183 -2
  229. package/src/http/x.ts +1 -0
  230. package/src/integrations/chat-sdk/adapters.ts +129 -0
  231. package/src/integrations/chat-sdk/attachments.ts +232 -0
  232. package/src/integrations/chat-sdk/background.ts +36 -0
  233. package/src/integrations/chat-sdk/commands.ts +194 -0
  234. package/src/integrations/chat-sdk/config.ts +198 -0
  235. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  236. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  237. package/src/integrations/chat-sdk/handlers.ts +28 -0
  238. package/src/integrations/chat-sdk/inbound.ts +426 -0
  239. package/src/integrations/chat-sdk/limits.ts +48 -0
  240. package/src/integrations/chat-sdk/logger.ts +81 -0
  241. package/src/integrations/chat-sdk/outbound.ts +123 -0
  242. package/src/integrations/chat-sdk/relay.ts +172 -0
  243. package/src/integrations/chat-sdk/router.ts +169 -0
  244. package/src/integrations/chat-sdk/runtime.ts +351 -0
  245. package/src/integrations/chat-sdk/schema.ts +245 -0
  246. package/src/integrations/chat-sdk/state.ts +243 -0
  247. package/src/integrations/chat-sdk/status.ts +244 -0
  248. package/src/integrations/chat-sdk/steering.ts +58 -0
  249. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  250. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  251. package/src/integrations/chat-sdk/templates.ts +16 -0
  252. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  253. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  254. package/src/integrations/chat-sdk/typing.ts +65 -0
  255. package/src/jira/adf.ts +3 -1
  256. package/src/kv-reserved-namespaces.ts +2 -0
  257. package/src/oauth/mcp-wrapper.ts +10 -6
  258. package/src/otel-impl.ts +2 -1
  259. package/src/pages/version.ts +4 -5
  260. package/src/prompts/base-prompt.ts +111 -45
  261. package/src/providers/acp-target-catalog.ts +6 -0
  262. package/src/providers/acp-targets.ts +56 -3
  263. package/src/providers/amp-adapter.ts +1 -1
  264. package/src/providers/claude-adapter.ts +27 -2
  265. package/src/providers/claude-flag-settings.ts +133 -0
  266. package/src/providers/claude-managed-adapter.ts +1 -1
  267. package/src/providers/claude-sdk-session.ts +7 -0
  268. package/src/providers/claude-session-events.ts +6 -1
  269. package/src/providers/dsh-adapter.ts +1 -1
  270. package/src/providers/opencode-adapter.ts +268 -37
  271. package/src/providers/opencode-billing.ts +86 -0
  272. package/src/providers/opencode-swarm-plugin.ts +51 -0
  273. package/src/providers/pi-mono-adapter.ts +398 -153
  274. package/src/providers/pricing-sources.md +37 -3
  275. package/src/providers/reasoning-effort.ts +27 -0
  276. package/src/providers/types.ts +11 -2
  277. package/src/rbac/legacy-policy.ts +8 -2
  278. package/src/rbac/permissions.ts +21 -0
  279. package/src/scheduler/scheduler.ts +4 -2
  280. package/src/scripts-runtime/api-client.ts +15 -1
  281. package/src/scripts-runtime/api-types.ts +9 -1
  282. package/src/scripts-runtime/ctx.ts +1 -1
  283. package/src/scripts-runtime/loader.ts +19 -7
  284. package/src/scripts-runtime/mcp-client.ts +30 -19
  285. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  286. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  287. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  288. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  289. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  290. package/src/server.ts +52 -18
  291. package/src/slack/actions.ts +29 -1
  292. package/src/slack/app.ts +14 -2
  293. package/src/slack/assistant.ts +36 -2
  294. package/src/slack/channel-join.ts +3 -16
  295. package/src/slack/commands.ts +9 -1
  296. package/src/slack/event-dedup.ts +1 -1
  297. package/src/slack/external-channel-egress.ts +113 -0
  298. package/src/slack/external-channel.ts +309 -0
  299. package/src/slack/handlers.ts +61 -0
  300. package/src/slack/render-v2.ts +40 -47
  301. package/src/slack/work-objects.ts +5 -0
  302. package/src/tasks/additive-buffer.ts +4 -2
  303. package/src/tasks/context-key.ts +94 -2
  304. package/src/tasks/model-pin.ts +67 -0
  305. package/src/tasks/worker-follow-up.ts +4 -31
  306. package/src/telemetry.ts +2 -1
  307. package/src/tests/acp-adapter.test.ts +42 -0
  308. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  309. package/src/tests/apps-catalog-feedback.test.ts +310 -0
  310. package/src/tests/apps-catalog-inputs.test.ts +70 -0
  311. package/src/tests/apps-catalog-table.test.ts +113 -0
  312. package/src/tests/azure-devops-boards-sync.test.ts +584 -0
  313. package/src/tests/azure-devops-html.test.ts +13 -0
  314. package/src/tests/azure-devops-webhook-malformed.test.ts +4 -0
  315. package/src/tests/base-prompt.test.ts +58 -54
  316. package/src/tests/chat-sdk-commands.test.ts +191 -0
  317. package/src/tests/chat-sdk-config.test.ts +334 -0
  318. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  319. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  320. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  321. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  322. package/src/tests/chat-sdk-harness.ts +146 -0
  323. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  324. package/src/tests/chat-sdk-relay.test.ts +470 -0
  325. package/src/tests/chat-sdk-reload.test.ts +128 -0
  326. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  327. package/src/tests/chat-sdk-slack.test.ts +268 -0
  328. package/src/tests/chat-sdk-state.test.ts +163 -0
  329. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  330. package/src/tests/chat-sdk-teams.test.ts +277 -0
  331. package/src/tests/chat-sdk-tools.test.ts +520 -0
  332. package/src/tests/chat-sdk-typing.test.ts +120 -0
  333. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  334. package/src/tests/claude-adapter-binary.test.ts +144 -0
  335. package/src/tests/claude-flag-settings.test.ts +114 -0
  336. package/src/tests/claude-managed-adapter.test.ts +1 -1
  337. package/src/tests/claude-sdk-lifecycle.test.ts +148 -1
  338. package/src/tests/claude-sdk-process.fixture.ts +7 -0
  339. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  340. package/src/tests/claude-worker-parity.test.ts +2 -7
  341. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  342. package/src/tests/config-refs.test.ts +66 -0
  343. package/src/tests/context-key.test.ts +51 -0
  344. package/src/tests/cors-allowlist.test.ts +25 -0
  345. package/src/tests/create-page-tool.test.ts +18 -0
  346. package/src/tests/credential-check.test.ts +139 -0
  347. package/src/tests/credentials.test.ts +21 -0
  348. package/src/tests/db-runtime.test.ts +61 -0
  349. package/src/tests/db-task-read-contract.test.ts +20 -0
  350. package/src/tests/db-task-write-contract.test.ts +10 -0
  351. package/src/tests/deferred-task-wake.test.ts +52 -4
  352. package/src/tests/docs-index-footprint.test.js +3 -3
  353. package/src/tests/dsh-adapter.test.ts +1 -1
  354. package/src/tests/enabled-capabilities.test.ts +20 -0
  355. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  356. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  357. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  358. package/src/tests/explicit-model-validation.test.ts +105 -1
  359. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  360. package/src/tests/extensions-post-bridge.test.ts +51 -0
  361. package/src/tests/fetch-resolved-env.test.ts +128 -0
  362. package/src/tests/fixtures/azure-boards/workitem.commented.json +89 -0
  363. package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
  364. package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
  365. package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
  366. package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
  367. package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
  368. package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
  369. package/src/tests/fixtures/catalog-table-definition.json.txt +468 -0
  370. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  371. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  372. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  373. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  374. package/src/tests/fixtures/runner-identity-refresh.ts +3 -0
  375. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  376. package/src/tests/github-handlers.test.ts +35 -0
  377. package/src/tests/harness-provider-resolution.test.ts +12 -0
  378. package/src/tests/heartbeat-checklist.test.ts +3 -2
  379. package/src/tests/hook-runtime-close.test.ts +0 -2
  380. package/src/tests/http-api-integration.test.ts +0 -25
  381. package/src/tests/migration-046-budgets.test.ts +17 -16
  382. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  383. package/src/tests/model-tier-resolution.test.ts +93 -0
  384. package/src/tests/models-catalog.test.ts +42 -1
  385. package/src/tests/node-env-runtime.test.ts +98 -0
  386. package/src/tests/openai-compatible-providers.test.ts +134 -0
  387. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  388. package/src/tests/opencode-adapter.test.ts +348 -3
  389. package/src/tests/opencode-billing.test.ts +28 -0
  390. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  391. package/src/tests/opencode-shell-env.test.ts +145 -0
  392. package/src/tests/openrouter-base-url.test.ts +101 -151
  393. package/src/tests/page-proxy.test.ts +1 -1
  394. package/src/tests/pages-browser-sdk.test.ts +76 -0
  395. package/src/tests/pages-feedback.test.ts +1 -1
  396. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  397. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  398. package/src/tests/pi-mono-adapter.test.ts +385 -5
  399. package/src/tests/pi-shell-env.test.ts +170 -0
  400. package/src/tests/prompt-template-remaining.test.ts +35 -0
  401. package/src/tests/rbac-engine.test.ts +11 -3
  402. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  403. package/src/tests/runner-identity-refresh.test.ts +3 -0
  404. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  405. package/src/tests/script-connections-sql.test.ts +675 -0
  406. package/src/tests/script-connections.test.ts +7 -1
  407. package/src/tests/scripts-http.test.ts +201 -1
  408. package/src/tests/scripts-only-gating.test.ts +2 -3
  409. package/src/tests/scripts-runtime.test.ts +58 -40
  410. package/src/tests/secret-scrubber.test.ts +86 -0
  411. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  412. package/src/tests/seed-skills-retire.test.ts +182 -0
  413. package/src/tests/send-task-requester-gate.test.ts +58 -0
  414. package/src/tests/session-cost-recompute-unit.test.ts +94 -0
  415. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  416. package/src/tests/slack-channel-join.test.ts +4 -1
  417. package/src/tests/slack-external-channel-config.test.ts +65 -0
  418. package/src/tests/slack-external-channel-egress.test.ts +237 -0
  419. package/src/tests/slack-external-channel-guard.test.ts +280 -0
  420. package/src/tests/slack-external-channel-tools.test.ts +173 -0
  421. package/src/tests/slack-external-channel.test.ts +370 -0
  422. package/src/tests/slack-render-v2.test.ts +86 -10
  423. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  424. package/src/tests/spec-base-url.test.ts +16 -1
  425. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  426. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  427. package/src/tests/swarm-diff.test.ts +1 -1
  428. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  429. package/src/tests/task-session-prompts.test.ts +132 -0
  430. package/src/tests/task-tool-manifest.test.ts +14 -0
  431. package/src/tests/task-tools-ctx.test.ts +2 -0
  432. package/src/tests/tool-annotations.test.ts +6 -6
  433. package/src/tests/ui-logs-parser.test.ts +43 -0
  434. package/src/tests/used-by-route.test.ts +6 -0
  435. package/src/tests/vendored-openapi.test.ts +16 -1
  436. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  437. package/src/tests/workflow-swarm-script.test.ts +38 -0
  438. package/src/tests/workflow-wait-filter.test.ts +5 -3
  439. package/src/tools/chat-access.ts +41 -0
  440. package/src/tools/chat-open-dm.ts +59 -0
  441. package/src/tools/chat-post.ts +74 -0
  442. package/src/tools/chat-read-thread.ts +100 -0
  443. package/src/tools/chat-reply.ts +75 -0
  444. package/src/tools/create-page.ts +3 -13
  445. package/src/tools/defer-task.ts +17 -0
  446. package/src/tools/model-catalog/index.ts +2 -1
  447. package/src/tools/register-chat-channel.ts +139 -0
  448. package/src/tools/send-task.ts +24 -0
  449. package/src/tools/skills/skill-get.ts +17 -9
  450. package/src/tools/slack-post.ts +6 -0
  451. package/src/tools/slack-read.ts +8 -0
  452. package/src/tools/slack-reply.ts +6 -0
  453. package/src/tools/slack-start-thread.ts +6 -0
  454. package/src/tools/slack-update.ts +6 -0
  455. package/src/tools/slack-upload-file.ts +6 -0
  456. package/src/tools/swarm-config/get-config.ts +2 -1
  457. package/src/tools/swarm-config/list-config.ts +2 -1
  458. package/src/tools/swarm-config/set-config.ts +16 -3
  459. package/src/tools/tool-config.ts +8 -6
  460. package/src/types.ts +12 -2
  461. package/src/utils/config-env-export.ts +74 -0
  462. package/src/utils/credentials.test.ts +92 -0
  463. package/src/utils/credentials.ts +52 -4
  464. package/src/utils/harness-provider.ts +15 -6
  465. package/src/utils/node-env.ts +24 -0
  466. package/src/utils/runtime-model-catalog.ts +11 -1
  467. package/src/utils/secret-scrubber.ts +71 -2
  468. package/src/utils/task-tool-manifest.ts +19 -2
  469. package/src/workflows/executors/swarm-script.ts +1 -0
  470. package/templates/skills/agent-browser/SKILL.md +1 -1
  471. package/templates/skills/agent-browser/content.md +1 -1
  472. package/templates/skills/apps/SKILL.md +749 -0
  473. package/templates/skills/apps/content.md +60 -22
  474. package/templates/skills/chat-sdk/config.json +14 -0
  475. package/templates/skills/chat-sdk/content.md +84 -0
  476. package/templates/skills/composio-gmail/content.md +7 -0
  477. package/templates/skills/composio-google-docs/content.md +1 -1
  478. package/templates/skills/email-domain-health/config.json +14 -0
  479. package/templates/skills/email-domain-health/content.md +131 -0
  480. package/templates/skills/kv-storage/content.md +2 -2
  481. package/templates/skills/pages/content.md +8 -10
  482. package/templates/skills/qa-loom/config.json +14 -0
  483. package/templates/skills/qa-loom/content.md +187 -0
  484. package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
  485. package/templates/skills/qa-loom/files/run-qa.sh +57 -0
  486. package/templates/skills/slack-interaction/content.md +4 -0
  487. package/templates/skills/swarm-extensions/content.md +2 -0
  488. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  489. package/templates/skills/swarm-scripts/config.json +1 -1
  490. package/templates/skills/swarm-scripts/content.md +4 -2
  491. package/templates/skills/workflow-iterate/content.md +38 -0
  492. package/dist/artifact-eczsknc3.js +0 -3843
  493. package/dist/cli-9xsmdpdt.js +0 -56377
  494. package/dist/cli-bd957dgb.js +0 -153
  495. package/dist/cli-q51yprfq.js +0 -1104
  496. package/dist/cli-t1ze64h3.js +0 -811
  497. package/dist/index-9ch1n761.js +0 -1102
  498. package/src/artifact-sdk/index.ts +0 -2
  499. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  500. package/src/artifact-sdk/port.ts +0 -12
  501. package/src/artifact-sdk/server.ts +0 -183
  502. package/src/artifact-sdk/tunnel.ts +0 -19
  503. package/src/commands/artifact.ts +0 -249
  504. package/src/http/ecosystem.ts +0 -77
  505. package/src/tests/artifact-commands.test.ts +0 -92
  506. package/src/tests/artifact-sdk.test.ts +0 -793
  507. package/src/tests/register-service-security.test.ts +0 -194
  508. package/src/tools/list-services.ts +0 -107
  509. package/src/tools/register-service.ts +0 -177
  510. package/src/tools/unregister-service.ts +0 -73
  511. package/src/tools/update-service-status.ts +0 -96
  512. package/templates/skills/artifacts/config.json +0 -14
  513. package/templates/skills/artifacts/content.md +0 -344
  514. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  515. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  516. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  517. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  518. package/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
  519. package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
  520. package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
  521. package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
  522. package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
  523. package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
  524. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  525. /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
@@ -0,0 +1,57 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { normalizeClaudeMessage } from "../providers/claude-session-events";
3
+
4
+ const state = {
5
+ taskId: "task-fixture",
6
+ agentId: "agent-fixture",
7
+ model: "claude-opus-5-5",
8
+ contextWindowSize: 1_000_000,
9
+ transport: "cli" as const,
10
+ };
11
+
12
+ // Trimmed from a claude CLI stream-json `result` line.
13
+ const resultLine = {
14
+ type: "result",
15
+ subtype: "success",
16
+ is_error: false,
17
+ duration_ms: 48_210,
18
+ num_turns: 6,
19
+ session_id: "session-fixture",
20
+ total_cost_usd: 0.3121,
21
+ usage: {
22
+ input_tokens: 12,
23
+ cache_creation_input_tokens: 20_792,
24
+ cache_read_input_tokens: 54_611,
25
+ output_tokens: 2_993,
26
+ output_tokens_details: { thinking_tokens: 1_304 },
27
+ cache_creation: { ephemeral_5m_input_tokens: 0, ephemeral_1h_input_tokens: 20_792 },
28
+ },
29
+ modelUsage: {
30
+ "claude-opus-5-5": {
31
+ inputTokens: 12,
32
+ outputTokens: 2_993,
33
+ cacheReadInputTokens: 54_611,
34
+ cacheCreationInputTokens: 20_792,
35
+ webSearchRequests: 0,
36
+ costUSD: 0.3121,
37
+ contextWindow: 1_000_000,
38
+ },
39
+ },
40
+ };
41
+
42
+ describe("claude result-line thinking tokens", () => {
43
+ test("records output_tokens_details.thinking_tokens without touching output or cost", () => {
44
+ const { cost } = normalizeClaudeMessage(resultLine, state);
45
+ expect(cost).toMatchObject({
46
+ thinkingTokens: 1_304,
47
+ outputTokens: 2_993,
48
+ totalCostUsd: 0.3121,
49
+ });
50
+ });
51
+
52
+ test("reports zero when the result line has no thinking breakdown", () => {
53
+ const { output_tokens_details: _, ...usage } = resultLine.usage;
54
+ const { cost } = normalizeClaudeMessage({ ...resultLine, usage }, state);
55
+ expect(cost?.thinkingTokens).toBe(0);
56
+ });
57
+ });
@@ -27,7 +27,6 @@ let sut: Sut;
27
27
  let api: ApiClient;
28
28
  let fixtureDir: string;
29
29
  let fixturePath: string;
30
- let pm2StubPath: string;
31
30
  let openRouterMock: Bun.Server;
32
31
 
33
32
  const fixtureSource = `#!/usr/bin/env bun
@@ -248,7 +247,7 @@ async function runTransport(transport: "cli" | "sdk", agentId: string): Promise<
248
247
  const home = await mkdtemp(join(fixtureDir, `home-${transport}-`));
249
248
  const workerEnv = {
250
249
  ...minimalEnv(),
251
- PATH: `${pm2StubPath}:${process.env.PATH ?? "/usr/bin:/bin"}`,
250
+ PATH: process.env.PATH ?? "/usr/bin:/bin",
252
251
  HOME: home,
253
252
  AGENT_SWARM_API_KEY: sut.apiKey,
254
253
  API_KEY: sut.apiKey,
@@ -387,7 +386,7 @@ async function runCredentialRotation(): Promise<void> {
387
386
  const observationFile = join(home, "fixture-observation.json");
388
387
  const workerEnv = {
389
388
  ...minimalEnv(),
390
- PATH: `${pm2StubPath}:${process.env.PATH ?? "/usr/bin:/bin"}`,
389
+ PATH: process.env.PATH ?? "/usr/bin:/bin",
391
390
  HOME: home,
392
391
  AGENT_SWARM_API_KEY: sut.apiKey,
393
392
  API_KEY: sut.apiKey,
@@ -537,12 +536,8 @@ describe("Claude worker transport parity", () => {
537
536
  }
538
537
  fixtureDir = await mkdtemp("/tmp/claude-worker-parity-");
539
538
  fixturePath = join(fixtureDir, "claude-fixture");
540
- pm2StubPath = join(fixtureDir, "bin");
541
- await Bun.$`mkdir -p ${pm2StubPath}`.quiet();
542
539
  await Bun.write(fixturePath, fixtureSource);
543
- await Bun.write(join(pm2StubPath, "pm2"), "#!/bin/sh\nexit 0\n");
544
540
  await Bun.$`chmod 755 ${fixturePath}`.quiet();
545
- await Bun.$`chmod 755 ${join(pm2StubPath, "pm2")}`.quiet();
546
541
  openRouterMock = Bun.serve({
547
542
  port: 0,
548
543
  fetch: () =>
@@ -0,0 +1,11 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { defaultExportToEnv } from "../utils/config-env-export";
3
+
4
+ describe("defaultExportToEnv for Chat SDK rows", () => {
5
+ test("CHAT_SDK_* rows start server-only; other keys keep the default", () => {
6
+ expect(defaultExportToEnv("CHAT_SDK_TELEGRAM_BOT_TOKEN")).toBe(false);
7
+ expect(defaultExportToEnv("chat_sdk_adapters")).toBe(false);
8
+ expect(defaultExportToEnv("CHAT_SDK_ENABLED")).toBe(false);
9
+ expect(defaultExportToEnv("MY_TOOL_TOKEN")).toBe(true);
10
+ });
11
+ });
@@ -0,0 +1,66 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { ConfigRefError, isConfigRef, resolveConfigRefs } from "../be/config-refs";
3
+
4
+ const values: Record<string, string> = { A: "alpha", B: "bravo" };
5
+ const lookup = (key: string) => values[key];
6
+
7
+ describe("resolveConfigRefs", () => {
8
+ test("resolves nested refs and refs inside arrays", () => {
9
+ const resolved = resolveConfigRefs<Record<string, unknown>>(
10
+ {
11
+ top: { $config: "A" },
12
+ nested: { deep: { value: { $config: "B" } } },
13
+ list: [{ $config: "A" }, "plain", { inner: { $config: "B" } }],
14
+ },
15
+ lookup,
16
+ );
17
+ expect(resolved).toEqual({
18
+ top: "alpha",
19
+ nested: { deep: { value: "bravo" } },
20
+ list: ["alpha", "plain", { inner: "bravo" }],
21
+ });
22
+ });
23
+
24
+ test("aggregates every missing key into one error", () => {
25
+ let error: unknown;
26
+ try {
27
+ resolveConfigRefs({ a: { $config: "MISSING_2" }, b: [{ $config: "MISSING_1" }] }, lookup);
28
+ } catch (caught) {
29
+ error = caught;
30
+ }
31
+ expect(error).toBeInstanceOf(ConfigRefError);
32
+ expect((error as ConfigRefError).missingKeys).toEqual(["MISSING_1", "MISSING_2"]);
33
+ expect((error as Error).message).toContain("MISSING_1, MISSING_2");
34
+ });
35
+
36
+ test("leaves non-ref objects and scalars untouched", () => {
37
+ const input = {
38
+ notRef: { $config: "A", extra: true },
39
+ emptyRef: { $config: "" },
40
+ numberRef: { $config: 7 },
41
+ n: 1,
42
+ b: false,
43
+ nil: null,
44
+ };
45
+ expect(resolveConfigRefs(input, lookup)).toEqual(input);
46
+ });
47
+
48
+ test("propagates a lookup that throws", () => {
49
+ expect(() =>
50
+ resolveConfigRefs({ k: { $config: "FORBIDDEN" } }, () => {
51
+ throw new Error("forbidden key");
52
+ }),
53
+ ).toThrow("forbidden key");
54
+ });
55
+ });
56
+
57
+ describe("isConfigRef", () => {
58
+ test("accepts only a single non-empty $config string key", () => {
59
+ expect(isConfigRef({ $config: "X" })).toBe(true);
60
+ expect(isConfigRef({ $config: "X", other: 1 })).toBe(false);
61
+ expect(isConfigRef({ $config: "" })).toBe(false);
62
+ expect(isConfigRef([{ $config: "X" }])).toBe(false);
63
+ expect(isConfigRef("X")).toBe(false);
64
+ expect(isConfigRef(null)).toBe(false);
65
+ });
66
+ });
@@ -2,7 +2,10 @@ import { describe, expect, test } from "bun:test";
2
2
  import {
3
3
  agentContextKey,
4
4
  agentmailContextKey,
5
+ azureDevOpsContextKey,
6
+ azureDevOpsWorkItemContextKey,
5
7
  buildJiraContextKey,
8
+ chatSdkContextKey,
6
9
  githubContextKey,
7
10
  gitlabContextKey,
8
11
  linearContextKey,
@@ -124,6 +127,36 @@ describe("context-key separator safety", () => {
124
127
  });
125
128
  });
126
129
 
130
+ describe("azure-devops work item keys", () => {
131
+ test("round-trips workitem keys", () => {
132
+ const key = azureDevOpsWorkItemContextKey({ organization: "fabrikam", workItemId: 42 });
133
+ expect(key).toBe("task:trackers:azure-devops:fabrikam:workitem:42");
134
+ expect(parseContextKey(key)).toEqual({
135
+ family: "trackers",
136
+ subFamily: "azure-devops",
137
+ parts: { organization: "fabrikam", kind: "workitem", workItemId: 42 },
138
+ });
139
+ });
140
+
141
+ test("the PR key still parses", () => {
142
+ const key = azureDevOpsContextKey({ repositoryId: "repo-1", pullRequestId: 7 });
143
+ expect(parseContextKey(key)).toEqual({
144
+ family: "trackers",
145
+ subFamily: "azure-devops",
146
+ parts: { repositoryId: "repo-1", kind: "pr", pullRequestId: 7 },
147
+ });
148
+ });
149
+
150
+ test("rejects a non-integer work item id", () => {
151
+ expect(() => azureDevOpsWorkItemContextKey({ organization: "o", workItemId: 1.5 })).toThrow();
152
+ expect(() => parseContextKey("task:trackers:azure-devops:o:workitem:abc")).toThrow();
153
+ });
154
+
155
+ test("rejects a workitem key with the wrong part count", () => {
156
+ expect(() => parseContextKey("task:trackers:azure-devops:workitem:42")).toThrow();
157
+ });
158
+ });
159
+
127
160
  describe("parseContextKey", () => {
128
161
  test("round-trips slack keys", () => {
129
162
  const key = slackContextKey({ channelId: "C0A4J7GB0UD", threadTs: "1776800534.257079" });
@@ -134,6 +167,24 @@ describe("parseContextKey", () => {
134
167
  });
135
168
  });
136
169
 
170
+ test("round-trips chat-sdk keys whose thread id contains colons", () => {
171
+ const threadId = "telegram:-1001234567890:42";
172
+ const key = chatSdkContextKey(threadId);
173
+ expect(key).toBe("task:chat-sdk:telegram:-1001234567890%3A42");
174
+ expect(key.split(":")).toHaveLength(4);
175
+ expect(parseContextKey(key)).toEqual({
176
+ family: "chat-sdk",
177
+ parts: { adapter: "telegram", threadId },
178
+ });
179
+ });
180
+
181
+ test("chat-sdk keys reject thread ids without an adapter prefix", () => {
182
+ expect(() => chatSdkContextKey("no-adapter")).toThrow(/<adapter>:<id>/);
183
+ expect(() => chatSdkContextKey(":123")).toThrow(/<adapter>:<id>/);
184
+ expect(() => chatSdkContextKey("telegram:")).toThrow(/<adapter>:<id>/);
185
+ expect(() => parseContextKey("task:chat-sdk:telegram")).toThrow(/malformed chat-sdk/);
186
+ });
187
+
137
188
  test("round-trips agentmail keys", () => {
138
189
  const key = agentmailContextKey({ threadId: "thr_abc" });
139
190
  expect(parseContextKey(key)).toEqual({
@@ -58,6 +58,31 @@ describe("isOriginAllowedForCredentials", () => {
58
58
  expect(isOriginAllowedForCredentials("https://evil.example")).toBe(false);
59
59
  });
60
60
 
61
+ test("development trusts loopback origins on any port; other modes do not", () => {
62
+ const originalNodeEnv = process.env.NODE_ENV;
63
+ try {
64
+ process.env.NODE_ENV = "development";
65
+ delete process.env[ENV_KEY];
66
+ expect(isOriginAllowedForCredentials("http://localhost:5974")).toBe(true);
67
+ expect(isOriginAllowedForCredentials("http://127.0.0.1:4173")).toBe(true);
68
+ expect(isOriginAllowedForCredentials("http://[::1]:3000")).toBe(true);
69
+ expect(isOriginAllowedForCredentials("https://localhost:8443")).toBe(true);
70
+ expect(isOriginAllowedForCredentials("http://localhost.evil.example")).toBe(false);
71
+ expect(isOriginAllowedForCredentials("http://evil.example:5974")).toBe(false);
72
+ // A custom allowlist does not switch the loopback rule off in development.
73
+ process.env[ENV_KEY] = "https://app.example";
74
+ expect(isOriginAllowedForCredentials("http://localhost:5974")).toBe(true);
75
+
76
+ process.env.NODE_ENV = "production";
77
+ expect(isOriginAllowedForCredentials("http://localhost:5974")).toBe(false);
78
+ delete process.env.NODE_ENV;
79
+ expect(isOriginAllowedForCredentials("http://localhost:5974")).toBe(false);
80
+ } finally {
81
+ if (originalNodeEnv === undefined) delete process.env.NODE_ENV;
82
+ else process.env.NODE_ENV = originalNodeEnv;
83
+ }
84
+ });
85
+
61
86
  test("denies arbitrary origins when CORS_ALLOWED_ORIGINS is blank", () => {
62
87
  process.env[ENV_KEY] = " ";
63
88
  expect(isOriginAllowedForCredentials("https://evil.example")).toBe(false);
@@ -159,6 +159,24 @@ describe("create_page MCP tool", () => {
159
159
  // Parent now holds the new body.
160
160
  const row = await getPageBySlug(agentId, "upsert");
161
161
  expect(row?.body).toBe("v1");
162
+
163
+ // The counter and the next snapshot number keep climbing from MAX(version).
164
+ const third = (await tool.handler(
165
+ {
166
+ title: "Upsert Page",
167
+ slug: "upsert",
168
+ body: "v2",
169
+ contentType: "text/html",
170
+ authMode: "public",
171
+ },
172
+ fakeMeta,
173
+ )) as { structuredContent: { version: number } };
174
+ expect(third.structuredContent.version).toBe(3);
175
+ const after = await getPageVersions(first.structuredContent.id);
176
+ expect(after.map((v) => [v.version, v.snapshot.body])).toEqual([
177
+ [2, "v1"],
178
+ [1, "v0"],
179
+ ]);
162
180
  });
163
181
 
164
182
  test("missing X-Agent-ID returns an error result", async () => {
@@ -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",