@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,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
+ });
@@ -38,6 +38,7 @@ import {
38
38
  } from "node:fs/promises";
39
39
  import { tmpdir } from "node:os";
40
40
  import { join } from "node:path";
41
+ import { WORKSPACE_CLAUDE_MD_PATH } from "../commands/profile-sync";
41
42
  import {
42
43
  ClaudeAdapter,
43
44
  parseClaudeBinary,
@@ -48,6 +49,7 @@ import {
48
49
  resolveClaudeBridgeEnabled,
49
50
  resolveClaudeTrustDirs,
50
51
  } from "../providers/claude-adapter";
52
+ import { ClaudeSettingsConfigError, userClaudeMdExclude } from "../providers/claude-flag-settings";
51
53
  import type { ProviderSessionConfig } from "../providers/types";
52
54
  import { setFlockForTests } from "../utils/file-lock";
53
55
  import { holdFileLock } from "./fixtures/hold-file-lock";
@@ -694,6 +696,8 @@ describe("CLAUDE_BINARY env override", () => {
694
696
  let spawnSpy: ReturnType<typeof spyOn>;
695
697
  let whichSpy: ReturnType<typeof spyOn>;
696
698
  let spawnedArgs: Array<readonly string[]>;
699
+ /** Every non-git spawn, including the `--version` probe. */
700
+ let claudeSpawns: Array<readonly string[]>;
697
701
  let spawnedEnvs: Array<Record<string, string> | undefined>;
698
702
 
699
703
  beforeEach(async () => {
@@ -712,9 +716,11 @@ describe("CLAUDE_BINARY env override", () => {
712
716
  process.env.CLAUDE_CODE_OAUTH_TOKEN = "example-test-token";
713
717
 
714
718
  spawnedArgs = [];
719
+ claudeSpawns = [];
715
720
  spawnedEnvs = [];
716
721
  spawnSpy = spyOn(Bun, "spawn").mockImplementation(((cmd: readonly string[], opts?: unknown) => {
717
722
  if (isGit(cmd)) return (realSpawn as (...a: unknown[]) => unknown)(cmd, opts);
723
+ claudeSpawns.push(cmd);
718
724
  if (cmd.at(-1) !== "--version") {
719
725
  spawnedArgs.push(cmd);
720
726
  spawnedEnvs.push((opts as { env?: Record<string, string> } | undefined)?.env);
@@ -769,6 +775,144 @@ describe("CLAUDE_BINARY env override", () => {
769
775
  expect(argv[0]).toBe("claude");
770
776
  });
771
777
 
778
+ test("excludes both on-disk agent CLAUDE.md copies from Claude's memory load", async () => {
779
+ const adapter = new ClaudeAdapter();
780
+ await createCompletedSession(adapter, makeConfig());
781
+
782
+ const argv = spawnedArgs[0];
783
+ const at = argv.indexOf("--settings");
784
+ expect(at).toBeGreaterThan(0);
785
+ expect(JSON.parse(argv[at + 1] ?? "")).toEqual({
786
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_PATH, userClaudeMdExclude()],
787
+ });
788
+ });
789
+
790
+ test("merges caller --settings from additionalArgs and CLAUDE_BINARY into one flag", async () => {
791
+ const settingsDir = await mkdtemp(join(tmpdir(), "claude-settings-merge-"));
792
+ try {
793
+ await writeFile(
794
+ join(settingsDir, "caller.json"),
795
+ JSON.stringify({ model: "from-file", claudeMdExcludes: ["/repo/CLAUDE.md"] }),
796
+ );
797
+ process.env.CLAUDE_BINARY = `claude --settings=${join(settingsDir, "caller.json")}`;
798
+ const adapter = new ClaudeAdapter();
799
+ await createCompletedSession(
800
+ adapter,
801
+ makeConfig({
802
+ cwd: settingsDir,
803
+ additionalArgs: [
804
+ "--settings",
805
+ JSON.stringify({
806
+ env: { X: "kept" },
807
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_PATH, "/other/CLAUDE.md"],
808
+ }),
809
+ "--max-turns",
810
+ "3",
811
+ ],
812
+ }),
813
+ );
814
+
815
+ const argv = spawnedArgs[0];
816
+ const settingsFlags = argv.filter(
817
+ (arg) => arg === "--settings" || arg.startsWith("--settings="),
818
+ );
819
+ expect(settingsFlags).toEqual(["--settings"]);
820
+ expect(JSON.parse(argv[argv.indexOf("--settings") + 1] ?? "")).toEqual({
821
+ model: "from-file",
822
+ env: { X: "kept" },
823
+ claudeMdExcludes: [
824
+ "/repo/CLAUDE.md",
825
+ WORKSPACE_CLAUDE_MD_PATH,
826
+ "/other/CLAUDE.md",
827
+ userClaudeMdExclude(),
828
+ ],
829
+ });
830
+ expect(argv[0]).toBe("claude");
831
+ expect(argv.slice(argv.indexOf("--max-turns"), argv.indexOf("--max-turns") + 2)).toEqual([
832
+ "--max-turns",
833
+ "3",
834
+ ]);
835
+ // The version probe runs with the same stripped prefix and merged settings.
836
+ const probe = claudeSpawns.find((cmd) => cmd.at(-1) === "--version") ?? [];
837
+ expect(probe).toEqual([
838
+ "claude",
839
+ "--settings",
840
+ argv[argv.indexOf("--settings") + 1] ?? "",
841
+ "--version",
842
+ ]);
843
+ } finally {
844
+ await rm(settingsDir, { recursive: true, force: true });
845
+ }
846
+ });
847
+
848
+ test("invalid caller --settings refuses to start: no Claude child spawns", async () => {
849
+ const settingsDir = await mkdtemp(join(tmpdir(), "claude-settings-invalid-"));
850
+ // Built at runtime so no single source literal looks like a credential.
851
+ const sentinel = ["settings", "sentinel", crypto.randomUUID()].join("-");
852
+ try {
853
+ await writeFile(join(settingsDir, "broken.json"), "{nope");
854
+ await writeFile(join(settingsDir, "array.json"), "[1]");
855
+ const cases: [string[], string][] = [
856
+ [["--settings", "missing.json"], "cannot be read (ENOENT)"],
857
+ [["--settings", "broken.json"], "is not valid JSON"],
858
+ [["--settings", `{"env":{"TOKEN":"${sentinel}"`], "is not valid JSON"],
859
+ [["--settings=array.json"], "is not a JSON object"],
860
+ [["--settings", "--max-turns", "3"], "--settings has no value"],
861
+ [["--settings", '--settings={"model":"x"}'], "--settings has no value"],
862
+ ];
863
+ for (const [additionalArgs, message] of cases) {
864
+ claudeSpawns = [];
865
+ const adapter = new ClaudeAdapter();
866
+ const error = await adapter
867
+ .createSession(makeConfig({ cwd: settingsDir, additionalArgs }))
868
+ .then(
869
+ () => undefined,
870
+ (err: unknown) => err,
871
+ );
872
+ expect(error).toBeInstanceOf(ClaudeSettingsConfigError);
873
+ expect((error as Error).message).toContain(message);
874
+ expect((error as Error).message).not.toContain(sentinel);
875
+ // Counts the `--version` probe too: validation runs before every spawn.
876
+ expect(claudeSpawns).toEqual([]);
877
+ // ...and before the trust pre-seed writes ~/.claude.json.
878
+ expect(await Bun.file(join(homeDir, ".claude.json")).exists()).toBeFalse();
879
+ }
880
+ } finally {
881
+ await rm(settingsDir, { recursive: true, force: true });
882
+ }
883
+ });
884
+
885
+ test("invalid CLAUDE_BINARY prefix --settings never reaches logs, errors, or a spawn", async () => {
886
+ const sentinel = ["prefix", "sentinel", crypto.randomUUID()].join("-");
887
+ const inline = `{"env":{"TOKEN":"${sentinel}"`;
888
+ const logged: string[] = [];
889
+ const capture = (...args: unknown[]) => {
890
+ logged.push(args.map(String).join(" "));
891
+ };
892
+ const logSpy = spyOn(console, "log").mockImplementation(capture);
893
+ const warnSpy = spyOn(console, "warn").mockImplementation(capture);
894
+ const errorSpy = spyOn(console, "error").mockImplementation(capture);
895
+ try {
896
+ for (const binary of [`claude --settings=${inline}`, `claude --settings ${inline}`]) {
897
+ process.env.CLAUDE_BINARY = binary;
898
+ claudeSpawns = [];
899
+ const error = await new ClaudeAdapter().createSession(makeConfig()).then(
900
+ () => undefined,
901
+ (err: unknown) => err,
902
+ );
903
+ expect(error).toBeInstanceOf(ClaudeSettingsConfigError);
904
+ expect((error as Error).message).toContain("inline JSON is not valid JSON");
905
+ expect((error as Error).message).not.toContain(sentinel);
906
+ expect(claudeSpawns).toEqual([]);
907
+ }
908
+ } finally {
909
+ logSpy.mockRestore();
910
+ warnSpy.mockRestore();
911
+ errorSpy.mockRestore();
912
+ }
913
+ expect(logged.filter((line) => line.includes(sentinel))).toEqual([]);
914
+ });
915
+
772
916
  test("legacy bridge override: argv[0] comes from CLAUDE_BINARY", async () => {
773
917
  process.env.CLAUDE_BINARY = LEGACY_BRIDGE_COMPAT_BINARY;
774
918
 
@@ -0,0 +1,114 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ ClaudeSettingsConfigError,
7
+ mergeClaudeSettingsArgs,
8
+ userClaudeMdExclude,
9
+ WORKSPACE_CLAUDE_MD_EXCLUDE,
10
+ } from "../providers/claude-flag-settings";
11
+
12
+ let directory: string;
13
+
14
+ beforeEach(async () => {
15
+ directory = await mkdtemp(join(tmpdir(), "claude-flag-settings-"));
16
+ });
17
+
18
+ afterEach(async () => {
19
+ await rm(directory, { recursive: true, force: true });
20
+ });
21
+
22
+ describe("mergeClaudeSettingsArgs", () => {
23
+ test("no caller settings: args pass through, only the workspace exclusion", () => {
24
+ const merged = mergeClaudeSettingsArgs([["--max-turns", "3"]], directory);
25
+ expect(merged.argLists).toEqual([["--max-turns", "3"]]);
26
+ expect(merged.settings).toEqual({
27
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE, userClaudeMdExclude()],
28
+ });
29
+ });
30
+
31
+ test("strips both flag forms, keeps caller keys, unions and dedupes claudeMdExcludes", async () => {
32
+ await writeFile(
33
+ join(directory, "relative.json"),
34
+ JSON.stringify({ model: "file", claudeMdExcludes: ["/a/CLAUDE.md", "/a/CLAUDE.md"] }),
35
+ );
36
+ const merged = mergeClaudeSettingsArgs(
37
+ [
38
+ ["--settings", "relative.json"],
39
+ [
40
+ "--verbose",
41
+ `--settings=${JSON.stringify({ env: { X: "kept" }, claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE, "/b/CLAUDE.md"] })}`,
42
+ ],
43
+ ],
44
+ directory,
45
+ );
46
+ expect(merged.argLists).toEqual([[], ["--verbose"]]);
47
+ expect(merged.settings).toEqual({
48
+ model: "file",
49
+ env: { X: "kept" },
50
+ claudeMdExcludes: [
51
+ "/a/CLAUDE.md",
52
+ WORKSPACE_CLAUDE_MD_EXCLUDE,
53
+ "/b/CLAUDE.md",
54
+ userClaudeMdExclude(),
55
+ ],
56
+ });
57
+ });
58
+
59
+ test("missing file, malformed JSON and non-object values refuse to start", async () => {
60
+ await writeFile(join(directory, "array.json"), "[1]");
61
+ await writeFile(join(directory, "broken.json"), "{nope");
62
+ const cases: [string[], string][] = [
63
+ [
64
+ ["--settings=missing.json"],
65
+ `file ${join(directory, "missing.json")} cannot be read (ENOENT)`,
66
+ ],
67
+ [["--settings", "broken.json"], `file ${join(directory, "broken.json")} is not valid JSON`],
68
+ [["--settings", "array.json"], "is not a JSON object"],
69
+ ];
70
+ for (const [args, message] of cases) {
71
+ expect(() => mergeClaudeSettingsArgs([args], directory)).toThrow(message);
72
+ expect(() => mergeClaudeSettingsArgs([args], directory)).toThrow(ClaudeSettingsConfigError);
73
+ }
74
+ });
75
+
76
+ test("malformed inline JSON error never echoes the value", () => {
77
+ let error: unknown;
78
+ try {
79
+ mergeClaudeSettingsArgs(
80
+ [["--settings", '{"env":{"TOKEN":"sk-ant-oat01-should-not-leak"', "--verbose"]],
81
+ directory,
82
+ );
83
+ } catch (err) {
84
+ error = err;
85
+ }
86
+ expect(error).toBeInstanceOf(ClaudeSettingsConfigError);
87
+ expect((error as Error).message).toContain("inline JSON is not valid JSON");
88
+ expect((error as Error).message).not.toContain("should-not-leak");
89
+ });
90
+
91
+ test("a missing value never swallows the next flag", () => {
92
+ for (const args of [
93
+ ["--settings"],
94
+ ["--settings="],
95
+ ["--settings", "--max-turns", "3"],
96
+ ["--settings", `--settings=${JSON.stringify({ model: "x" })}`],
97
+ ]) {
98
+ expect(() => mergeClaudeSettingsArgs([args], directory)).toThrow("--settings has no value");
99
+ }
100
+ });
101
+
102
+ test("consecutive valid settings flags merge in order", () => {
103
+ const merged = mergeClaudeSettingsArgs(
104
+ [["--settings", '{"model":"a"}', '--settings={"model":"b","env":{}}', "--max-turns", "3"]],
105
+ directory,
106
+ );
107
+ expect(merged.argLists).toEqual([["--max-turns", "3"]]);
108
+ expect(merged.settings).toEqual({
109
+ model: "b",
110
+ env: {},
111
+ claudeMdExcludes: [WORKSPACE_CLAUDE_MD_EXCLUDE, userClaudeMdExclude()],
112
+ });
113
+ });
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({
@@ -1,8 +1,9 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
2
  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,6 +149,152 @@ describe("Claude SDK production adapter lifecycle", () => {
148
149
  argument === "--system-prompt" && fixtureState.argv[index + 1] === "",
149
150
  ),
150
151
  ).toBeFalse();
152
+ // The SDK forwards `settings` as `--settings`: Claude skips both agent CLAUDE.md copies.
153
+ const settingsAt = fixtureState.argv.indexOf("--settings");
154
+ expect(settingsAt).toBeGreaterThan(-1);
155
+ expect(JSON.parse(fixtureState.argv[settingsAt + 1] ?? "")).toMatchObject({
156
+ claudeMdExcludes: ["/workspace/CLAUDE.md", userClaudeMdExclude()],
157
+ });
158
+ },
159
+ { timeout: CHILD_PROCESS_TEST_BUDGET_MS },
160
+ );
161
+
162
+ test(
163
+ "merges caller --settings from additionalArgs and CLAUDE_BINARY into the SDK's one flag",
164
+ async () => {
165
+ const directory = await temporaryDirectory();
166
+ const stateFile = join(directory, "settings-state.json");
167
+ const settingsFile = join(directory, "caller-settings.json");
168
+ await Bun.write(
169
+ settingsFile,
170
+ JSON.stringify({ model: "from-file", claudeMdExcludes: ["/repo/CLAUDE.md"] }),
171
+ );
172
+ const sessionConfig = config(directory, "queue", stateFile);
173
+ sessionConfig.env = {
174
+ ...sessionConfig.env,
175
+ CLAUDE_BINARY: `${process.execPath} ${fixturePath} --settings ${settingsFile}`,
176
+ };
177
+ sessionConfig.additionalArgs = [
178
+ `--settings=${JSON.stringify({ env: { X: "kept" }, claudeMdExcludes: ["/other/CLAUDE.md"] })}`,
179
+ ];
180
+ const session = await new ClaudeAdapter(async () => {}).createSession(sessionConfig);
181
+ // Queue mode records argv once it has the initial plus two queued messages.
182
+ await session.deliverSteering?.({ mode: "queue", text: "queued-message-a" });
183
+ await session.deliverSteering?.({ mode: "queue", text: "queued-message-b" });
184
+ await session.waitForCompletion();
185
+
186
+ await waitForFile(stateFile);
187
+ const { argv } = JSON.parse(await Bun.file(stateFile).text()) as { argv: string[] };
188
+ const settingsFlags = argv.filter(
189
+ (arg) => arg === "--settings" || arg.startsWith("--settings="),
190
+ );
191
+ expect(settingsFlags).toEqual(["--settings"]);
192
+ expect(JSON.parse(argv[argv.indexOf("--settings") + 1] ?? "")).toEqual({
193
+ model: "from-file",
194
+ env: { X: "kept" },
195
+ claudeMdExcludes: [
196
+ "/repo/CLAUDE.md",
197
+ "/other/CLAUDE.md",
198
+ "/workspace/CLAUDE.md",
199
+ userClaudeMdExclude(),
200
+ ],
201
+ });
202
+ expect(argv).not.toContain(settingsFile);
203
+ // The version probe carries the same merged settings value.
204
+ const spawns = (await Bun.file(`${stateFile}.spawns`).text())
205
+ .trim()
206
+ .split("\n")
207
+ .map((line) => JSON.parse(line) as string[]);
208
+ const probe = spawns.find((args) => args.at(-1) === "--version") ?? [];
209
+ expect(probe).toEqual([
210
+ "--settings",
211
+ argv[argv.indexOf("--settings") + 1] ?? "",
212
+ "--version",
213
+ ]);
214
+ },
215
+ { timeout: CHILD_PROCESS_TEST_BUDGET_MS },
216
+ );
217
+
218
+ test(
219
+ "invalid caller --settings refuses to start: no Claude child spawns",
220
+ async () => {
221
+ const directory = await temporaryDirectory();
222
+ // Built at runtime so no single source literal looks like a credential.
223
+ const sentinel = ["settings", "sentinel", crypto.randomUUID()].join("-");
224
+ await Bun.write(join(directory, "broken.json"), "{nope");
225
+ await Bun.write(join(directory, "array.json"), "[1]");
226
+ const cases: [string[], string][] = [
227
+ [["--settings", "missing.json"], "cannot be read (ENOENT)"],
228
+ [["--settings", "broken.json"], "is not valid JSON"],
229
+ [["--settings", `{"env":{"TOKEN":"${sentinel}"`], "is not valid JSON"],
230
+ [["--settings=array.json"], "is not a JSON object"],
231
+ [["--settings", "--max-turns", "3"], "--settings has no value"],
232
+ [["--settings", '--settings={"model":"x"}'], "--settings has no value"],
233
+ ];
234
+ for (const [index, [additionalArgs, message]] of cases.entries()) {
235
+ const stateFile = join(directory, `invalid-${index}.json`);
236
+ const sessionConfig = { ...config(directory, "cancel", stateFile), additionalArgs };
237
+ let summaries = 0;
238
+ const error = await new ClaudeAdapter(async () => {
239
+ summaries++;
240
+ })
241
+ .createSession(sessionConfig)
242
+ .then(
243
+ () => undefined,
244
+ (err: unknown) => err,
245
+ );
246
+
247
+ expect((error as Error).name).toBe("ClaudeSettingsConfigError");
248
+ expect((error as Error).message).toContain(message);
249
+ expect((error as Error).message).not.toContain(sentinel);
250
+ // The fixture records every start, the `--version` probe included.
251
+ expect(await Bun.file(`${stateFile}.spawns`).exists()).toBeFalse();
252
+ expect(summaries).toBe(0);
253
+ expect(
254
+ await Bun.file(getTaskFilePath(`${process.pid}-${sessionConfig.taskId}`)).exists(),
255
+ ).toBeFalse();
256
+ }
257
+ },
258
+ { timeout: CHILD_PROCESS_TEST_BUDGET_MS },
259
+ );
260
+
261
+ test(
262
+ "invalid CLAUDE_BINARY prefix --settings never reaches logs, errors, or a spawn",
263
+ async () => {
264
+ const directory = await temporaryDirectory();
265
+ const sentinel = ["prefix", "sentinel", crypto.randomUUID()].join("-");
266
+ const inline = `{"env":{"TOKEN":"${sentinel}"`;
267
+ const logged: string[] = [];
268
+ const capture = (...args: unknown[]) => {
269
+ logged.push(args.map(String).join(" "));
270
+ };
271
+ const logSpy = spyOn(console, "log").mockImplementation(capture);
272
+ const warnSpy = spyOn(console, "warn").mockImplementation(capture);
273
+ const errorSpy = spyOn(console, "error").mockImplementation(capture);
274
+ try {
275
+ for (const [index, flag] of [`--settings=${inline}`, `--settings ${inline}`].entries()) {
276
+ const stateFile = join(directory, `prefix-${index}.json`);
277
+ const sessionConfig = config(directory, "cancel", stateFile);
278
+ sessionConfig.env = {
279
+ ...sessionConfig.env,
280
+ CLAUDE_BINARY: `${process.execPath} ${fixturePath} ${flag}`,
281
+ };
282
+ const error = await new ClaudeAdapter(async () => {}).createSession(sessionConfig).then(
283
+ () => undefined,
284
+ (err: unknown) => err,
285
+ );
286
+
287
+ expect((error as Error).name).toBe("ClaudeSettingsConfigError");
288
+ expect((error as Error).message).toContain("inline JSON is not valid JSON");
289
+ expect((error as Error).message).not.toContain(sentinel);
290
+ expect(await Bun.file(`${stateFile}.spawns`).exists()).toBeFalse();
291
+ }
292
+ } finally {
293
+ logSpy.mockRestore();
294
+ warnSpy.mockRestore();
295
+ errorSpy.mockRestore();
296
+ }
297
+ expect(logged.filter((line) => line.includes(sentinel))).toEqual([]);
151
298
  },
152
299
  { timeout: CHILD_PROCESS_TEST_BUDGET_MS },
153
300
  );
@@ -1,9 +1,16 @@
1
+ import { appendFileSync } from "node:fs";
1
2
  import { rename } from "node:fs/promises";
2
3
 
3
4
  const mode = process.env.CLAUDE_SDK_FIXTURE_MODE;
4
5
  const stateFile = process.env.CLAUDE_SDK_FIXTURE_STATE_FILE;
5
6
  const sessionId = "11111111-1111-4111-8111-111111111111";
6
7
 
8
+ // Record every invocation, the `--version` probe included, before any exit, so
9
+ // a test can assert that no Claude child started at all.
10
+ if (stateFile) {
11
+ appendFileSync(`${stateFile}.spawns`, `${JSON.stringify(process.argv.slice(2))}\n`);
12
+ }
13
+
7
14
  if (process.argv.includes("--version")) {
8
15
  if (process.env.CLAUDE_SDK_FIXTURE_UNKNOWN_VERSION === "1") process.exit(1);
9
16
  console.log("2.1.263 (Claude Code)");