@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
@@ -9,13 +9,28 @@
9
9
 
10
10
  import { existsSync, readFileSync } from "node:fs";
11
11
  import { dirname, join } from "node:path";
12
+ import {
13
+ baseUrlOverride,
14
+ ignoredBaseUrlVar,
15
+ type OpenAICompatibleProvider,
16
+ openAICompatibleKeyVars,
17
+ openAICompatibleProvider,
18
+ openAICompatibleProviders,
19
+ providerBaseUrl,
20
+ providerForKeyVar,
21
+ providerKeyVars,
22
+ } from "@desplega/model-catalog";
12
23
  import type { OpenCode, OpenCodeEvent } from "@opencode/sdk";
13
24
  import {
14
25
  CONTEXT_FORMULA,
15
26
  clampContextPercent,
16
27
  getContextWindowSize,
17
28
  } from "../utils/context-window";
18
- import { validateOpencodeCredentials } from "../utils/credentials";
29
+ import {
30
+ openAICompatibleKeyLabels,
31
+ splitCredentialPool,
32
+ validateOpencodeCredentials,
33
+ } from "../utils/credentials";
19
34
  import { isEnvFlagEnabled } from "../utils/env-flag";
20
35
  import { fetchInstalledMcpServers } from "../utils/mcp-server-fetcher";
21
36
  import { swarmRuntimeInstanceId } from "../utils/multi-runtime";
@@ -24,7 +39,7 @@ import {
24
39
  getOpenRouterAttributionHeaders,
25
40
  getOpenRouterBaseUrl,
26
41
  } from "../utils/openrouter-base-url";
27
- import { scrubSecrets } from "../utils/secret-scrubber";
42
+ import { registerVolatileSecret, scrubSecrets } from "../utils/secret-scrubber";
28
43
  import { resolveSlashSkillPrompt } from "./codex-skill-resolver";
29
44
  import { CTX_MODE_NUDGE_EVERY } from "./ctx-mode-env";
30
45
  import { OpenRouterBilling } from "./opencode-billing";
@@ -61,8 +76,12 @@ export type OpencodeConfig = {
61
76
  provider?: Record<
62
77
  string,
63
78
  {
79
+ /** Env var names OpenCode reads the key from; `[]` stops it reading `process.env`. */
80
+ env?: string[];
81
+ npm?: string;
82
+ name?: string;
64
83
  options?: Record<string, unknown>;
65
- models?: Record<string, { options?: Record<string, unknown> }>;
84
+ models?: Record<string, { options?: Record<string, unknown>; [key: string]: unknown }>;
66
85
  }
67
86
  >;
68
87
  [key: string]: unknown;
@@ -80,18 +99,41 @@ function opencodeModelToCredKey(modelStr: string | undefined): string | null {
80
99
  if (provider === "anthropic") return "ANTHROPIC_API_KEY";
81
100
  if (provider === "openrouter") return "OPENROUTER_API_KEY";
82
101
  if (provider === "openai") return "OPENAI_API_KEY";
102
+ // OpenAI-compatible registry providers (`nan`, `helmcode`).
103
+ const direct = openAICompatibleProvider(provider);
104
+ if (direct) return direct.envVar;
83
105
  }
84
106
  return null;
85
107
  }
86
108
 
109
+ /**
110
+ * Env var names that satisfy `requiredKey`. The runner copies a registry
111
+ * key alias (`HELMCODE_API_NAN`) into its canonical var per task, but a
112
+ * check that reads `process.env` directly sees only the alias.
113
+ */
114
+ function credKeyNames(requiredKey: string): readonly string[] {
115
+ const direct = providerForKeyVar(requiredKey);
116
+ return direct ? providerKeyVars(direct) : [requiredKey];
117
+ }
118
+
119
+ /** Keys that satisfy opencode when no model pins a provider. */
120
+ const OPENCODE_ANY_KEY_VARS = [
121
+ "OPENROUTER_API_KEY",
122
+ "ANTHROPIC_API_KEY",
123
+ "OPENAI_API_KEY",
124
+ ...openAICompatibleKeyVars(),
125
+ ];
126
+
87
127
  /**
88
128
  * Opencode is satisfied by ANY of:
89
129
  * 1. `~/.local/share/opencode/auth.json` exists (the file `opencode auth login`
90
130
  * writes; OpenCode 2 imports it into its credential store on start).
91
131
  * 2. `MODEL_OVERRIDE` resolves to a provider-prefixed model — only that
92
- * provider's key is required.
132
+ * provider's key is required (a registry provider also accepts its key
133
+ * aliases: `nan/*` takes `HELMCODE_API_NAN`).
93
134
  * 3. Otherwise any one of OPENROUTER_API_KEY / ANTHROPIC_API_KEY /
94
- * OPENAI_API_KEY suffices.
135
+ * OPENAI_API_KEY / a registry provider key (canonical or alias)
136
+ * suffices.
95
137
  */
96
138
  export function checkOpencodeCredentials(
97
139
  env: Record<string, string | undefined>,
@@ -106,7 +148,7 @@ export function checkOpencodeCredentials(
106
148
 
107
149
  const requiredKey = opencodeModelToCredKey(env.MODEL_OVERRIDE);
108
150
  if (requiredKey) {
109
- if (env[requiredKey]) {
151
+ if (credKeyNames(requiredKey).some((name) => env[name])) {
110
152
  return { ready: true, missing: [], satisfiedBy: "env" };
111
153
  }
112
154
  return {
@@ -116,13 +158,26 @@ export function checkOpencodeCredentials(
116
158
  };
117
159
  }
118
160
 
119
- if (env.OPENROUTER_API_KEY || env.ANTHROPIC_API_KEY || env.OPENAI_API_KEY) {
161
+ if (OPENCODE_ANY_KEY_VARS.some((name) => env[name])) {
120
162
  return { ready: true, missing: [], satisfiedBy: "env" };
121
163
  }
122
164
  return {
123
165
  ready: false,
124
- missing: ["OPENROUTER_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY", authFile],
125
- hint: "Set one of OPENROUTER_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY (any one suffices), or run `opencode auth login` to create ~/.local/share/opencode/auth.json.",
166
+ missing: [
167
+ "OPENROUTER_API_KEY",
168
+ "ANTHROPIC_API_KEY",
169
+ "OPENAI_API_KEY",
170
+ ...openAICompatibleProviders().map((p) => p.envVar),
171
+ authFile,
172
+ ],
173
+ hint: `Set one of ${[
174
+ "OPENROUTER_API_KEY",
175
+ "ANTHROPIC_API_KEY",
176
+ "OPENAI_API_KEY",
177
+ ...openAICompatibleKeyLabels(),
178
+ ].join(
179
+ " / ",
180
+ )} (any one suffices), or run \`opencode auth login\` to create ~/.local/share/opencode/auth.json.`,
126
181
  };
127
182
  }
128
183
 
@@ -217,6 +272,150 @@ export function applyOpenRouterBaseUrlOverride(
217
272
  };
218
273
  }
219
274
 
275
+ /**
276
+ * OpenCode provider block for an OpenAI-compatible registry provider that
277
+ * OpenCode does not ship (`catalog: "registry"`, e.g. `helmcode`). `apiKey` is
278
+ * a literal key (embedded host) or an `{env:NAME}` reference (ACP
279
+ * subprocess). `baseUrl` is the `<ID>_BASE_URL` override or the registry
280
+ * default. `env: []` keeps OpenCode from reading a key out of `process.env`.
281
+ * A `modelId` missing from the registry (accepted through `allowCustomModel`)
282
+ * gets a bare entry so OpenCode still resolves it instead of falling back to
283
+ * its default model.
284
+ */
285
+ export function buildOpenAICompatibleProviderBlock(
286
+ provider: OpenAICompatibleProvider,
287
+ apiKey: string,
288
+ modelId?: string,
289
+ baseUrl: string = provider.baseUrl,
290
+ ): NonNullable<OpencodeConfig["provider"]>[string] {
291
+ const models: Record<string, Record<string, unknown>> = {};
292
+ for (const m of provider.models) {
293
+ models[m.id] = {
294
+ name: m.name,
295
+ tool_call: true,
296
+ reasoning: m.reasoning,
297
+ limit: { context: m.contextWindow, output: m.maxOutputTokens },
298
+ };
299
+ }
300
+ if (modelId && !models[modelId]) models[modelId] = { name: modelId, tool_call: true };
301
+ return {
302
+ npm: provider.npm,
303
+ name: provider.displayName,
304
+ env: [],
305
+ options: { baseURL: baseUrl, apiKey },
306
+ models,
307
+ };
308
+ }
309
+
310
+ /**
311
+ * Built-in OpenCode providers whose key OpenCode reads from `process.env`:
312
+ * `openrouter` plus every registry provider models.dev lists
313
+ * (`catalog: "models.dev"`, e.g. `nan`), which OpenCode ships built in.
314
+ */
315
+ function envBoundKeyVars(providerId: string): readonly string[] | undefined {
316
+ if (providerId === "openrouter") return ["OPENROUTER_API_KEY"];
317
+ const direct = openAICompatibleProvider(providerId);
318
+ return direct?.catalog === "models.dev" ? providerKeyVars(direct) : undefined;
319
+ }
320
+
321
+ /** Registry providers OpenCode does not ship (`catalog: "registry"`); they get a full custom block. */
322
+ function customBlockProvider(providerId: string): OpenAICompatibleProvider | undefined {
323
+ const direct = openAICompatibleProvider(providerId);
324
+ return direct?.catalog === "registry" ? direct : undefined;
325
+ }
326
+
327
+ /**
328
+ * One key from the first non-empty var in `names`. The runner's per-task env
329
+ * already holds one picked pool member; a value that still carries a comma
330
+ * pool (no runner, or `process.env`) yields its first member.
331
+ */
332
+ function pickProviderKey(
333
+ env: Record<string, string | undefined>,
334
+ names: readonly string[],
335
+ ): { key: string; name: string } | undefined {
336
+ for (const name of names) {
337
+ const value = env[name]?.trim();
338
+ if (!value) continue;
339
+ const key = splitCredentialPool(value)[0];
340
+ if (key) return { key, name };
341
+ }
342
+ return undefined;
343
+ }
344
+
345
+ /**
346
+ * Hand the embedded host exactly one key for the active model's provider.
347
+ *
348
+ * Embedded OpenCode reads a built-in provider's key from the shared
349
+ * `process.env`, and that value beats `options.apiKey`. The entrypoint exports
350
+ * a credential pool there as a raw comma list, and a key saved from the
351
+ * dashboard after boot never reaches `process.env` at all, so both 401 without
352
+ * this splice. For `openrouter` and the built-in registry providers (`nan`,
353
+ * `vercel`) it sets `env: []` plus `options.apiKey` (and `options.baseURL`
354
+ * when `<ID>_BASE_URL` is set; a `baseUrlLocked` provider gets none); for registry providers OpenCode does not ship
355
+ * (`helmcode`) it writes the full custom provider block at
356
+ * `<ID>_BASE_URL` or the registry default. No splice when the key is absent
357
+ * (credStatus reports that).
358
+ *
359
+ * The key lives only in the in-memory config string handed to the SDK.
360
+ * Mutates `opencodeConfig` in place and returns the env var the key came from,
361
+ * or `undefined` when nothing was spliced.
362
+ */
363
+ export function applyProviderKeySplice(
364
+ opencodeConfig: OpencodeConfig,
365
+ model: string | undefined,
366
+ env: Record<string, string | undefined>,
367
+ ): { key: string; name: string } | undefined {
368
+ const slash = model?.indexOf("/") ?? -1;
369
+ if (!model || slash <= 0) return undefined;
370
+ const providerId = model.slice(0, slash);
371
+ const existing = opencodeConfig.provider?.[providerId];
372
+
373
+ const custom = customBlockProvider(providerId);
374
+ if (custom) {
375
+ const picked = pickProviderKey(env, providerKeyVars(custom));
376
+ if (!picked) return undefined;
377
+ const block = buildOpenAICompatibleProviderBlock(
378
+ custom,
379
+ picked.key,
380
+ model.slice(slash + 1),
381
+ providerBaseUrl(custom, env),
382
+ );
383
+ opencodeConfig.provider = {
384
+ ...opencodeConfig.provider,
385
+ [providerId]: {
386
+ ...existing,
387
+ ...block,
388
+ options: { ...existing?.options, ...block.options },
389
+ models: { ...existing?.models, ...block.models },
390
+ },
391
+ };
392
+ return picked;
393
+ }
394
+
395
+ const names = envBoundKeyVars(providerId);
396
+ if (!names) return undefined;
397
+ const picked = pickProviderKey(env, names);
398
+ if (!picked) return undefined;
399
+ const direct = openAICompatibleProvider(providerId);
400
+ const baseURL = direct ? baseUrlOverride(direct, env) : undefined;
401
+ const options = { ...existing?.options, ...(baseURL ? { baseURL } : {}), apiKey: picked.key };
402
+ if (direct?.baseUrlLocked) {
403
+ // OpenCode's built-in SDK (AI Gateway) knows its endpoint; any URL here is wrong.
404
+ const ignored = ignoredBaseUrlVar(direct, env);
405
+ if (ignored || options.baseURL !== undefined) {
406
+ console.warn(
407
+ `[opencode] ${ignored ?? `provider.${providerId}.options.baseURL`} is ignored: ${direct.displayName} uses its built-in endpoint`,
408
+ );
409
+ }
410
+ delete options.baseURL;
411
+ }
412
+ opencodeConfig.provider = {
413
+ ...opencodeConfig.provider,
414
+ [providerId]: { ...existing, env: [], options },
415
+ };
416
+ return picked;
417
+ }
418
+
220
419
  const SWARM_AGENT_NAME = "swarm";
221
420
  const TRANSCRIPT_TAIL_CHARS = 20_000;
222
421
  const TOOL_RESULT_PREVIEW_CHARS = 500;
@@ -547,8 +746,11 @@ export class OpencodeSession implements ProviderSession {
547
746
  }
548
747
 
549
748
  /** Settle the session as failed; also used when the event stream dies. */
550
- fail(message: string): void {
749
+ fail(rawMessage: string): void {
551
750
  if (this.aborted || this.completed) return;
751
+ // SDK and provider errors can echo request details; the message reaches
752
+ // the runner's logs and the task's failure reason.
753
+ const message = scrubSecrets(rawMessage);
552
754
  this.emit({ type: "error", message });
553
755
  void this.finish(async () => ({
554
756
  exitCode: 1,
@@ -733,6 +935,12 @@ export class OpencodeAdapter implements ProviderAdapter {
733
935
  },
734
936
  };
735
937
 
938
+ // One picked key for the model's provider (D3). It stays in this
939
+ // in-memory config; registering it lets every scrubbed egress redact it
940
+ // even when it never reached `process.env` (a key saved after boot).
941
+ const splicedKey = applyProviderKeySplice(opencodeConfig, config.model, env);
942
+ if (splicedKey) registerVolatileSecret(splicedKey.key, splicedKey.name);
943
+
736
944
  // Splice the provider-keyed reasoning options into
737
945
  // `provider[providerId].models[modelId].options`. `off` (and any
738
946
  // capability-rejected level) resolves to `noop`.
@@ -774,21 +982,30 @@ export class OpencodeAdapter implements ProviderAdapter {
774
982
  const { sdk, version: opencodeVersion } = await loadOpencodeSdkImpl();
775
983
  let listTools: OpencodeToolLister | undefined;
776
984
  const billing = config.model.startsWith("openrouter/") ? new OpenRouterBilling() : undefined;
777
- const host = await sdk.OpenCode.create({
778
- config: { content: JSON.stringify(opencodeConfig) },
779
- plugins: [
780
- createOpencodeSwarmPlugin(swarm, {
781
- onToolRegistry: (lister) => {
782
- listTools = lister;
783
- },
784
- ...(billing ? { onOpenRouterResponse: (response) => billing.observe(response) } : {}),
785
- }),
786
- ],
787
- log: {
788
- level: "warn",
789
- emit: (entry) => console.warn(scrubSecrets(`[opencode] ${entry.message}`)),
790
- },
791
- });
985
+ // The config string carries the spliced provider key and the swarm MCP
986
+ // bearer. It goes to the SDK only; nothing logs, writes, or uploads it,
987
+ // and errors raised while the SDK parses it are scrubbed before they
988
+ // propagate to the runner.
989
+ let host: OpenCode.Interface;
990
+ try {
991
+ host = await sdk.OpenCode.create({
992
+ config: { content: JSON.stringify(opencodeConfig) },
993
+ plugins: [
994
+ createOpencodeSwarmPlugin(swarm, {
995
+ onToolRegistry: (lister) => {
996
+ listTools = lister;
997
+ },
998
+ ...(billing ? { onOpenRouterResponse: (response) => billing.observe(response) } : {}),
999
+ }),
1000
+ ],
1001
+ log: {
1002
+ level: "warn",
1003
+ emit: (entry) => console.warn(scrubSecrets(`[opencode] ${entry.message}`)),
1004
+ },
1005
+ });
1006
+ } catch (err) {
1007
+ throw new Error(scrubSecrets(`opencode host failed to start: ${errorMessage(err, "")}`));
1008
+ }
792
1009
 
793
1010
  let sessionId: string;
794
1011
  try {
@@ -799,7 +1016,7 @@ export class OpencodeAdapter implements ProviderAdapter {
799
1016
  sessionId = created.id;
800
1017
  } catch (err) {
801
1018
  await host.close().catch(() => {});
802
- throw err;
1019
+ throw new Error(scrubSecrets(`opencode session create failed: ${errorMessage(err, "")}`));
803
1020
  }
804
1021
 
805
1022
  const session = new OpencodeSession(
@@ -32,6 +32,27 @@ export interface OpencodeSwarmPluginOptions {
32
32
  onOpenRouterResponse?: (response: Response) => void;
33
33
  }
34
34
 
35
+ /** Keys OpenCode sets on each shell call, on top of its base env. */
36
+ const OPENCODE_SHELL_ENV_KEYS = ["TERM", "OPENCODE_TERMINAL"] as const;
37
+
38
+ /**
39
+ * Rewrites one OpenCode shell call's env, in place, to the session's own env
40
+ * plus the keys OpenCode set for the call. Nothing else from the process env
41
+ * gets through.
42
+ */
43
+ function applyOpencodeShellEnv(
44
+ callEnv: Record<string, string | undefined>,
45
+ sessionEnv: Record<string, string | undefined>,
46
+ ): void {
47
+ const keep = new Set<string>(OPENCODE_SHELL_ENV_KEYS);
48
+ for (const key of Object.keys(callEnv)) {
49
+ if (!keep.has(key)) delete callEnv[key];
50
+ }
51
+ for (const [key, value] of Object.entries(sessionEnv)) {
52
+ if (value !== undefined && !keep.has(key)) callEnv[key] = value;
53
+ }
54
+ }
55
+
35
56
  export function createOpencodeSwarmPlugin(
36
57
  config: SwarmHooksConfig,
37
58
  options: OpencodeSwarmPluginOptions = {},
@@ -41,6 +62,16 @@ export function createOpencodeSwarmPlugin(
41
62
  async setup(ctx) {
42
63
  options.onToolRegistry?.(() => ctx.tool.list());
43
64
 
65
+ // OpenCode runs in-process, so its shell tool spawns with process.env,
66
+ // which every concurrent session shares and which never sees this
67
+ // task's env (rows admitted or withheld by fetchResolvedEnv after boot).
68
+ const sessionEnv = config.env;
69
+ if (sessionEnv) {
70
+ await ctx.shell.hook("create.before", (event) => {
71
+ applyOpencodeShellEnv(event.env, sessionEnv);
72
+ });
73
+ }
74
+
44
75
  // A throw blocks the tool; OpenCode hands the message back to the model.
45
76
  await ctx.tool.hook("execute.before", async (event) => {
46
77
  if (!config.isLead && config.taskId) {