@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
package/src/http/mcp.ts CHANGED
@@ -7,7 +7,12 @@ import { createServer } from "@/server";
7
7
  import { parseEnvFlag } from "@/utils/env-flag";
8
8
  import { getRequestAuth } from "@/utils/request-auth-context";
9
9
  import { resolveScriptsOnlyMode } from "@/utils/scripts-only-mode";
10
- import { parseTaskToolManifest, selectTaskTools } from "@/utils/task-tool-manifest";
10
+ import {
11
+ CHAT_TASK_ONLY_TOOLS,
12
+ isChatSdkTask,
13
+ parseTaskToolManifest,
14
+ selectTaskTools,
15
+ } from "@/utils/task-tool-manifest";
11
16
 
12
17
  export type McpTransportActivity = Record<string, number>;
13
18
  export type McpSessionAgents = Record<string, string>;
@@ -218,18 +223,22 @@ export async function handleMcp(
218
223
  true,
219
224
  );
220
225
  let preloadedTools: string[] = [];
226
+ const sourceTaskId = headerValue(req.headers["x-source-task-id"]);
227
+ const sourceTask = sourceTaskId ? await getTaskById(sourceTaskId) : undefined;
228
+ // A session bound to a non-chat task never sees chat-reply. Without the
229
+ // header the tool stays, and resolves the agent's current task itself.
230
+ const excludedTools =
231
+ sourceTask && !isChatSdkTask(sourceTask) ? [...CHAT_TASK_ONLY_TOOLS] : [];
221
232
  if (preloadEnabled) {
222
- const taskId = headerValue(req.headers["x-source-task-id"]);
223
- const task = taskId ? await getTaskById(taskId) : undefined;
224
233
  // Never select a manifest using another agent's task. Session-token
225
234
  // identity is also checked above, before any MCP session is created.
226
- if (task?.agentId === agentId) {
235
+ if (sourceTask?.agentId === agentId) {
227
236
  const manifestValue =
228
237
  configs.find((config) => config.key === "TASK_TOOL_MANIFESTS")?.value ??
229
238
  process.env.TASK_TOOL_MANIFESTS ??
230
239
  "{}";
231
240
  try {
232
- preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue), task);
241
+ preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue), sourceTask);
233
242
  } catch {
234
243
  // An invalid deployment value must not prevent tool discovery.
235
244
  // Avoid logging its contents, which may contain misfiled secrets.
@@ -239,6 +248,7 @@ export async function handleMcp(
239
248
  }
240
249
  const server = await createServer({
241
250
  preloadedTools,
251
+ excludedTools,
242
252
  // Agent row, then global row, then deployment env. A blank agent row
243
253
  // resolves to "unset" for that agent.
244
254
  enabledTools:
@@ -6,10 +6,8 @@ import { route } from "./route-def";
6
6
  import { jsonError } from "./utils";
7
7
 
8
8
  /**
9
- * `/@swarm/api/*` proxy. Cookie-gated equivalent of the artifact-sdk proxy
10
- * (src/artifact-sdk/server.ts:42-69), but lives on the MAIN API server and
11
- * authenticates via the page-session cookie instead of basic-auth + a per-
12
- * artifact tunnel.
9
+ * `/@swarm/api/*` proxy. Lives on the MAIN API server and authenticates via
10
+ * the page-session cookie.
13
11
  *
14
12
  * Flow:
15
13
  * 1. Browser hits `/@swarm/api/<rest>` from inside an iframe of `/p/:id`.
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Scope of THIS module (step-3):
9
9
  * - `auth_mode === 'public'`: ungated. HTML responses inline-inject the
10
- * `BROWSER_SDK_JS` constant from `src/artifact-sdk/browser-sdk.ts` (reused
10
+ * `BROWSER_SDK_JS` constant from `src/pages/browser-sdk.ts` (reused
11
11
  * verbatim — no token-injection hook on the client). SVG responses serve
12
12
  * raw bytes with a restrictive CSP and no injected scripts. JSON responses
13
13
  * 302-redirect to the SPA `/pages/:id` route (the JSON renderer lives
@@ -23,11 +23,12 @@
23
23
  */
24
24
  import type { IncomingMessage, ServerResponse } from "node:http";
25
25
  import { z } from "zod";
26
- import { BROWSER_SDK_JS, SWARM_UI_JS } from "../artifact-sdk/browser-sdk";
27
- import { feedbackOverlayScripts, isPageFeedbackRequested } from "../artifact-sdk/feedback-overlay";
28
26
  import { getPage, incrementPageViewCount } from "../be/db";
27
+ import { BROWSER_SDK_JS, SWARM_UI_JS } from "../pages/browser-sdk";
28
+ import { feedbackOverlayScripts, isPageFeedbackRequested } from "../pages/feedback-overlay";
29
29
  import { type Page, PageSchema } from "../types";
30
30
  import { getAppUrl, getConfiguredAppUrls } from "../utils/constants";
31
+ import { getNodeEnv } from "../utils/node-env";
31
32
  import { extractAndVerifyCookie, issuePageSessionCookie } from "../utils/page-session";
32
33
  import { scrubSecrets } from "../utils/secret-scrubber";
33
34
  import { route } from "./route-def";
@@ -318,7 +319,7 @@ function buildCsp(): string {
318
319
  // between Vite ports / portless dev doesn't require restarting the API.
319
320
  const configured = getConfiguredAppUrls();
320
321
  const devFallbacks =
321
- process.env.NODE_ENV === "production"
322
+ getNodeEnv() === "production"
322
323
  ? []
323
324
  : [
324
325
  "http://localhost:5274",
@@ -459,7 +460,7 @@ function extractPasswordCandidate(
459
460
  * `SameSite=Lax` (dev) vs `SameSite=None; Secure` (prod).
460
461
  */
461
462
  function isLocalhostRequest(req: IncomingMessage): boolean {
462
- if (process.env.NODE_ENV === "production") return false;
463
+ if (getNodeEnv() === "production") return false;
463
464
  // Only emit `SameSite=Lax` (no Secure) when the request comes from the
464
465
  // SAME http://localhost origin as the API — Lax cookies don't travel on
465
466
  // cross-site fetches, so portless `*.localhost` setups (SPA on https
package/src/http/pages.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  updatePage,
20
20
  withFavoriteFlags,
21
21
  } from "../be/db";
22
+ import { getPageEditCounter } from "../be/db/page-versions";
22
23
  import { snapshotPage } from "../pages/version";
23
24
  import { resolveTemplate } from "../prompts/resolver";
24
25
  // Side-effect import: registers `task.page.feedback`.
@@ -34,6 +35,7 @@ import {
34
35
  PageVersionSchema,
35
36
  } from "../types";
36
37
  import { getAppUrl, getPublicMcpBaseUrl } from "../utils/constants";
38
+ import { getNodeEnv } from "../utils/node-env";
37
39
  import { issuePageSessionCookie } from "../utils/page-session";
38
40
  import { getRequestAuth } from "../utils/request-auth-context";
39
41
  import { resolveHttpFavoriteOwner } from "./favorite-owner";
@@ -226,7 +228,7 @@ const updatePageRoute = route({
226
228
  /**
227
229
  * POST /api/pages/:id/feedback — turn the element comments collected by the
228
230
  * in-page feedback overlay (`?__swarm-feedback`, see
229
- * `src/artifact-sdk/feedback-overlay.ts`) into one task for the lead. In the
231
+ * `src/pages/feedback-overlay.ts`) into one task for the lead. In the
230
232
  * dashboard the SPA calls it with its bearer for the page it shows. A page
231
233
  * opened directly reaches it through the `/@swarm/api/*` page proxy, which
232
234
  * pins `X-Page-Id` to the cookie's page, so a page can only file feedback on
@@ -481,17 +483,6 @@ function withShareUrls<T extends { id: string; slug: string }>(
481
483
  };
482
484
  }
483
485
 
484
- /**
485
- * Compute the page's "edit counter" — `MAX(page_versions.version) + 1`. Means
486
- * "this is the N-th edit since the page was created". After the first PUT the
487
- * value is 2 (one snapshot row → version 1 → counter becomes 2). This is the
488
- * value POST and PUT return as `version` on the wire.
489
- */
490
- async function pageEditCounter(pageId: string): Promise<number> {
491
- const versions = await getPageVersions(pageId);
492
- return versions.length > 0 ? versions[0]!.version + 1 : 1;
493
- }
494
-
495
486
  /**
496
487
  * Viewer and page-supplied text sits inside a `<page_feedback>` block in the
497
488
  * task prompt. Defuse any tag of that name so the text cannot close the block
@@ -536,7 +527,7 @@ function formatFeedbackComments(comments: z.infer<typeof PageFeedbackCommentSche
536
527
  }
537
528
 
538
529
  function isDevRequest(req: IncomingMessage): boolean {
539
- if (process.env.NODE_ENV === "production") return false;
530
+ if (getNodeEnv() === "production") return false;
540
531
  // We want `SameSite=Lax` only when the request itself comes from the same
541
532
  // local-`http://localhost` origin as the API — same-site loads tolerate
542
533
  // Lax without Secure. Anything else (including portless `*.localhost`
@@ -842,7 +833,7 @@ export async function handlePages(
842
833
  updatePageRoute.respond(res, 200, {
843
834
  id: updated.id,
844
835
  key: updated.key,
845
- version: await pageEditCounter(updated.id),
836
+ version: await getPageEditCounter(updated.id),
846
837
  });
847
838
  return true;
848
839
  }
package/src/http/poll.ts CHANGED
@@ -19,7 +19,6 @@ import {
19
19
  getTaskAttachments,
20
20
  getTaskById,
21
21
  getUnassignedTaskIdsForAgent,
22
- getUserById,
23
22
  hasCapacity,
24
23
  isExtensionAgent,
25
24
  recordBudgetRefusalNotification,
@@ -27,7 +26,6 @@ import {
27
26
  updateAgentStatusFromCapacity,
28
27
  upsertChannelActivityCursor,
29
28
  } from "../be/db";
30
- import { renderIdentity, resolveIdentity } from "../be/identity";
31
29
  import {
32
30
  parseModelTierOverridesHeader,
33
31
  recordClaimModelResolution,
@@ -37,19 +35,14 @@ import { poolTaskRunsOnHarness } from "../be/model-validation";
37
35
  import { getRuntimeInstanceById, touchRuntimeInstance } from "../be/multi-runtime";
38
36
  import { hasCapability } from "../server";
39
37
  import { fetchChannelActivity } from "../slack/channel-activity";
38
+ import { type TaskModelPinFields, taskModelPin } from "../tasks/model-pin";
40
39
  import { emitTaskTelemetry, resolveTriggerSurface } from "../telemetry-trigger";
41
- import {
42
- AgentTaskSchema,
43
- BudgetRefusedTriggerSchema,
44
- TaskAttachmentSchema,
45
- UserCommsPrefsSchema,
46
- UserSchema,
47
- } from "../types";
40
+ import { AgentTaskSchema, BudgetRefusedTriggerSchema, TaskAttachmentSchema } from "../types";
48
41
  import { ensure } from "../utils/business-use";
49
42
  import { isLeadChannelMonitoringEnabled } from "../utils/lead-monitor-channels";
50
43
  import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
51
- import { getUserCommsPrefs } from "../utils/requester-comms";
52
44
  import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
45
+ import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
53
46
  import { route, runtimeInstanceHeader } from "./route-def";
54
47
  import { jsonError } from "./utils";
55
48
 
@@ -100,27 +93,11 @@ const PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
100
93
  sizeBytes: true,
101
94
  });
102
95
 
103
- // Requester projection sent on `task_assigned` triggers — either the resolved
104
- // user's identity fields, or (when no `requestedByUserId` is recorded) just a
105
- // rendered `name` for the UNKNOWN-identity sentinel.
106
- const PollRequestedBySchema = UserSchema.pick({
107
- id: true,
108
- name: true,
109
- email: true,
110
- role: true,
111
- notes: true,
112
- }).extend({
113
- // Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
114
- id: z.string().optional(),
115
- // Structured communication preferences from `users.metadata.comms`.
116
- comms: UserCommsPrefsSchema.optional(),
117
- });
118
-
119
96
  const PollTaskOfferedTriggerSchema = z.object({
120
97
  type: z.literal("task_offered"),
121
98
  taskId: z.string(),
122
99
  task: AgentTaskSchema,
123
- requestedBy: PollRequestedBySchema.optional(),
100
+ requestedBy: TaskRequestedBySchema.optional(),
124
101
  });
125
102
 
126
103
  const PollTaskAssignedTriggerSchema = z.object({
@@ -132,7 +109,7 @@ const PollTaskAssignedTriggerSchema = z.object({
132
109
  task: AgentTaskSchema.extend({
133
110
  attachments: z.array(PollTriggerAttachmentSchema),
134
111
  }),
135
- requestedBy: PollRequestedBySchema.optional(),
112
+ requestedBy: TaskRequestedBySchema.optional(),
136
113
  });
137
114
 
138
115
  // `budget_refused` reuses the canonical `BudgetRefusedTriggerSchema` entity
@@ -204,38 +181,6 @@ const pollTriggers = route({
204
181
  const CHANNEL_ACTIVITY_INTERVAL_MS = 60_000; // Check at most once per 60s
205
182
  let lastChannelActivityCheckAt = 0;
206
183
 
207
- function getRequesterNotes(notes: string | undefined): string | undefined {
208
- return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
209
- }
210
-
211
- /**
212
- * Resolve the requester projection for a task trigger. If the task carries a
213
- * machine-recorded external id (today: the Slack user field) but no
214
- * requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
215
- * omitting the section: never a substituted human (Rule 33 /
216
- * provenance-or-silence).
217
- */
218
- async function buildTriggerRequestedBy(task: {
219
- requestedByUserId?: string | null;
220
- slackUserId?: string | null;
221
- }): Promise<z.infer<typeof PollRequestedBySchema> | undefined> {
222
- const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
223
- if (user) {
224
- return {
225
- id: user.id,
226
- name: user.name,
227
- email: user.email,
228
- role: user.role,
229
- notes: getRequesterNotes(user.notes),
230
- comms: getUserCommsPrefs(user),
231
- };
232
- }
233
- if (task.slackUserId) {
234
- return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
235
- }
236
- return undefined;
237
- }
238
-
239
184
  /**
240
185
  * Slim attachment projection for the `task_assigned` poll trigger — just
241
186
  * enough (id, name, mimeType, sizeBytes) for the worker to build a one-shot
@@ -256,16 +201,31 @@ async function attachmentsForTrigger(
256
201
  /**
257
202
  * Claim-time model resolution (runbooks/model-tiers.md). Never blocks a claim:
258
203
  * a resolution failure leaves the worker on its own local resolution.
204
+ *
205
+ * The trigger's `model` is the task's own pin, not the column: a re-claimed
206
+ * row carries the model its previous session reported, and the worker treats
207
+ * `task.model` as a pin that beats the agent's current MODEL_OVERRIDE. The
208
+ * previous claim record is overwritten for the same reason.
259
209
  */
260
210
  async function claimModelFields(
261
- task: { id: string; model?: string | null; modelTier?: string | null },
211
+ task: TaskModelPinFields & { id: string },
262
212
  agent: Parameters<typeof recordClaimModelResolution>[1],
263
- ): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>>> {
213
+ ): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>> & { model: string | undefined }> {
214
+ const pin = taskModelPin(task);
215
+ const cleared = {
216
+ model: pin,
217
+ resolvedModel: undefined,
218
+ modelSource: undefined,
219
+ modelAlias: undefined,
220
+ };
264
221
  try {
265
- return await recordClaimModelResolution(task, agent);
222
+ return {
223
+ ...cleared,
224
+ ...(await recordClaimModelResolution({ ...task, model: pin }, agent)),
225
+ };
266
226
  } catch (error) {
267
227
  console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
268
- return {};
228
+ return cleared;
269
229
  }
270
230
  }
271
231
 
@@ -422,7 +382,7 @@ export async function handlePoll(
422
382
  const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
423
383
  if (claimedTask) {
424
384
  await recordRuntimeTask(claimedTask.id);
425
- const offeredRequestedBy = await buildTriggerRequestedBy(claimedTask);
385
+ const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
426
386
  return {
427
387
  trigger: {
428
388
  type: "task_offered",
@@ -519,8 +479,8 @@ export async function handlePoll(
519
479
  });
520
480
 
521
481
  // Resolve requesting user if available (UNKNOWN sentinel handling
522
- // lives in buildTriggerRequestedBy).
523
- const assignedRequestedBy = await buildTriggerRequestedBy(pendingTask);
482
+ // lives in buildTaskRequestedBy).
483
+ const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
524
484
  // The surface that started the whole chain, so the worker can tag
525
485
  // its session events with it (workers cannot read the task tree).
526
486
  const assignedTriggerSurface = await resolveTriggerSurface(
@@ -667,7 +627,7 @@ export async function handlePoll(
667
627
  agentId: myAgentId,
668
628
  });
669
629
  });
670
- const claimedRequestedBy = await buildTriggerRequestedBy(claimed);
630
+ const claimedRequestedBy = await buildTaskRequestedBy(claimed);
671
631
  const claimedTriggerSurface = await resolveTriggerSurface(
672
632
  claimed.id,
673
633
  claimed.source,
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { getUserById } from "../be/db";
3
+ import { renderIdentity, resolveIdentity } from "../be/identity";
4
+ import { UserCommsPrefsSchema, UserSchema } from "../types";
5
+ import { getUserCommsPrefs } from "../utils/requester-comms";
6
+
7
+ // Requester projection sent with a task to the worker that runs it (poll
8
+ // triggers, paused-task resume) — either the resolved user's identity fields,
9
+ // or (when no `requestedByUserId` is recorded) just a rendered `name` for the
10
+ // UNKNOWN-identity sentinel.
11
+ export const TaskRequestedBySchema = UserSchema.pick({
12
+ id: true,
13
+ name: true,
14
+ email: true,
15
+ role: true,
16
+ notes: true,
17
+ }).extend({
18
+ // Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
19
+ id: z.string().optional(),
20
+ // Structured communication preferences from `users.metadata.comms`.
21
+ comms: UserCommsPrefsSchema.optional(),
22
+ });
23
+
24
+ function getRequesterNotes(notes: string | undefined): string | undefined {
25
+ return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
26
+ }
27
+
28
+ /**
29
+ * Resolve the requester projection for a task. If the task carries a
30
+ * machine-recorded external id (today: the Slack user field) but no
31
+ * requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
32
+ * omitting the section: never a substituted human (Rule 33 /
33
+ * provenance-or-silence).
34
+ */
35
+ export async function buildTaskRequestedBy(task: {
36
+ requestedByUserId?: string | null;
37
+ slackUserId?: string | null;
38
+ }): Promise<z.infer<typeof TaskRequestedBySchema> | undefined> {
39
+ const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
40
+ if (user) {
41
+ return {
42
+ id: user.id,
43
+ name: user.name,
44
+ email: user.email,
45
+ role: user.role,
46
+ notes: getRequesterNotes(user.notes),
47
+ comms: getUserCommsPrefs(user),
48
+ };
49
+ }
50
+ if (task.slackUserId) {
51
+ return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
52
+ }
53
+ return undefined;
54
+ }
@@ -1,6 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { z } from "zod";
3
3
  import type { PermissionVerb } from "../rbac";
4
+ import { getNodeEnv } from "../utils/node-env";
4
5
  import { scrubSecrets } from "../utils/secret-scrubber";
5
6
  import { jsonError, matchRoute, parseBody, RequestBodyTooLargeError } from "./utils";
6
7
 
@@ -310,13 +311,13 @@ export function describeRequestRoute(
310
311
  * endpoint down outside tests (2026-08-18 incident — a single priority='high'
311
312
  * row 500'd every task listing). When the gate is on outside tests
312
313
  * (NODE_ENV=development, or VALIDATE_HTTP_RESPONSES=true), `respond()` logs
313
- * the violation and sends the response anyway. Note: the compiled Bun binary
314
- * defaults NODE_ENV to "development", so deploys must also set
315
- * NODE_ENV=production explicitly (see the deploy-env change in the fix PR).
314
+ * the violation and sends the response anyway. NODE_ENV is read at runtime
315
+ * (`getNodeEnv`), so the compiled binary with NODE_ENV unset leaves the gate
316
+ * off unless VALIDATE_HTTP_RESPONSES=true.
316
317
  */
317
318
  const VALIDATE_RESPONSES =
318
- process.env.NODE_ENV === "test" ||
319
- process.env.NODE_ENV === "development" ||
319
+ getNodeEnv() === "test" ||
320
+ getNodeEnv() === "development" ||
320
321
  process.env.VALIDATE_HTTP_RESPONSES === "true";
321
322
 
322
323
  export function route<
@@ -343,7 +344,7 @@ export function route<
343
344
  result.error.issues
344
345
  .map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
345
346
  .join("; ");
346
- if (process.env.NODE_ENV === "test") {
347
+ if (getNodeEnv() === "test") {
347
348
  throw new Error(detail);
348
349
  }
349
350
  console.error(`[route-def] ${scrubSecrets(detail)}`);
@@ -3,7 +3,9 @@ import { z } from "zod";
3
3
  import { getAgentById } from "@/be/db";
4
4
  import { callMcpServerTool } from "@/be/mcp-proxy";
5
5
  import { getScriptConnectionById } from "@/be/script-connections";
6
+ import { resolveSqlDsn, runReadOnlyQuery } from "@/be/sql-connections";
6
7
  import { can } from "@/rbac";
8
+ import { scrubSecrets } from "@/utils/secret-scrubber";
7
9
  import { route } from "./route-def";
8
10
  import { jsonError } from "./utils";
9
11
 
@@ -51,6 +53,37 @@ const mcpCallRoute = route({
51
53
  },
52
54
  });
53
55
 
56
+ const SqlQueryEnvelopeSchema = z.union([
57
+ z.object({
58
+ ok: z.literal(true),
59
+ rows: z.array(z.record(z.string(), z.unknown())),
60
+ rowCount: z.number(),
61
+ truncated: z.boolean(),
62
+ }),
63
+ z.object({ ok: z.literal(false), error: z.string() }),
64
+ ]);
65
+
66
+ const sqlQueryRoute = route({
67
+ method: "post",
68
+ path: "/api/script-connections/{id}/sql-query",
69
+ pattern: ["api", "script-connections", null, "sql-query"],
70
+ summary: "Run one read-only query on a SQL script connection",
71
+ tags: ["Script Connections"],
72
+ auth: { apiKey: true, agentId: true },
73
+ rbac: { permission: "script-connection.invoke" },
74
+ params: z.object({ id: z.string().uuid() }),
75
+ body: z.object({
76
+ sql: z.string().min(1).max(100_000),
77
+ params: z.array(z.unknown()).max(100).optional(),
78
+ }),
79
+ responses: {
80
+ 200: { description: "Query rows or a scrubbed error", schema: SqlQueryEnvelopeSchema },
81
+ 400: { description: "Invalid SQL connection or request" },
82
+ 403: { description: "Not allowed to invoke this SQL connection" },
83
+ 404: { description: "Script connection or agent not found" },
84
+ },
85
+ });
86
+
54
87
  function connectionScopeMatches(
55
88
  connection: { scope: string; scopeId: string | null },
56
89
  agentId: string,
@@ -75,9 +108,12 @@ export async function handleScriptConnectionProxy(
75
108
  queryParams: URLSearchParams,
76
109
  agentId: string | undefined,
77
110
  ): Promise<boolean> {
78
- if (!mcpCallRoute.match(req.method, pathSegments)) return false;
111
+ const isSql = sqlQueryRoute.match(req.method, pathSegments);
112
+ if (!isSql && !mcpCallRoute.match(req.method, pathSegments)) return false;
79
113
 
80
- const parsed = await mcpCallRoute.parse(req, res, pathSegments, queryParams);
114
+ const parsed = isSql
115
+ ? await sqlQueryRoute.parse(req, res, pathSegments, queryParams)
116
+ : await mcpCallRoute.parse(req, res, pathSegments, queryParams);
81
117
  if (!parsed) return true;
82
118
 
83
119
  if (!agentId) {
@@ -111,14 +147,25 @@ export async function handleScriptConnectionProxy(
111
147
  jsonError(res, "Script connection is disabled", 403);
112
148
  return true;
113
149
  }
114
- if (connection.kind !== "mcp") {
115
- jsonError(res, "Script connection is not an MCP connection", 400);
150
+ if (connection.kind !== (isSql ? "sql" : "mcp")) {
151
+ jsonError(res, `Script connection is not ${isSql ? "a SQL" : "an MCP"} connection`, 400);
116
152
  return true;
117
153
  }
118
154
  if (!connectionScopeMatches(connection, agentId, syntheticPrincipal)) {
119
155
  jsonError(res, "Script connection is not available to this agent", 403);
120
156
  return true;
121
157
  }
158
+ if ("sql" in parsed.body) {
159
+ try {
160
+ const dsn = await resolveSqlDsn(connection, agentId);
161
+ const result = await runReadOnlyQuery(dsn, parsed.body.sql, parsed.body.params);
162
+ sqlQueryRoute.respond(res, 200, { ok: true, ...result });
163
+ } catch (err) {
164
+ const error = scrubSecrets(err instanceof Error ? err.message : String(err));
165
+ sqlQueryRoute.respond(res, 200, { ok: false, error });
166
+ }
167
+ return true;
168
+ }
122
169
  if (!connection.mcpServerId) {
123
170
  jsonError(res, "Script MCP connection is missing mcpServerId", 400);
124
171
  return true;
@@ -38,6 +38,7 @@ import {
38
38
  type ScriptConnectionKind,
39
39
  type ScriptConnectionRecord,
40
40
  type ScriptCredentialBindingRecord,
41
+ SqlDsnInputSchema,
41
42
  setScriptConnectionEnabled,
42
43
  upsertCredentialBinding,
43
44
  upsertScriptConnection,
@@ -80,7 +81,7 @@ const providerSchema = z
80
81
  .regex(/^[A-Za-z0-9_-]+$/);
81
82
 
82
83
  const scopeSchema = z.enum(["global", "agent", "repo"]);
83
- const connectionKindSchema = z.enum(["openapi", "graphql", "mcp"]);
84
+ const connectionKindSchema = z.enum(["openapi", "graphql", "mcp", "sql"]);
84
85
 
85
86
  const idParamsSchema = z.object({ id: z.string().uuid() });
86
87
  const providerParamsSchema = z.object({ provider: providerSchema });
@@ -133,6 +134,7 @@ const upsertConnectionBodySchema = z.discriminatedUnion("kind", [
133
134
  kind: z.literal("mcp"),
134
135
  mcpServerId: z.string().uuid(),
135
136
  }),
137
+ connectionBaseBodySchema.extend({ kind: z.literal("sql"), dsn: SqlDsnInputSchema.optional() }),
136
138
  ]);
137
139
 
138
140
  const disableConnectionBodySchema = z.object({ enabled: z.boolean() });
@@ -232,7 +234,7 @@ const connectionAuthTypeResponseSchema = z.enum(["none", "bearer", "header", "qu
232
234
  // value (never written via this handler's upsert path, but readable via the
233
235
  // list/get routes for rows written elsewhere) — wider than the HTTP-body-only
234
236
  // `connectionKindSchema` above.
235
- const connectionRecordKindSchema = z.enum(["raw", "openapi", "mcp", "graphql"]);
237
+ const connectionRecordKindSchema = z.enum(["raw", "openapi", "mcp", "graphql", "sql"]);
236
238
 
237
239
  const bindingSummarySchema = z.object({
238
240
  id: z.string(),
@@ -2062,6 +2064,7 @@ export async function handleScriptConnections(
2062
2064
  ? existingOpenapiConnection?.openapiSpecFetchedAt
2063
2065
  : undefined,
2064
2066
  mcpServerId: parsed.body.kind === "mcp" ? parsed.body.mcpServerId : null,
2067
+ sqlDsn: parsed.body.kind === "sql" ? parsed.body.dsn : undefined,
2065
2068
  enabled,
2066
2069
  agentId,
2067
2070
  userId,
@@ -765,6 +765,7 @@ export async function handleScripts(
765
765
  runtimeInstanceId,
766
766
  egressSecrets: credentials.egressSecrets,
767
767
  failedBindings: credentials.failedBindings,
768
+ userConfig: credentials.userConfig,
768
769
  apiConnections: getScriptApiConnectionDescriptors({ agentId: agent.id }),
769
770
  mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agent.id }),
770
771
  });
@@ -31,6 +31,8 @@ interface SessionCostRecomputeInput {
31
31
  provider?: PricingProvider;
32
32
  model: string;
33
33
  harnessCostUsd: number;
34
+ /** `harnessCostUsd` is the provider's bill, not a token estimate. */
35
+ harnessCostBilled?: boolean;
34
36
  inputTokens: number;
35
37
  outputTokens: number;
36
38
  cacheReadTokens: number;
@@ -187,8 +189,16 @@ export async function recomputeSessionCost(
187
189
  // every request was billed through Amp, so it wins over the token price,
188
190
  // which still fills the per-model breakdown. Grok's `costUsdTicks` is the
189
191
  // same kind of report: what xAI billed, long-context tiers included.
190
- if ((input.provider === "amp" || input.provider === "grok") && input.harnessCostUsd > 0) {
191
- const priced = await recomputeSessionCost({ ...input, harnessCostUsd: 0 }, lookupRate);
192
+ // opencode on OpenRouter sends OpenRouter's `usage.cost` sum, flagged as
193
+ // billed: the routed endpoint can cost many times the list price.
194
+ if (
195
+ (input.provider === "amp" || input.provider === "grok" || input.harnessCostBilled) &&
196
+ input.harnessCostUsd > 0
197
+ ) {
198
+ const priced = await recomputeSessionCost(
199
+ { ...input, harnessCostUsd: 0, harnessCostBilled: false },
200
+ lookupRate,
201
+ );
192
202
  return {
193
203
  totalCostUsd: input.harnessCostUsd,
194
204
  costSource: "harness",
@@ -179,6 +179,9 @@ const createSessionCostRoute = route({
179
179
  sessionId: z.string().min(1),
180
180
  agentId: z.string().min(1),
181
181
  totalCostUsd: z.number(),
182
+ // The adapter's totalCostUsd is the provider's bill (e.g. OpenRouter's
183
+ // `usage.cost`), so it wins over the pricing table.
184
+ harnessCostBilled: z.boolean().optional(),
182
185
  taskId: z.string().optional(),
183
186
  // Phase 3: non-negative — the recompute no longer clamps, so negative
184
187
  // token counts must be rejected at the wire instead of pricing below $0.
@@ -397,6 +400,7 @@ export async function handleSessionData(
397
400
  provider: parsed.body.provider,
398
401
  model,
399
402
  harnessCostUsd: parsed.body.totalCostUsd,
403
+ harnessCostBilled: parsed.body.harnessCostBilled,
400
404
  inputTokens,
401
405
  outputTokens,
402
406
  cacheReadTokens: cachedInputTokens,