@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,37 @@
1
+ /**
2
+ * Test-only seams for the Chat SDK adapters. Production code never calls
3
+ * `setChatSdkTestHooks`, so every hook is unset in a running API. The
4
+ * operator schema rejects `webhookVerifier`, and this module is the only
5
+ * other way to set one.
6
+ */
7
+ export interface ChatSdkTestHooks {
8
+ /**
9
+ * Replaces the Bot Framework JWT check of the Teams adapter. Tests cannot
10
+ * mint a Microsoft-signed token, so they inject `() => true`.
11
+ */
12
+ teamsWebhookVerifier?: (request: Request, body: string) => boolean | Promise<boolean>;
13
+ /**
14
+ * Bot token factory for the Teams adapter, so an outbound send needs no
15
+ * MSAL call to login.microsoftonline.com.
16
+ */
17
+ teamsToken?: () => string;
18
+ /**
19
+ * Skips the Discord Gateway loop. The loop logs in through `discord.js`
20
+ * against the real Discord API, which `apiUrl` does not redirect.
21
+ */
22
+ disableDiscordGateway?: boolean;
23
+ /** Typing keepalive interval, shortened so a test sees several renewals. */
24
+ typingIntervalMs?: number;
25
+ /** Typing keepalive hard cap, shortened so a test can reach it. */
26
+ typingMaxMs?: number;
27
+ }
28
+
29
+ let hooks: ChatSdkTestHooks = {};
30
+
31
+ export function setChatSdkTestHooks(next: ChatSdkTestHooks): void {
32
+ hooks = { ...next };
33
+ }
34
+
35
+ export function getChatSdkTestHooks(): Readonly<ChatSdkTestHooks> {
36
+ return hooks;
37
+ }
@@ -0,0 +1,100 @@
1
+ import type { Attachment } from "chat";
2
+ import { type AdditiveBuffer, createAdditiveBuffer } from "@/tasks/additive-buffer";
3
+ import { trackBackground } from "./background";
4
+ import { flushChatThread } from "./inbound";
5
+ import { stopTypingKeepalive } from "./typing";
6
+
7
+ /** One inbound message waiting in its thread's buffer. */
8
+ export interface ChatBufferedMessage {
9
+ messageId: string;
10
+ channelId: string;
11
+ /** Mention-stripped text. */
12
+ text: string;
13
+ userId: string;
14
+ userName?: string;
15
+ botMentioned: boolean;
16
+ isDirectMessage: boolean;
17
+ attachments: Attachment[];
18
+ }
19
+
20
+ const DEFAULT_BUFFER_MS = 10_000;
21
+
22
+ function configuredBufferMs(): number {
23
+ const value = Number(process.env.CHAT_SDK_BUFFER_MS);
24
+ return Number.isFinite(value) && value > 0 ? value : DEFAULT_BUFFER_MS;
25
+ }
26
+
27
+ let buffer: AdditiveBuffer<ChatBufferedMessage> | undefined;
28
+ let bufferMs = 0;
29
+ /** Set while shutdown flushes every waiting thread; those flushes keep `dependsOn`. */
30
+ let draining = false;
31
+ /**
32
+ * Tail of each thread's flush chain. The additive buffer clears a key before
33
+ * it awaits `onFlush`, so a second flush of one thread can start while the
34
+ * first still routes, downloads files or creates its task. Chaining keeps the
35
+ * task order (and `dependsOn`) of one thread; different threads run in parallel.
36
+ */
37
+ const flushChains = new Map<string, Promise<void>>();
38
+
39
+ /** Run `work` after every earlier flush of `threadId`. A failed flush does not block the next one. */
40
+ function runInThreadOrder(threadId: string, work: () => Promise<void>): Promise<void> {
41
+ const previous = flushChains.get(threadId) ?? Promise.resolve();
42
+ const run = previous.then(work);
43
+ const tail = run.catch(() => {});
44
+ flushChains.set(threadId, tail);
45
+ void tail.then(() => {
46
+ if (flushChains.get(threadId) === tail) flushChains.delete(threadId);
47
+ });
48
+ return run;
49
+ }
50
+
51
+ /**
52
+ * Keyed by the raw SDK `thread.id` (`<adapter>:<platform id>`, colons
53
+ * included); the key is never split. A changed `CHAT_SDK_BUFFER_MS` applies
54
+ * once no thread is waiting on the old timer.
55
+ */
56
+ function getBuffer(): AdditiveBuffer<ChatBufferedMessage> {
57
+ const ms = configuredBufferMs();
58
+ if (!buffer || (ms !== bufferMs && buffer.keys().length === 0)) {
59
+ bufferMs = ms;
60
+ buffer = createAdditiveBuffer<ChatBufferedMessage>({
61
+ timeoutMs: ms,
62
+ label: "chat",
63
+ onFlush: async (items, threadId, reason) => {
64
+ // Read now: `draining` can change while this flush waits in the chain.
65
+ const immediate = reason === "manual" && !draining;
66
+ // Tracked so shutdown drains timer-driven flushes before the DB closes.
67
+ const work = runInThreadOrder(threadId, () => flushChatThread(threadId, items, immediate));
68
+ trackBackground(work);
69
+ // A failed flush created no task, so no relay will stop the indicator.
70
+ await work.catch(() => stopTypingKeepalive(threadId));
71
+ },
72
+ });
73
+ }
74
+ return buffer;
75
+ }
76
+
77
+ /** Add a message to its thread's buffer and reset the debounce timer. */
78
+ export function bufferChatMessage(threadId: string, item: ChatBufferedMessage): void {
79
+ getBuffer().enqueue(threadId, item);
80
+ }
81
+
82
+ /** Flush now with no `dependsOn` (the `!now` command). */
83
+ export async function instantFlushChat(threadId: string): Promise<void> {
84
+ await getBuffer().instantFlush(threadId);
85
+ }
86
+
87
+ export function isChatThreadBuffered(threadId: string): boolean {
88
+ return buffer?.isBuffered(threadId) ?? false;
89
+ }
90
+
91
+ /** Shutdown: turn every waiting thread into its task instead of dropping it. */
92
+ export async function flushAllChatBuffers(): Promise<void> {
93
+ if (!buffer) return;
94
+ draining = true;
95
+ try {
96
+ for (const threadId of buffer.keys()) await buffer.instantFlush(threadId);
97
+ } finally {
98
+ draining = false;
99
+ }
100
+ }
@@ -0,0 +1,65 @@
1
+ import { errorMessage } from "./logger";
2
+ import { getChatInstance, isChatSdkStopping } from "./runtime";
3
+ import { assertTrustedChatTarget } from "./teams-origin";
4
+ import { getChatSdkTestHooks } from "./test-hooks";
5
+
6
+ /** Telegram shows one typing action for about 5 s, so renew it before that. */
7
+ const TYPING_INTERVAL_MS = 4_000;
8
+ /** Hard cap: a task that never relays must not show typing forever. */
9
+ const TYPING_MAX_MS = 15 * 60 * 1000;
10
+
11
+ const keepalives = new Map<string, ReturnType<typeof setInterval>>();
12
+
13
+ /**
14
+ * Show the typing indicator in `threadId` until `stopTypingKeepalive`, the
15
+ * hard cap, or the adapter stops. Idempotent per thread id. Adapters without
16
+ * `startTyping` and untrusted Teams service URLs are skipped.
17
+ */
18
+ export function startTypingKeepalive(adapter: string, threadId: string): void {
19
+ if (keepalives.has(threadId)) return;
20
+ // A shutdown flush creates tasks too; no indicator for a reply that comes after a restart.
21
+ if (isChatSdkStopping()) return;
22
+ // Typing sends the Teams bot token to the service URL in the thread id.
23
+ try {
24
+ assertTrustedChatTarget(threadId);
25
+ } catch {
26
+ return;
27
+ }
28
+ if (typeof getChatInstance(adapter)?.getAdapter(adapter)?.startTyping !== "function") return;
29
+
30
+ const hooks = getChatSdkTestHooks();
31
+ const maxMs = hooks.typingMaxMs ?? TYPING_MAX_MS;
32
+ const startedAt = Date.now();
33
+ let warned = false;
34
+ const sendTyping = async (): Promise<void> => {
35
+ const target = getChatInstance(adapter)?.getAdapter(adapter);
36
+ if (Date.now() - startedAt >= maxMs || typeof target?.startTyping !== "function") {
37
+ stopTypingKeepalive(threadId);
38
+ return;
39
+ }
40
+ try {
41
+ await target.startTyping(threadId);
42
+ } catch (error) {
43
+ if (warned) return;
44
+ warned = true;
45
+ console.warn(`[chat-sdk] Typing indicator failed for ${threadId}: ${errorMessage(error)}`);
46
+ }
47
+ };
48
+ const timer = setInterval(() => void sendTyping(), hooks.typingIntervalMs ?? TYPING_INTERVAL_MS);
49
+ timer.unref();
50
+ keepalives.set(threadId, timer);
51
+ void sendTyping();
52
+ }
53
+
54
+ export function stopTypingKeepalive(threadId: string): void {
55
+ const timer = keepalives.get(threadId);
56
+ if (!timer) return;
57
+ clearInterval(timer);
58
+ keepalives.delete(threadId);
59
+ }
60
+
61
+ /** Stop every keepalive. For `stopChatSdk`. */
62
+ export function stopAllTypingKeepalives(): void {
63
+ for (const timer of keepalives.values()) clearInterval(timer);
64
+ keepalives.clear();
65
+ }
package/src/jira/adf.ts CHANGED
@@ -13,6 +13,8 @@
13
13
  * debug line is emitted so unhandled cases surface during dev.
14
14
  */
15
15
 
16
+ import { getNodeEnv } from "../utils/node-env";
17
+
16
18
  type AdfNode = {
17
19
  type?: string;
18
20
  text?: string;
@@ -93,7 +95,7 @@ export function extractText(adf: unknown): string {
93
95
  }
94
96
  default: {
95
97
  // Unknown node — descend into content if present.
96
- if (process.env.NODE_ENV !== "production" && type) {
98
+ if (getNodeEnv() !== "production" && type) {
97
99
  console.log(`[jira.adf] unknown node type: ${type}`);
98
100
  }
99
101
  for (const child of asNodeArray(node.content)) visit(child);
@@ -9,6 +9,8 @@ const RESERVED_FAMILIES = new Map([
9
9
  // step-9: Comb's "Send to swarm" claims (`comb:sent`).
10
10
  ["comb", "namespace is reserved for Comb; use POST /api/comb/review-batches"],
11
11
  ["worker", "namespace is reserved for worker nudges"],
12
+ // Durable dedupe for SLACK_BLOCK_EXTERNAL_CHANNELS alerts (src/slack/external-channel.ts).
13
+ ["slack-external-alert", "namespace is reserved for Slack external channel alerts"],
12
14
  ]);
13
15
 
14
16
  export function isReservedNamespace(namespace: string): boolean {
@@ -1,5 +1,6 @@
1
1
  import * as oauth from "oauth4webapi";
2
2
  import { isEnvFlagEnabled } from "../utils/env-flag";
3
+ import { isDevOrTestNodeEnv } from "../utils/node-env";
3
4
  import { registerVolatileSecret, scrubSecrets } from "../utils/secret-scrubber";
4
5
 
5
6
  /**
@@ -113,19 +114,22 @@ export function publicEndpointSsrfOptions(): SsrfGuardOptions {
113
114
  // or the explicit override flag — may fetch private/insecure endpoints.
114
115
  // Local dev entrypoints (`bun run dev:http` / `start:http`) set
115
116
  // NODE_ENV=development.
116
- const env = process.env.NODE_ENV;
117
- const dev =
118
- env === "development" || env === "test" || process.env.ALLOW_PRIVATE_NETWORK_URLS === "true";
117
+ const dev = isDevOrTestNodeEnv() || process.env.ALLOW_PRIVATE_NETWORK_URLS === "true";
119
118
  return {
120
119
  allowPrivateHosts: dev,
121
120
  allowInsecure: dev,
122
121
  };
123
122
  }
124
123
 
125
- function defaultSsrfOptions(): SsrfGuardOptions {
124
+ /**
125
+ * Guard for MCP OAuth endpoints (discovery, DCR, token exchange, refresh,
126
+ * revocation). http:// is allowed only in an explicit development/test
127
+ * environment; private hosts stay behind MCP_OAUTH_ALLOW_PRIVATE_HOSTS.
128
+ */
129
+ export function mcpOAuthSsrfOptions(): SsrfGuardOptions {
126
130
  return {
127
131
  allowPrivateHosts: isEnvFlagEnabled("MCP_OAUTH_ALLOW_PRIVATE_HOSTS", false),
128
- allowInsecure: process.env.NODE_ENV !== "production",
132
+ allowInsecure: isDevOrTestNodeEnv(),
129
133
  };
130
134
  }
131
135
 
@@ -144,7 +148,7 @@ async function safeFetch(
144
148
  init?: RequestInit,
145
149
  timeoutMs: number = DEFAULT_FETCH_TIMEOUT_MS,
146
150
  ): Promise<Response> {
147
- assertUrlSafe(url, defaultSsrfOptions());
151
+ assertUrlSafe(url, mcpOAuthSsrfOptions());
148
152
  return fetch(url, { ...init, signal: init?.signal ?? AbortSignal.timeout(timeoutMs) });
149
153
  }
150
154
 
package/src/otel-impl.ts CHANGED
@@ -30,6 +30,7 @@ import { NodeSDK } from "@opentelemetry/sdk-node";
30
30
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
31
31
  import pkg from "../package.json";
32
32
  import type { DbRetentionSweepMetric, SpanOptions, SwarmSpan, SwarmSpanKind } from "./otel";
33
+ import { getNodeEnv } from "./utils/node-env";
33
34
  import { scrubSecrets } from "./utils/secret-scrubber";
34
35
 
35
36
  type AttributeValue = string | number | boolean | string[] | number[] | boolean[];
@@ -174,7 +175,7 @@ export async function boot(serviceRole: string, options: BootOptions = {}): Prom
174
175
 
175
176
  const configuredResourceAttributes = parseResourceAttributes();
176
177
  const deploymentEnvironment =
177
- configuredResourceAttributes["deployment.environment"] || process.env.NODE_ENV || "development";
178
+ configuredResourceAttributes["deployment.environment"] || getNodeEnv() || "development";
178
179
  const serviceName = resolveServiceName(serviceRole);
179
180
  sdk = new NodeSDK({
180
181
  resource: resourceFromAttributes({
@@ -1,4 +1,5 @@
1
- import { createPageVersion, getPage, getPageVersions } from "../be/db";
1
+ import { createPageVersion, getPage } from "../be/db";
2
+ import { getPageEditCounter } from "../be/db/page-versions";
2
3
  import type { PageSnapshot, PageVersion } from "../types";
3
4
 
4
5
  /**
@@ -8,7 +9,7 @@ import type { PageSnapshot, PageVersion } from "../types";
8
9
  * Mirrors `snapshotWorkflow` (src/workflows/version.ts:13-44).
9
10
  *
10
11
  * 1. Load current page state
11
- * 2. Get max version number for this page (page_versions ORDER BY version DESC)
12
+ * 2. Get max version number for this page (MAX(page_versions.version))
12
13
  * 3. Insert page_versions row with version+1 and the pre-update snapshot
13
14
  *
14
15
  * Throws on missing parent. Callers in HTTP handlers wrap this in a try/catch
@@ -24,9 +25,7 @@ export async function snapshotPage(
24
25
  throw new Error(`Page ${pageId} not found — cannot create snapshot`);
25
26
  }
26
27
 
27
- const existingVersions = await getPageVersions(pageId);
28
- const maxVersion = existingVersions.length > 0 ? existingVersions[0]!.version : 0;
29
- const nextVersion = maxVersion + 1;
28
+ const nextVersion = await getPageEditCounter(pageId);
30
29
 
31
30
  const snapshot: PageSnapshot = {
32
31
  title: page.title,
@@ -8,11 +8,15 @@
8
8
  * E outputs (gated on AGENT_FS_API_URL and the pages capability)
9
9
  * H deployment-gated notes: slack, steering
10
10
  * I tools and skills (harness tool discovery, skills, MCP server names)
11
- * J agent notes: CLAUDE.md for codex, opencode, pi, only when edited
12
- * K repository (per task)
11
+ * J agent notes: CLAUDE.md, only when edited
13
12
  *
14
- * The runner appends the requester profile, the operator SYSTEM_PROMPT, and a
15
- * cwd warning after this.
13
+ * The runner appends the operator SYSTEM_PROMPT after this.
14
+ *
15
+ * Nothing here may vary per task, requester, or session: the provider caches
16
+ * the system prompt as a prefix, and one changed byte re-bills everything
17
+ * after it. Per-task context (repository, scripts-only Slack, requester
18
+ * profile, cwd warning) goes into the task prompt instead; see
19
+ * `getTaskContextPrompt` and the runner's `buildTaskSessionPrompts`.
16
20
  */
17
21
 
18
22
  import type { ProviderTraits } from "../providers/types";
@@ -44,13 +48,28 @@ const BOOTSTRAP_TOTAL_MAX_CHARS = 120_000;
44
48
 
45
49
  /**
46
50
  * Per-section cap applied to the *repo* CLAUDE.md when it is inlined (opencode
47
- * only). The agent-swarm OSS one is ~18 KB and the biggest volatile component
48
- * of the system prompt.
51
+ * only). The agent-swarm OSS one is ~18 KB and the biggest piece of the task
52
+ * context.
49
53
  */
50
54
  const REPO_CLAUDE_MD_MAX_CHARS = 12_000;
51
55
 
52
- /** Providers that do not load the agent's CLAUDE.md natively. Claude does. */
53
- const CLAUDE_MD_INJECT_PROVIDERS: ReadonlySet<string> = new Set(["codex", "opencode", "pi"]);
56
+ /**
57
+ * Providers that get the agent's CLAUDE.md in the system prompt. Claude also
58
+ * keeps the editable copy at ~/.claude/CLAUDE.md but excludes it from its
59
+ * memory load (claude-flag-settings.ts), so it is read once, from the cached
60
+ * system prompt instead of an uncached message.
61
+ */
62
+ const CLAUDE_MD_INJECT_PROVIDERS: ReadonlySet<string> = new Set([
63
+ "claude",
64
+ "codex",
65
+ "opencode",
66
+ "pi",
67
+ ]);
68
+
69
+ /** Where each provider's editable CLAUDE.md lives, for the truncation notice. */
70
+ function claudeMdSourcePath(provider: string): string {
71
+ return provider === "claude" ? "~/.claude/CLAUDE.md" : "/workspace/CLAUDE.md";
72
+ }
54
73
 
55
74
  /** Providers that get the repo CLAUDE.md inlined until native loading is verified. */
56
75
  const REPO_CLAUDE_MD_INLINE_PROVIDERS: ReadonlySet<string> = new Set(["opencode"]);
@@ -95,36 +114,61 @@ export type BasePromptArgs = {
95
114
  soulMd?: string;
96
115
  identityMd?: string;
97
116
  claudeMd?: string;
98
- repoContext?: {
99
- claudeMd?: string | null;
100
- clonePath: string;
101
- warning?: string | null;
102
- autoStashes?: { ref: string; message: string }[];
103
- guidelines?: {
104
- prChecks: string[];
105
- mergeChecks: string[];
106
- allowMerge?: boolean;
107
- review: string[];
108
- } | null;
109
- };
110
- /** Slack context from the current task, if present */
111
- slackContext?: { channelId: string; threadTs?: string };
112
117
  /** Pre-fetched skill summaries for the tools and skills section */
113
118
  skillsSummary?: { name: string; description: string }[];
114
119
  /** Names of the MCP servers connected for this agent */
115
120
  mcpServers?: string[];
116
121
  };
117
122
 
118
- export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
119
- const { role, agentId, traits } = args;
123
+ export type RepoContext = {
124
+ claudeMd?: string | null;
125
+ clonePath: string;
126
+ warning?: string | null;
127
+ autoStashes?: { ref: string; message: string }[];
128
+ guidelines?: {
129
+ prChecks: string[];
130
+ mergeChecks: string[];
131
+ allowMerge?: boolean;
132
+ review: string[];
133
+ } | null;
134
+ };
135
+
136
+ export type SlackTaskContext = { channelId: string; threadTs?: string };
137
+
138
+ /** Inputs for the per-task context that rides in the task prompt. */
139
+ export type TaskContextPromptArgs = Pick<
140
+ BasePromptArgs,
141
+ "role" | "traits" | "provider" | "scriptsOnly" | "serverCapabilities"
142
+ > & {
143
+ repoContext?: RepoContext;
144
+ /** Slack context from the current task, if present */
145
+ slackContext?: SlackTaskContext;
146
+ };
147
+
148
+ function promptFlags(args: Pick<BasePromptArgs, "traits" | "provider" | "scriptsOnly">) {
120
149
  const {
121
150
  hasMcp = true,
122
151
  hasToolSearch = false,
123
152
  hasLocalEnvironment: hasLocalEnv = true,
124
153
  nativeSkillDiscovery = true,
125
- } = traits ?? {};
154
+ } = args.traits ?? {};
155
+ const scriptsOnly = args.scriptsOnly ?? process.env.SCRIPTS_ONLY_MCP === "true";
156
+ return {
157
+ hasMcp,
158
+ hasToolSearch,
159
+ hasLocalEnv,
160
+ nativeSkillDiscovery,
161
+ provider: args.provider ?? "claude",
162
+ // Experimental scripts-only MCP surface (code-mode).
163
+ scriptsOnlyMode: hasMcp && scriptsOnly,
164
+ };
165
+ }
166
+
167
+ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
168
+ const { role, agentId, traits } = args;
169
+ const { hasMcp, hasToolSearch, hasLocalEnv, nativeSkillDiscovery, provider, scriptsOnlyMode } =
170
+ promptFlags(args);
126
171
  const steerModes = traits?.steerModes ?? [];
127
- const provider = args.provider ?? "claude";
128
172
 
129
173
  const defaultsInput = {
130
174
  name: args.name ?? agentId,
@@ -168,8 +212,6 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
168
212
 
169
213
  // Experimental scripts-only MCP surface (code-mode): named swarm tools are
170
214
  // not registered, so tell the agent everything goes through script-run.
171
- const scriptsOnly = args.scriptsOnly ?? process.env.SCRIPTS_ONLY_MCP === "true";
172
- const scriptsOnlyMode = hasMcp && scriptsOnly;
173
215
  if (scriptsOnlyMode) {
174
216
  const scriptsOnlyResult = await resolveTemplateAsync("system.agent.scripts_only_mode", {});
175
217
  prompt += `\n${scriptsOnlyResult.text}`;
@@ -199,13 +241,6 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
199
241
  const slackResult = await resolveTemplateAsync("system.agent.slack", {});
200
242
  prompt += slackResult.text;
201
243
  }
202
- if (role !== "lead" && args.slackContext && scriptsOnlyMode && slackPromptToolsEnabled) {
203
- const slackResult = await resolveTemplateAsync("system.agent.scripts_only_mode.slack", {
204
- slackChannelId: args.slackContext.channelId,
205
- slackThreadTs: args.slackContext.threadTs ?? "",
206
- });
207
- prompt += slackResult.text;
208
- }
209
244
 
210
245
  if (
211
246
  hasMcp &&
@@ -244,9 +279,9 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
244
279
  prompt += toolsResult.text;
245
280
  }
246
281
 
247
- // J. Agent notes (CLAUDE.md). Claude loads ~/.claude/CLAUDE.md and
248
- // /workspace/CLAUDE.md natively; the others get it here, and only when it
249
- // differs from the generated default.
282
+ // J. Agent notes (CLAUDE.md), only when it differs from the generated
283
+ // default. Claude excludes both ~/.claude/CLAUDE.md and /workspace/CLAUDE.md
284
+ // from its memory load (claude-flag-settings.ts), so this is its only copy.
250
285
  if (
251
286
  hasLocalEnv &&
252
287
  args.claudeMd &&
@@ -257,10 +292,42 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
257
292
  BOOTSTRAP_MAX_CHARS,
258
293
  Math.max(0, BOOTSTRAP_TOTAL_MAX_CHARS - prompt.length),
259
294
  );
260
- prompt += truncateSection(args.claudeMd, "## Your notes (CLAUDE.md)", "CLAUDE.md", budget);
295
+ prompt += truncateSection(
296
+ args.claudeMd,
297
+ "## Your notes (CLAUDE.md)",
298
+ claudeMdSourcePath(provider),
299
+ budget,
300
+ );
261
301
  }
262
302
 
263
- // K. Repository (per task). Never truncated except the inlined repo CLAUDE.md.
303
+ // Blocks start and end with a newline; collapse the seams to one blank line.
304
+ return prompt.replace(/\n{3,}/g, "\n\n");
305
+ };
306
+
307
+ /**
308
+ * Per-task context that used to close the system prompt: the repository block
309
+ * and the scripts-only Slack note. It rides in the task prompt so the system
310
+ * prompt stays byte-identical across an agent's sessions. Empty when the task
311
+ * has neither.
312
+ */
313
+ export const getTaskContextPrompt = async (args: TaskContextPromptArgs): Promise<string> => {
314
+ const { hasMcp, hasLocalEnv, provider, scriptsOnlyMode } = promptFlags(args);
315
+ let prompt = "";
316
+
317
+ if (
318
+ args.role !== "lead" &&
319
+ args.slackContext &&
320
+ scriptsOnlyMode &&
321
+ areSlackPromptToolsEnabled(process.env, args.serverCapabilities)
322
+ ) {
323
+ const slackResult = await resolveTemplateAsync("system.agent.scripts_only_mode.slack", {
324
+ slackChannelId: args.slackContext.channelId,
325
+ slackThreadTs: args.slackContext.threadTs ?? "",
326
+ });
327
+ prompt += slackResult.text;
328
+ }
329
+
330
+ // Repository. Never truncated except the inlined repo CLAUDE.md.
264
331
  if (args.repoContext) {
265
332
  const repoResult = await resolveTemplateAsync(
266
333
  "system.agent.repository",
@@ -269,8 +336,7 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
269
336
  prompt += repoResult.text;
270
337
  }
271
338
 
272
- // Blocks start and end with a newline; collapse the seams to one blank line.
273
- return prompt.replace(/\n{3,}/g, "\n\n");
339
+ return prompt.replace(/\n{3,}/g, "\n\n").trim();
274
340
  };
275
341
 
276
342
  /**
@@ -331,7 +397,7 @@ function renderToolsAndSkillsVars(input: {
331
397
  * the heading lives in the template, the per-task pieces are built here.
332
398
  */
333
399
  function renderRepositoryVars(
334
- repo: NonNullable<BasePromptArgs["repoContext"]>,
400
+ repo: RepoContext,
335
401
  ctx: { hasMcp: boolean; hasLocalEnv: boolean; provider: string },
336
402
  ): Record<string, string> {
337
403
  const vars: Record<string, string> = {
@@ -432,7 +498,7 @@ export function truncateRepoClaudeMd(content: string, clonePath: string, budget:
432
498
  function truncateSection(
433
499
  content: string | undefined,
434
500
  header: string,
435
- fileName: string,
501
+ sourcePath: string,
436
502
  budget: number,
437
503
  ): string {
438
504
  if (!content || budget <= 0) return "";
@@ -441,7 +507,7 @@ function truncateSection(
441
507
  if (fullSection.length <= budget) return fullSection;
442
508
 
443
509
  const headerStr = `\n\n${header}\n\n`;
444
- const notice = `\n\n[...truncated, see /workspace/${fileName} for full content]\n`;
510
+ const notice = `\n\n[...truncated, see ${sourcePath} for full content]\n`;
445
511
  const contentBudget = budget - headerStr.length - notice.length;
446
512
 
447
513
  if (contentBudget > 0) {
@@ -1,3 +1,5 @@
1
+ import { openAICompatibleBaseUrlVars, openAICompatibleKeyVars } from "@desplega/model-catalog";
2
+
1
3
  export const ACP_TARGET_IDS = [
2
4
  "opencode",
3
5
  "gemini",
@@ -40,6 +42,10 @@ export const ACP_TARGET_CATALOG: readonly AcpTargetCatalogEntry[] = [
40
42
  "OPENAI_API_KEY",
41
43
  "OPENROUTER_API_KEY",
42
44
  "OPENROUTER_BASE_URL",
45
+ // OpenAI-compatible registry providers: keys (canonical and alias),
46
+ // then their `<ID>_BASE_URL` overrides.
47
+ ...openAICompatibleKeyVars(),
48
+ ...openAICompatibleBaseUrlVars(),
43
49
  "GOOGLE_API_KEY",
44
50
  "GEMINI_API_KEY",
45
51
  "OPENCODE_CONFIG",
@@ -1,7 +1,12 @@
1
1
  import { mkdir, mkdtemp, rm } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
- import { normalizeGrokModel } from "@desplega/model-catalog";
4
+ import {
5
+ baseUrlOverride,
6
+ normalizeGrokModel,
7
+ providerBaseUrl,
8
+ providerForModel,
9
+ } from "@desplega/model-catalog";
5
10
  import { clampContextPercent, getContextWindowSize } from "../utils/context-window";
6
11
  import {
7
12
  ACP_TARGET_IDS,
@@ -9,7 +14,11 @@ import {
9
14
  getAcpTargetCatalogEntry,
10
15
  isAcpTarget,
11
16
  } from "./acp-target-catalog";
12
- import { applyOpenRouterBaseUrlOverride, type OpencodeConfig } from "./opencode-adapter";
17
+ import {
18
+ applyOpenRouterBaseUrlOverride,
19
+ buildOpenAICompatibleProviderBlock,
20
+ type OpencodeConfig,
21
+ } from "./opencode-adapter";
13
22
  import { applyReasoningEffort } from "./reasoning-effort";
14
23
  import type { CostData, CostModelUsage, ProviderEvent, ProviderSessionConfig } from "./types";
15
24
 
@@ -140,10 +149,54 @@ function withOpencodeConfig(env: Record<string, string>, model: string): void {
140
149
  }
141
150
  }
142
151
  if (model.trim()) value.model = model.trim();
152
+ withCustomProviderBlock(value as OpencodeConfig, model.trim(), env);
143
153
  applyOpenRouterBaseUrlOverride(value as OpencodeConfig, env);
144
154
  env[key] = JSON.stringify(value);
145
155
  }
146
156
 
157
+ /**
158
+ * OpenCode ships no provider for a registry entry models.dev does not list
159
+ * (`catalog: "registry"`, e.g. `helmcode`), so its models need the custom
160
+ * block. The key stays an `{env:…}` reference: the ACP subprocess env is the
161
+ * forwarded per-task env, where the var holds one picked key, so no literal
162
+ * key lands in `OPENCODE_CONFIG_CONTENT`. A built-in registry provider
163
+ * (`nan`) only gets `options.baseURL` when `<ID>_BASE_URL` is set. Values the
164
+ * operator already set in `OPENCODE_CONFIG_CONTENT` win over the defaults.
165
+ */
166
+ function withCustomProviderBlock(
167
+ config: OpencodeConfig,
168
+ model: string,
169
+ env: Record<string, string>,
170
+ ): void {
171
+ const provider = providerForModel(model);
172
+ if (!provider) return;
173
+ const existing = config.provider?.[provider.id];
174
+ if (provider.catalog !== "registry") {
175
+ const baseURL = baseUrlOverride(provider, env);
176
+ if (!baseURL) return;
177
+ config.provider = {
178
+ ...config.provider,
179
+ [provider.id]: { ...existing, options: { baseURL, ...existing?.options } },
180
+ };
181
+ return;
182
+ }
183
+ const block = buildOpenAICompatibleProviderBlock(
184
+ provider,
185
+ `{env:${provider.envVar}}`,
186
+ model.slice(provider.id.length + 1),
187
+ providerBaseUrl(provider, env),
188
+ );
189
+ config.provider = {
190
+ ...config.provider,
191
+ [provider.id]: {
192
+ ...block,
193
+ ...existing,
194
+ options: { ...block.options, ...existing?.options },
195
+ models: { ...block.models, ...existing?.models },
196
+ },
197
+ };
198
+ }
199
+
147
200
  function parseCommand(command: string, args: string | undefined): string[] {
148
201
  const trimmed = command.trim();
149
202
  if (!trimmed) {
@@ -252,7 +305,7 @@ const geminiTargetProfile: AcpTargetProfile = {
252
305
  /**
253
306
  * Copilot CLI has no system-prompt flag. Under `--acp` it reads custom
254
307
  * instructions from the git root, the cwd, and every directory listed in
255
- * COPILOT_CUSTOM_INSTRUCTIONS_DIRS. Verified against 1.0.96: from those extra
308
+ * COPILOT_CUSTOM_INSTRUCTIONS_DIRS. Verified against 1.0.97: from those extra
256
309
  * directories it loads `.github/instructions/*.instructions.md`, but not
257
310
  * `AGENTS.md`. So the swarm prompt goes into a per-task directory outside the
258
311
  * repo, and the repo's own AGENTS.md is never touched.