@desplega.ai/agent-swarm 1.173.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
  7. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
  8. package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
  15. package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
  16. package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
  17. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  18. package/dist/cli-2m1ktfzz.js +17795 -0
  19. package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
  20. package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
  21. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  22. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  23. package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
  24. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  25. package/dist/cli-6cvfxs8r.js +47 -0
  26. package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
  27. package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
  28. package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
  29. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  30. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  31. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  32. package/dist/cli-9traj3dn.js +638 -0
  33. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  34. package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
  35. package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
  36. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  37. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  38. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  39. package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
  40. package/dist/cli-e3g5mbkz.js +776 -0
  41. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  42. package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
  43. package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
  44. package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
  45. package/dist/cli-f527ce74.js +781 -0
  46. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  47. package/dist/cli-hjwmhcg1.js +10622 -0
  48. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  49. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  50. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  51. package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
  52. package/dist/cli-jta2eyzc.js +619 -0
  53. package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
  54. package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
  55. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  56. package/dist/cli-kceah1eh.js +92 -0
  57. package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
  58. package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
  59. package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
  60. package/dist/cli-n2wq4j7m.js +6985 -0
  61. package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
  62. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  63. package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
  64. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  65. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  66. package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
  67. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  68. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  69. package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
  70. package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
  71. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  72. package/dist/cli-rhs9pz33.js +294 -0
  73. package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
  74. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  75. package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
  76. package/dist/cli-skxvvpap.js +10140 -0
  77. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  78. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  79. package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
  80. package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
  81. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  82. package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
  83. package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
  84. package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
  85. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  86. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  87. package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
  88. package/dist/cli-wmgrkscj.js +23254 -0
  89. package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
  90. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  91. package/dist/cli.js +29 -45
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  99. package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
  100. package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
  117. package/dist/index-52j1aja4.js +3465 -0
  118. package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
  119. package/dist/index-9gw58n4k.js +193 -0
  120. package/dist/index-9j87mcm8.js +56305 -0
  121. package/dist/index-bsmcresk.js +79424 -0
  122. package/dist/index-mh85qhec.js +475 -0
  123. package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
  124. package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
  125. package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
  126. package/dist/index-we27kd31.js +12810 -0
  127. package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  148. package/openapi.json +523 -344
  149. package/package.json +10 -11
  150. package/src/be/config-refs.ts +60 -0
  151. package/src/be/db/page-versions.ts +15 -0
  152. package/src/be/db/runtime.ts +35 -8
  153. package/src/be/db/skill-name-resolution.ts +36 -0
  154. package/src/be/db/tasks/read.ts +230 -0
  155. package/src/be/db/tasks/write.ts +7 -0
  156. package/src/be/db.ts +52 -7
  157. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  158. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  159. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  160. package/src/be/model-catalog-store.ts +5 -1
  161. package/src/be/model-tier-resolution.ts +12 -1
  162. package/src/be/model-validation.ts +5 -2
  163. package/src/be/models-catalog.ts +26 -1
  164. package/src/be/modelsdev-cache.json +198 -63
  165. package/src/be/modelsdev-cache.ts +9 -1
  166. package/src/be/script-credential-broker.ts +39 -2
  167. package/src/be/scripts/run-saved.ts +1 -0
  168. package/src/be/scripts/typecheck.ts +10 -8
  169. package/src/be/seed/runner.ts +13 -1
  170. package/src/be/seed/state-db.ts +5 -0
  171. package/src/be/seed/types.ts +6 -0
  172. package/src/be/seed-pricing.ts +19 -2
  173. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  174. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  175. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  176. package/src/be/seed-skills/index.ts +104 -3
  177. package/src/be/swarm-config-guard.ts +52 -0
  178. package/src/cli.tsx +0 -16
  179. package/src/commands/onboard/non-interactive.ts +4 -2
  180. package/src/commands/provider-credentials.ts +52 -6
  181. package/src/commands/runner.ts +190 -54
  182. package/src/e2b/env.ts +5 -0
  183. package/src/extensions/contract-types.generated.ts +1 -1
  184. package/src/extensions/contract.ts +45 -0
  185. package/src/extensions/dispatcher.ts +7 -0
  186. package/src/extensions/post-bridge.ts +40 -0
  187. package/src/github/handlers.ts +11 -4
  188. package/src/heartbeat/templates.ts +8 -5
  189. package/src/hooks/hook.ts +0 -26
  190. package/src/http/all-routes.ts +0 -1
  191. package/src/http/config.ts +36 -17
  192. package/src/http/core.ts +4 -0
  193. package/src/http/index.ts +14 -2
  194. package/src/http/integrations.ts +88 -1
  195. package/src/http/mcp.ts +15 -5
  196. package/src/http/page-proxy.ts +2 -4
  197. package/src/http/pages-public.ts +3 -3
  198. package/src/http/pages.ts +3 -13
  199. package/src/http/poll.ts +28 -68
  200. package/src/http/requested-by.ts +54 -0
  201. package/src/http/scripts.ts +1 -0
  202. package/src/http/stats.ts +1 -30
  203. package/src/http/tasks.ts +61 -5
  204. package/src/http/utils.ts +14 -0
  205. package/src/http/webhooks.ts +159 -1
  206. package/src/http/x.ts +1 -0
  207. package/src/integrations/chat-sdk/adapters.ts +129 -0
  208. package/src/integrations/chat-sdk/attachments.ts +232 -0
  209. package/src/integrations/chat-sdk/background.ts +36 -0
  210. package/src/integrations/chat-sdk/commands.ts +194 -0
  211. package/src/integrations/chat-sdk/config.ts +198 -0
  212. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  213. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  214. package/src/integrations/chat-sdk/handlers.ts +28 -0
  215. package/src/integrations/chat-sdk/inbound.ts +426 -0
  216. package/src/integrations/chat-sdk/limits.ts +48 -0
  217. package/src/integrations/chat-sdk/logger.ts +81 -0
  218. package/src/integrations/chat-sdk/outbound.ts +123 -0
  219. package/src/integrations/chat-sdk/relay.ts +172 -0
  220. package/src/integrations/chat-sdk/router.ts +169 -0
  221. package/src/integrations/chat-sdk/runtime.ts +351 -0
  222. package/src/integrations/chat-sdk/schema.ts +245 -0
  223. package/src/integrations/chat-sdk/state.ts +243 -0
  224. package/src/integrations/chat-sdk/status.ts +244 -0
  225. package/src/integrations/chat-sdk/steering.ts +58 -0
  226. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  227. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  228. package/src/integrations/chat-sdk/templates.ts +16 -0
  229. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  230. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  231. package/src/integrations/chat-sdk/typing.ts +65 -0
  232. package/src/pages/version.ts +4 -5
  233. package/src/prompts/base-prompt.ts +111 -45
  234. package/src/providers/acp-target-catalog.ts +6 -0
  235. package/src/providers/acp-targets.ts +55 -2
  236. package/src/providers/amp-adapter.ts +1 -1
  237. package/src/providers/claude-adapter.ts +1 -1
  238. package/src/providers/claude-flag-settings.ts +17 -5
  239. package/src/providers/claude-managed-adapter.ts +1 -1
  240. package/src/providers/claude-session-events.ts +6 -1
  241. package/src/providers/dsh-adapter.ts +1 -1
  242. package/src/providers/opencode-adapter.ts +243 -26
  243. package/src/providers/opencode-swarm-plugin.ts +31 -0
  244. package/src/providers/pi-mono-adapter.ts +398 -153
  245. package/src/providers/pricing-sources.md +33 -2
  246. package/src/providers/reasoning-effort.ts +27 -0
  247. package/src/providers/types.ts +6 -2
  248. package/src/rbac/legacy-policy.ts +8 -2
  249. package/src/rbac/permissions.ts +21 -0
  250. package/src/scheduler/scheduler.ts +4 -2
  251. package/src/scripts-runtime/loader.ts +19 -7
  252. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  253. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  254. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  255. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  256. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  257. package/src/server.ts +52 -18
  258. package/src/slack/event-dedup.ts +1 -1
  259. package/src/tasks/additive-buffer.ts +4 -2
  260. package/src/tasks/context-key.ts +60 -1
  261. package/src/tasks/model-pin.ts +67 -0
  262. package/src/tasks/worker-follow-up.ts +4 -31
  263. package/src/tests/acp-adapter.test.ts +42 -0
  264. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  265. package/src/tests/base-prompt.test.ts +58 -54
  266. package/src/tests/chat-sdk-commands.test.ts +191 -0
  267. package/src/tests/chat-sdk-config.test.ts +334 -0
  268. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  269. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  270. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  271. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  272. package/src/tests/chat-sdk-harness.ts +146 -0
  273. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  274. package/src/tests/chat-sdk-relay.test.ts +470 -0
  275. package/src/tests/chat-sdk-reload.test.ts +128 -0
  276. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  277. package/src/tests/chat-sdk-slack.test.ts +268 -0
  278. package/src/tests/chat-sdk-state.test.ts +163 -0
  279. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  280. package/src/tests/chat-sdk-teams.test.ts +277 -0
  281. package/src/tests/chat-sdk-tools.test.ts +520 -0
  282. package/src/tests/chat-sdk-typing.test.ts +120 -0
  283. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  284. package/src/tests/claude-adapter-binary.test.ts +9 -4
  285. package/src/tests/claude-flag-settings.test.ts +11 -3
  286. package/src/tests/claude-managed-adapter.test.ts +1 -1
  287. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  288. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  289. package/src/tests/claude-worker-parity.test.ts +2 -7
  290. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  291. package/src/tests/config-refs.test.ts +66 -0
  292. package/src/tests/context-key.test.ts +19 -0
  293. package/src/tests/cors-allowlist.test.ts +25 -0
  294. package/src/tests/create-page-tool.test.ts +18 -0
  295. package/src/tests/credential-check.test.ts +139 -0
  296. package/src/tests/credentials.test.ts +21 -0
  297. package/src/tests/db-runtime.test.ts +61 -0
  298. package/src/tests/db-task-read-contract.test.ts +20 -0
  299. package/src/tests/db-task-write-contract.test.ts +10 -0
  300. package/src/tests/docs-index-footprint.test.js +3 -3
  301. package/src/tests/dsh-adapter.test.ts +1 -1
  302. package/src/tests/enabled-capabilities.test.ts +20 -0
  303. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  304. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  305. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  306. package/src/tests/explicit-model-validation.test.ts +105 -1
  307. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  308. package/src/tests/extensions-post-bridge.test.ts +51 -0
  309. package/src/tests/fetch-resolved-env.test.ts +128 -0
  310. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  311. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  312. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  313. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  314. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  315. package/src/tests/github-handlers.test.ts +35 -0
  316. package/src/tests/harness-provider-resolution.test.ts +12 -0
  317. package/src/tests/heartbeat-checklist.test.ts +3 -2
  318. package/src/tests/hook-runtime-close.test.ts +0 -2
  319. package/src/tests/http-api-integration.test.ts +0 -25
  320. package/src/tests/migration-046-budgets.test.ts +17 -16
  321. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  322. package/src/tests/model-tier-resolution.test.ts +93 -0
  323. package/src/tests/models-catalog.test.ts +42 -1
  324. package/src/tests/openai-compatible-providers.test.ts +134 -0
  325. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  326. package/src/tests/opencode-adapter.test.ts +216 -2
  327. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  328. package/src/tests/opencode-shell-env.test.ts +145 -0
  329. package/src/tests/openrouter-base-url.test.ts +101 -151
  330. package/src/tests/page-proxy.test.ts +1 -1
  331. package/src/tests/pages-browser-sdk.test.ts +76 -0
  332. package/src/tests/pages-feedback.test.ts +1 -1
  333. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  334. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  335. package/src/tests/pi-mono-adapter.test.ts +385 -5
  336. package/src/tests/pi-shell-env.test.ts +170 -0
  337. package/src/tests/prompt-template-remaining.test.ts +35 -0
  338. package/src/tests/rbac-engine.test.ts +11 -3
  339. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  340. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  341. package/src/tests/script-connections.test.ts +2 -0
  342. package/src/tests/scripts-http.test.ts +201 -1
  343. package/src/tests/scripts-only-gating.test.ts +2 -3
  344. package/src/tests/scripts-runtime.test.ts +58 -40
  345. package/src/tests/secret-scrubber.test.ts +86 -0
  346. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  347. package/src/tests/seed-skills-retire.test.ts +182 -0
  348. package/src/tests/send-task-requester-gate.test.ts +58 -0
  349. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  350. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  351. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  352. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  353. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  354. package/src/tests/swarm-diff.test.ts +1 -1
  355. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  356. package/src/tests/task-session-prompts.test.ts +132 -0
  357. package/src/tests/task-tool-manifest.test.ts +14 -0
  358. package/src/tests/task-tools-ctx.test.ts +2 -0
  359. package/src/tests/tool-annotations.test.ts +6 -6
  360. package/src/tests/ui-logs-parser.test.ts +43 -0
  361. package/src/tests/used-by-route.test.ts +6 -0
  362. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  363. package/src/tests/workflow-swarm-script.test.ts +38 -0
  364. package/src/tests/workflow-wait-filter.test.ts +5 -3
  365. package/src/tools/chat-access.ts +41 -0
  366. package/src/tools/chat-open-dm.ts +59 -0
  367. package/src/tools/chat-post.ts +74 -0
  368. package/src/tools/chat-read-thread.ts +100 -0
  369. package/src/tools/chat-reply.ts +75 -0
  370. package/src/tools/create-page.ts +3 -13
  371. package/src/tools/model-catalog/index.ts +2 -1
  372. package/src/tools/register-chat-channel.ts +139 -0
  373. package/src/tools/send-task.ts +24 -0
  374. package/src/tools/skills/skill-get.ts +17 -9
  375. package/src/tools/swarm-config/get-config.ts +2 -1
  376. package/src/tools/swarm-config/list-config.ts +2 -1
  377. package/src/tools/swarm-config/set-config.ts +16 -3
  378. package/src/tools/tool-config.ts +8 -6
  379. package/src/types.ts +12 -2
  380. package/src/utils/config-env-export.ts +74 -0
  381. package/src/utils/credentials.test.ts +92 -0
  382. package/src/utils/credentials.ts +52 -4
  383. package/src/utils/harness-provider.ts +15 -6
  384. package/src/utils/runtime-model-catalog.ts +11 -1
  385. package/src/utils/secret-scrubber.ts +71 -2
  386. package/src/utils/task-tool-manifest.ts +19 -2
  387. package/src/workflows/executors/swarm-script.ts +1 -0
  388. package/templates/skills/agent-browser/SKILL.md +1 -1
  389. package/templates/skills/agent-browser/content.md +1 -1
  390. package/templates/skills/apps/SKILL.md +8 -1
  391. package/templates/skills/apps/content.md +8 -1
  392. package/templates/skills/chat-sdk/config.json +14 -0
  393. package/templates/skills/chat-sdk/content.md +84 -0
  394. package/templates/skills/composio-gmail/content.md +7 -0
  395. package/templates/skills/composio-google-docs/content.md +1 -1
  396. package/templates/skills/email-domain-health/config.json +14 -0
  397. package/templates/skills/email-domain-health/content.md +131 -0
  398. package/templates/skills/kv-storage/content.md +2 -2
  399. package/templates/skills/pages/content.md +8 -10
  400. package/templates/skills/swarm-extensions/content.md +2 -0
  401. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  402. package/templates/skills/swarm-scripts/config.json +1 -1
  403. package/templates/skills/swarm-scripts/content.md +4 -2
  404. package/templates/skills/workflow-iterate/content.md +38 -0
  405. package/dist/artifact-eczsknc3.js +0 -3843
  406. package/dist/cli-bd957dgb.js +0 -153
  407. package/dist/cli-q51yprfq.js +0 -1104
  408. package/dist/cli-t1ze64h3.js +0 -811
  409. package/dist/cli-tcc4wd5d.js +0 -56471
  410. package/dist/index-9ch1n761.js +0 -1102
  411. package/src/artifact-sdk/index.ts +0 -2
  412. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  413. package/src/artifact-sdk/port.ts +0 -12
  414. package/src/artifact-sdk/server.ts +0 -183
  415. package/src/artifact-sdk/tunnel.ts +0 -19
  416. package/src/commands/artifact.ts +0 -249
  417. package/src/http/ecosystem.ts +0 -77
  418. package/src/tests/artifact-commands.test.ts +0 -92
  419. package/src/tests/artifact-sdk.test.ts +0 -793
  420. package/src/tests/register-service-security.test.ts +0 -194
  421. package/src/tools/list-services.ts +0 -107
  422. package/src/tools/register-service.ts +0 -177
  423. package/src/tools/unregister-service.ts +0 -73
  424. package/src/tools/update-service-status.ts +0 -96
  425. package/templates/skills/artifacts/config.json +0 -14
  426. package/templates/skills/artifacts/content.md +0 -344
  427. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  428. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  429. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  430. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  431. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  432. /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
@@ -0,0 +1,200 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { unlink } from "node:fs/promises";
3
+ import type { IncomingMessage, ServerResponse } from "node:http";
4
+ import {
5
+ startTelegramBotApiMock,
6
+ type TelegramBotApiMock,
7
+ } from "../../scripts/dev/telegram-bot-api-mock";
8
+ import { closeDb, createAgent, getDbClient, initDb } from "../be/db";
9
+ import { handleWebhooks } from "../http/webhooks";
10
+ import { drainBackground } from "../integrations/chat-sdk/background";
11
+ import { getChatSdkHealth, startChatSdk, stopChatSdk } from "../integrations/chat-sdk/runtime";
12
+ import { flushAllChatBuffers } from "../integrations/chat-sdk/thread-buffer";
13
+ import { getEnabledCapabilities } from "../server";
14
+ import privateText from "./fixtures/chat-sdk/telegram-private-text.json";
15
+
16
+ const TEST_DB_PATH = "./test-chat-sdk-telegram.sqlite";
17
+ const SECRET = "s";
18
+ const ENV_KEYS = ["CHAT_SDK_ENABLED", "CHAT_SDK_ADAPTERS", "CHAT_SDK_API_URL_OVERRIDES"];
19
+ const savedEnv = new Map(ENV_KEYS.map((key) => [key, process.env[key]]));
20
+
21
+ let mock: TelegramBotApiMock;
22
+ let leadId: string;
23
+
24
+ async function removeDbFiles(): Promise<void> {
25
+ for (const suffix of ["", "-wal", "-shm"]) {
26
+ await unlink(`${TEST_DB_PATH}${suffix}`).catch(() => {});
27
+ }
28
+ }
29
+
30
+ /** Minimal fake req/res to drive handleWebhooks without a live server. */
31
+ function fakeReqRes(path: string, rawBody: string, headers: Record<string, string>) {
32
+ const rawHeaders = Object.entries(headers).flat();
33
+ const req = {
34
+ method: "POST",
35
+ url: path,
36
+ headers: Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])),
37
+ rawHeaders,
38
+ async *[Symbol.asyncIterator]() {
39
+ yield Buffer.from(rawBody);
40
+ },
41
+ } as unknown as IncomingMessage;
42
+
43
+ const captured = { status: 0, body: "" };
44
+ const res = {
45
+ writeHead(status: number) {
46
+ captured.status = status;
47
+ return this;
48
+ },
49
+ end(chunk?: string | Buffer) {
50
+ if (chunk) captured.body = chunk.toString();
51
+ return this;
52
+ },
53
+ } as unknown as ServerResponse;
54
+
55
+ return { req, res, captured };
56
+ }
57
+
58
+ async function post(adapter: string, body: unknown, headers: Record<string, string>) {
59
+ const path = `/api/integrations/chat/${adapter}/webhook`;
60
+ const { req, res, captured } = fakeReqRes(path, JSON.stringify(body), {
61
+ "Content-Type": "application/json",
62
+ ...headers,
63
+ });
64
+ const handled = await handleWebhooks(req, res, path.split("/").filter(Boolean));
65
+ expect(handled).toBe(true);
66
+ return captured;
67
+ }
68
+
69
+ beforeAll(async () => {
70
+ await removeDbFiles();
71
+ initDb(TEST_DB_PATH);
72
+ const lead = await createAgent({
73
+ id: crypto.randomUUID(),
74
+ name: "ChatLead",
75
+ isLead: true,
76
+ status: "idle",
77
+ });
78
+ leadId = lead.id;
79
+ mock = startTelegramBotApiMock();
80
+ process.env.CHAT_SDK_ENABLED = "true";
81
+ process.env.CHAT_SDK_ADAPTERS = JSON.stringify({
82
+ telegram: { botToken: "t", secretToken: SECRET, mode: "webhook" },
83
+ });
84
+ process.env.CHAT_SDK_API_URL_OVERRIDES = JSON.stringify({ telegram: mock.url });
85
+ await startChatSdk();
86
+ });
87
+
88
+ afterAll(async () => {
89
+ await stopChatSdk();
90
+ mock.stop();
91
+ for (const [key, value] of savedEnv) {
92
+ if (value === undefined) delete process.env[key];
93
+ else process.env[key] = value;
94
+ }
95
+ closeDb();
96
+ await removeDbFiles();
97
+ });
98
+
99
+ describe("Chat SDK Telegram webhook", () => {
100
+ test("the adapter starts healthy against the mock Bot API", () => {
101
+ expect(getChatSdkHealth().telegram?.status).toBe("ok");
102
+ expect(mock.calls.some((call) => call.method === "getMe")).toBe(true);
103
+ });
104
+
105
+ test("a wrong secret token is rejected with 401", async () => {
106
+ const captured = await post("telegram", privateText, {
107
+ "X-Telegram-Bot-Api-Secret-Token": "wrong",
108
+ });
109
+ expect(captured.status).toBe(401);
110
+ });
111
+
112
+ test("a body above 1 MiB is rejected with 413 before the adapter runs", async () => {
113
+ const path = "/api/integrations/chat/telegram/webhook";
114
+ const segments = path.split("/").filter(Boolean);
115
+ const oversized = "x".repeat(1024 * 1024 + 1);
116
+ const secretHeader = { "X-Telegram-Bot-Api-Secret-Token": SECRET };
117
+ const sentBefore = mock.calls.length;
118
+
119
+ // Declared up front: rejected before any chunk is read.
120
+ const declared = fakeReqRes(path, "{}", {
121
+ ...secretHeader,
122
+ "Content-Length": String(oversized.length),
123
+ });
124
+ expect(await handleWebhooks(declared.req, declared.res, segments)).toBe(true);
125
+ expect(declared.captured.status).toBe(413);
126
+
127
+ // Streamed without Content-Length: rejected once the cap is crossed.
128
+ const streamed = fakeReqRes(path, oversized, secretHeader);
129
+ expect(await handleWebhooks(streamed.req, streamed.res, segments)).toBe(true);
130
+ expect(streamed.captured.status).toBe(413);
131
+ expect(JSON.parse(streamed.captured.body).error).toContain("Payload too large");
132
+ expect(mock.calls.length).toBe(sentBefore);
133
+ });
134
+
135
+ test("an unconfigured adapter answers 404", async () => {
136
+ const captured = await post("discord", {}, {});
137
+ expect(captured.status).toBe(404);
138
+ });
139
+
140
+ test("a private-chat text update creates one Lead task with a chat-sdk contextKey", async () => {
141
+ const captured = await post("telegram", privateText, {
142
+ "X-Telegram-Bot-Api-Secret-Token": SECRET,
143
+ });
144
+ expect(captured.status).toBe(200);
145
+ expect(await drainBackground(5_000)).toBe(true);
146
+ // Messages wait in the thread buffer; flush it instead of waiting 10 s.
147
+ await flushAllChatBuffers();
148
+ expect(await drainBackground(5_000)).toBe(true);
149
+
150
+ const tasks = await getDbClient().query<{
151
+ id: string;
152
+ source: string;
153
+ contextKey: string;
154
+ agentId: string | null;
155
+ task: string;
156
+ taskType: string;
157
+ }>(
158
+ "SELECT id, source, contextKey, agentId, task, taskType FROM agent_tasks WHERE source = 'chat'",
159
+ );
160
+ expect(tasks).toHaveLength(1);
161
+ const [task] = tasks;
162
+ expect(task!.contextKey.startsWith("task:chat-sdk:telegram:")).toBe(true);
163
+ expect(task!.agentId).toBe(leadId);
164
+ expect(task!.taskType).toBe("chat-inbound");
165
+ expect(task!.task).toContain("summarize the open tasks");
166
+
167
+ const subscriptions = await getDbClient().query<{ thread_id: string }>(
168
+ "SELECT thread_id FROM chat_subscriptions",
169
+ );
170
+ expect(subscriptions.map((row) => row.thread_id)).toContain("telegram:555001");
171
+ });
172
+
173
+ test("a redelivered update is deduplicated", async () => {
174
+ const captured = await post("telegram", privateText, {
175
+ "X-Telegram-Bot-Api-Secret-Token": SECRET,
176
+ });
177
+ expect(captured.status).toBe(200);
178
+ expect(await drainBackground(5_000)).toBe(true);
179
+ await flushAllChatBuffers();
180
+ const row = await getDbClient().get<{ n: number }>(
181
+ "SELECT COUNT(*) AS n FROM agent_tasks WHERE source = 'chat'",
182
+ );
183
+ expect(row?.n).toBe(1);
184
+ });
185
+ });
186
+
187
+ describe("chat-sdk capability", () => {
188
+ test("is advertised only when CHAT_SDK_ENABLED is true", () => {
189
+ const previous = process.env.CHAT_SDK_ENABLED;
190
+ try {
191
+ process.env.CHAT_SDK_ENABLED = "true";
192
+ expect(getEnabledCapabilities()).toContain("chat-sdk");
193
+ process.env.CHAT_SDK_ENABLED = "false";
194
+ expect(getEnabledCapabilities()).not.toContain("chat-sdk");
195
+ } finally {
196
+ if (previous === undefined) delete process.env.CHAT_SDK_ENABLED;
197
+ else process.env.CHAT_SDK_ENABLED = previous;
198
+ }
199
+ });
200
+ });
@@ -49,7 +49,7 @@ import {
49
49
  resolveClaudeBridgeEnabled,
50
50
  resolveClaudeTrustDirs,
51
51
  } from "../providers/claude-adapter";
52
- import { ClaudeSettingsConfigError } from "../providers/claude-flag-settings";
52
+ import { ClaudeSettingsConfigError, userClaudeMdExclude } from "../providers/claude-flag-settings";
53
53
  import type { ProviderSessionConfig } from "../providers/types";
54
54
  import { setFlockForTests } from "../utils/file-lock";
55
55
  import { holdFileLock } from "./fixtures/hold-file-lock";
@@ -775,7 +775,7 @@ describe("CLAUDE_BINARY env override", () => {
775
775
  expect(argv[0]).toBe("claude");
776
776
  });
777
777
 
778
- test("excludes only /workspace/CLAUDE.md from Claude's memory load", async () => {
778
+ test("excludes both on-disk agent CLAUDE.md copies from Claude's memory load", async () => {
779
779
  const adapter = new ClaudeAdapter();
780
780
  await createCompletedSession(adapter, makeConfig());
781
781
 
@@ -783,7 +783,7 @@ describe("CLAUDE_BINARY env override", () => {
783
783
  const at = argv.indexOf("--settings");
784
784
  expect(at).toBeGreaterThan(0);
785
785
  expect(JSON.parse(argv[at + 1] ?? "")).toEqual({
786
- claudeMdExcludes: [WORKSPACE_CLAUDE_MD_PATH],
786
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_PATH, userClaudeMdExclude()],
787
787
  });
788
788
  });
789
789
 
@@ -820,7 +820,12 @@ describe("CLAUDE_BINARY env override", () => {
820
820
  expect(JSON.parse(argv[argv.indexOf("--settings") + 1] ?? "")).toEqual({
821
821
  model: "from-file",
822
822
  env: { X: "kept" },
823
- claudeMdExcludes: ["/repo/CLAUDE.md", WORKSPACE_CLAUDE_MD_PATH, "/other/CLAUDE.md"],
823
+ claudeMdExcludes: [
824
+ "/repo/CLAUDE.md",
825
+ WORKSPACE_CLAUDE_MD_PATH,
826
+ "/other/CLAUDE.md",
827
+ userClaudeMdExclude(),
828
+ ],
824
829
  });
825
830
  expect(argv[0]).toBe("claude");
826
831
  expect(argv.slice(argv.indexOf("--max-turns"), argv.indexOf("--max-turns") + 2)).toEqual([
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  import {
6
6
  ClaudeSettingsConfigError,
7
7
  mergeClaudeSettingsArgs,
8
+ userClaudeMdExclude,
8
9
  WORKSPACE_CLAUDE_MD_EXCLUDE,
9
10
  } from "../providers/claude-flag-settings";
10
11
 
@@ -22,7 +23,9 @@ describe("mergeClaudeSettingsArgs", () => {
22
23
  test("no caller settings: args pass through, only the workspace exclusion", () => {
23
24
  const merged = mergeClaudeSettingsArgs([["--max-turns", "3"]], directory);
24
25
  expect(merged.argLists).toEqual([["--max-turns", "3"]]);
25
- expect(merged.settings).toEqual({ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE] });
26
+ expect(merged.settings).toEqual({
27
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE, userClaudeMdExclude()],
28
+ });
26
29
  });
27
30
 
28
31
  test("strips both flag forms, keeps caller keys, unions and dedupes claudeMdExcludes", async () => {
@@ -44,7 +47,12 @@ describe("mergeClaudeSettingsArgs", () => {
44
47
  expect(merged.settings).toEqual({
45
48
  model: "file",
46
49
  env: { X: "kept" },
47
- claudeMdExcludes: ["/a/CLAUDE.md", WORKSPACE_CLAUDE_MD_EXCLUDE, "/b/CLAUDE.md"],
50
+ claudeMdExcludes: [
51
+ "/a/CLAUDE.md",
52
+ WORKSPACE_CLAUDE_MD_EXCLUDE,
53
+ "/b/CLAUDE.md",
54
+ userClaudeMdExclude(),
55
+ ],
48
56
  });
49
57
  });
50
58
 
@@ -100,7 +108,7 @@ describe("mergeClaudeSettingsArgs", () => {
100
108
  expect(merged.settings).toEqual({
101
109
  model: "b",
102
110
  env: {},
103
- claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE],
111
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE, userClaudeMdExclude()],
104
112
  });
105
113
  });
106
114
  });
@@ -966,7 +966,7 @@ describe("ClaudeManagedAdapter (Phase 4) — repo provisioning + cost data", ()
966
966
  expect(getClaudeManagedModelPricing("claude-sonnet-5-5")).toEqual({
967
967
  inputPerMillion: 2.0,
968
968
  outputPerMillion: 10.0,
969
- cacheReadPerMillion: 0.2,
969
+ cacheReadPerMillion: 0.1,
970
970
  cacheWritePerMillion: 2.5,
971
971
  });
972
972
  expect(getClaudeManagedModelPricing("claude-sonnet-5")).toEqual({
@@ -3,6 +3,7 @@ import { mkdtemp, readFile, realpath, rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { ClaudeAdapter, getTaskFilePath } from "../providers/claude-adapter";
6
+ import { userClaudeMdExclude } from "../providers/claude-flag-settings";
6
7
  import type { ProviderEvent, ProviderSessionConfig } from "../providers/types";
7
8
  import { CHILD_PROCESS_TEST_BUDGET_MS, runChild } from "./test-proc";
8
9
 
@@ -148,11 +149,11 @@ describe("Claude SDK production adapter lifecycle", () => {
148
149
  argument === "--system-prompt" && fixtureState.argv[index + 1] === "",
149
150
  ),
150
151
  ).toBeFalse();
151
- // The SDK forwards `settings` as `--settings`: Claude skips /workspace/CLAUDE.md.
152
+ // The SDK forwards `settings` as `--settings`: Claude skips both agent CLAUDE.md copies.
152
153
  const settingsAt = fixtureState.argv.indexOf("--settings");
153
154
  expect(settingsAt).toBeGreaterThan(-1);
154
155
  expect(JSON.parse(fixtureState.argv[settingsAt + 1] ?? "")).toMatchObject({
155
- claudeMdExcludes: ["/workspace/CLAUDE.md"],
156
+ claudeMdExcludes: ["/workspace/CLAUDE.md", userClaudeMdExclude()],
156
157
  });
157
158
  },
158
159
  { timeout: CHILD_PROCESS_TEST_BUDGET_MS },
@@ -191,7 +192,12 @@ describe("Claude SDK production adapter lifecycle", () => {
191
192
  expect(JSON.parse(argv[argv.indexOf("--settings") + 1] ?? "")).toEqual({
192
193
  model: "from-file",
193
194
  env: { X: "kept" },
194
- claudeMdExcludes: ["/repo/CLAUDE.md", "/other/CLAUDE.md", "/workspace/CLAUDE.md"],
195
+ claudeMdExcludes: [
196
+ "/repo/CLAUDE.md",
197
+ "/other/CLAUDE.md",
198
+ "/workspace/CLAUDE.md",
199
+ userClaudeMdExclude(),
200
+ ],
195
201
  });
196
202
  expect(argv).not.toContain(settingsFile);
197
203
  // The version probe carries the same merged settings value.
@@ -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
+ });
@@ -5,6 +5,7 @@ import {
5
5
  azureDevOpsContextKey,
6
6
  azureDevOpsWorkItemContextKey,
7
7
  buildJiraContextKey,
8
+ chatSdkContextKey,
8
9
  githubContextKey,
9
10
  gitlabContextKey,
10
11
  linearContextKey,
@@ -166,6 +167,24 @@ describe("parseContextKey", () => {
166
167
  });
167
168
  });
168
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
+
169
188
  test("round-trips agentmail keys", () => {
170
189
  const key = agentmailContextKey({ threadId: "thr_abc" });
171
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 () => {