@desplega.ai/agent-swarm 1.173.0 → 2.1.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 (511) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
  7. package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
  8. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
  15. package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
  16. package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
  17. package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
  18. package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
  19. package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
  20. package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
  21. package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
  22. package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
  23. package/dist/cli-2m1ktfzz.js +17795 -0
  24. package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
  25. package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
  26. package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
  27. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  28. package/dist/cli-6cvfxs8r.js +47 -0
  29. package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
  30. package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
  31. package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
  32. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  33. package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
  34. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  35. package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
  36. package/dist/cli-9traj3dn.js +638 -0
  37. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  38. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  39. package/dist/cli-cvv9k0hh.js +10143 -0
  40. package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/cli-f527ce74.js +781 -0
  43. package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
  44. package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
  45. package/dist/cli-hjwmhcg1.js +10622 -0
  46. package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
  47. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  48. package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
  49. package/dist/cli-jta2eyzc.js +619 -0
  50. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  51. package/dist/cli-kceah1eh.js +92 -0
  52. package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
  53. package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
  54. package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
  55. package/dist/cli-n2wq4j7m.js +6985 -0
  56. package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
  57. package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
  58. package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
  59. package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
  60. package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
  61. package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
  62. package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
  63. package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
  64. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  65. package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
  66. package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
  67. package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
  68. package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
  69. package/dist/cli-rhs9pz33.js +294 -0
  70. package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
  71. package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
  72. package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
  73. package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
  74. package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
  75. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  76. package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
  77. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  78. package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
  79. package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
  80. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  81. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  82. package/dist/cli-wfnemr1t.js +86 -0
  83. package/dist/cli-wmgrkscj.js +23254 -0
  84. package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
  85. package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
  86. package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
  87. package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
  88. package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
  89. package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
  90. package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
  91. package/dist/cli.js +34 -49
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
  99. package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
  100. package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-s9ncp9vy.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
  117. package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
  118. package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
  121. package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
  122. package/dist/index-9gw58n4k.js +193 -0
  123. package/dist/index-9j87mcm8.js +56305 -0
  124. package/dist/index-bsmcresk.js +79424 -0
  125. package/dist/index-mh85qhec.js +475 -0
  126. package/dist/{index-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
  127. package/dist/index-we27kd31.js +12810 -0
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
  148. package/openapi.json +1279 -329
  149. package/package.json +15 -15
  150. package/src/agentmail/handlers.ts +21 -11
  151. package/src/apps/definition.ts +16 -0
  152. package/src/azure-devops/api.ts +52 -2
  153. package/src/azure-devops/boards-sync.ts +50 -20
  154. package/src/azure-devops/index.ts +1 -0
  155. package/src/azure-devops/outbound.ts +95 -0
  156. package/src/be/boot-seeds.ts +11 -0
  157. package/src/be/config-refs.ts +60 -0
  158. package/src/be/db/model-profiles.ts +315 -0
  159. package/src/be/db/page-versions.ts +15 -0
  160. package/src/be/db/runtime.ts +35 -8
  161. package/src/be/db/skill-name-resolution.ts +36 -0
  162. package/src/be/db/tasks/read.ts +237 -0
  163. package/src/be/db/tasks/write.ts +7 -0
  164. package/src/be/db.ts +87 -36
  165. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  166. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  167. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  168. package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
  169. package/src/be/migrations/220_model_profiles.sql +137 -0
  170. package/src/be/model-catalog-store.ts +5 -1
  171. package/src/be/model-tier-resolution.ts +155 -10
  172. package/src/be/model-validation.ts +12 -2
  173. package/src/be/models-catalog.ts +26 -1
  174. package/src/be/modelsdev-cache.json +198 -63
  175. package/src/be/modelsdev-cache.ts +9 -1
  176. package/src/be/script-credential-broker.ts +39 -2
  177. package/src/be/scripts/run-saved.ts +1 -0
  178. package/src/be/scripts/typecheck.ts +22 -8
  179. package/src/be/secret-registry.ts +6 -2
  180. package/src/be/seed/runner.ts +13 -1
  181. package/src/be/seed/state-db.ts +5 -0
  182. package/src/be/seed/types.ts +6 -0
  183. package/src/be/seed-model-profiles.ts +41 -0
  184. package/src/be/seed-pricing.ts +19 -2
  185. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
  186. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  187. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  188. package/src/be/seed-scripts/index.ts +9 -0
  189. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  190. package/src/be/seed-skills/index.ts +104 -3
  191. package/src/be/swarm-config-guard.ts +53 -0
  192. package/src/cli.tsx +0 -16
  193. package/src/commands/onboard/non-interactive.ts +4 -2
  194. package/src/commands/provider-credentials.ts +52 -6
  195. package/src/commands/runner.ts +193 -71
  196. package/src/e2b/env.ts +5 -0
  197. package/src/extensions/catalog.generated.json +17 -0
  198. package/src/extensions/contract-types.generated.ts +1 -1
  199. package/src/extensions/contract.ts +45 -0
  200. package/src/extensions/dispatcher.ts +7 -0
  201. package/src/extensions/loader.ts +10 -5
  202. package/src/extensions/post-bridge.ts +40 -0
  203. package/src/github/handlers.ts +11 -4
  204. package/src/heartbeat/heartbeat.ts +2 -1
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/agents.ts +49 -2
  208. package/src/http/all-routes.ts +1 -1
  209. package/src/http/apps.ts +30 -3
  210. package/src/http/config.ts +62 -19
  211. package/src/http/core.ts +8 -0
  212. package/src/http/index.ts +25 -3
  213. package/src/http/integrations.ts +88 -1
  214. package/src/http/mcp.ts +15 -5
  215. package/src/http/model-profiles.ts +303 -0
  216. package/src/http/page-proxy.ts +2 -4
  217. package/src/http/pages-public.ts +13 -7
  218. package/src/http/pages.ts +3 -13
  219. package/src/http/poll.ts +42 -71
  220. package/src/http/requested-by.ts +54 -0
  221. package/src/http/script-runs.ts +1 -1
  222. package/src/http/scripts.ts +26 -2
  223. package/src/http/stats.ts +1 -30
  224. package/src/http/tasks.ts +79 -5
  225. package/src/http/utils.ts +14 -0
  226. package/src/http/webhooks.ts +160 -3
  227. package/src/http/x.ts +1 -0
  228. package/src/integrations/chat-sdk/adapters.ts +129 -0
  229. package/src/integrations/chat-sdk/attachments.ts +232 -0
  230. package/src/integrations/chat-sdk/background.ts +36 -0
  231. package/src/integrations/chat-sdk/commands.ts +194 -0
  232. package/src/integrations/chat-sdk/config.ts +198 -0
  233. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  234. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  235. package/src/integrations/chat-sdk/handlers.ts +28 -0
  236. package/src/integrations/chat-sdk/inbound.ts +426 -0
  237. package/src/integrations/chat-sdk/limits.ts +48 -0
  238. package/src/integrations/chat-sdk/logger.ts +81 -0
  239. package/src/integrations/chat-sdk/outbound.ts +123 -0
  240. package/src/integrations/chat-sdk/relay.ts +172 -0
  241. package/src/integrations/chat-sdk/router.ts +169 -0
  242. package/src/integrations/chat-sdk/runtime.ts +351 -0
  243. package/src/integrations/chat-sdk/schema.ts +245 -0
  244. package/src/integrations/chat-sdk/state.ts +243 -0
  245. package/src/integrations/chat-sdk/status.ts +244 -0
  246. package/src/integrations/chat-sdk/steering.ts +58 -0
  247. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  248. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  249. package/src/integrations/chat-sdk/templates.ts +16 -0
  250. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  251. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  252. package/src/integrations/chat-sdk/typing.ts +65 -0
  253. package/src/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
  254. package/src/pages/version.ts +4 -5
  255. package/src/prompts/base-prompt.ts +111 -45
  256. package/src/providers/acp-target-catalog.ts +6 -0
  257. package/src/providers/acp-targets.ts +56 -3
  258. package/src/providers/amp-adapter.ts +1 -1
  259. package/src/providers/claude-adapter.ts +1 -1
  260. package/src/providers/claude-flag-settings.ts +17 -5
  261. package/src/providers/claude-managed-adapter.ts +1 -1
  262. package/src/providers/claude-session-events.ts +6 -1
  263. package/src/providers/dsh-adapter.ts +1 -1
  264. package/src/providers/opencode-adapter.ts +243 -26
  265. package/src/providers/opencode-swarm-plugin.ts +31 -0
  266. package/src/providers/pi-mono-adapter.ts +398 -153
  267. package/src/providers/pi-mono-extension.ts +17 -3
  268. package/src/providers/pricing-sources.md +33 -2
  269. package/src/providers/reasoning-effort.ts +37 -0
  270. package/src/providers/types.ts +6 -2
  271. package/src/rbac/legacy-policy.ts +16 -2
  272. package/src/rbac/permissions.ts +26 -0
  273. package/src/scheduler/deferred-task-waits.ts +18 -5
  274. package/src/scheduler/scheduler.ts +11 -3
  275. package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
  276. package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
  277. package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
  278. package/src/scripts-runtime/extract-args-schema.ts +9 -8
  279. package/src/scripts-runtime/loader.ts +19 -7
  280. package/src/scripts-runtime/quickjs-compat.ts +464 -0
  281. package/src/scripts-runtime/sdk-allowlist.ts +12 -6
  282. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  283. package/src/scripts-runtime/types/stdlib.d.ts +39 -15
  284. package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
  285. package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
  286. package/src/server.ts +62 -19
  287. package/src/slack/blocks.ts +4 -2
  288. package/src/slack/event-dedup.ts +1 -1
  289. package/src/slack/reaction-shortcode.ts +14 -0
  290. package/src/slack/render-v2.ts +12 -7
  291. package/src/slack/responses.ts +5 -4
  292. package/src/slack/thread-buffer.ts +41 -22
  293. package/src/tasks/additive-buffer.ts +4 -2
  294. package/src/tasks/context-key.ts +60 -1
  295. package/src/tasks/model-pin.ts +67 -0
  296. package/src/tasks/worker-follow-up.ts +15 -33
  297. package/src/tests/acp-adapter.test.ts +42 -0
  298. package/src/tests/agentmail-filters.test.ts +47 -0
  299. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  300. package/src/tests/apps-spike2.test.ts +35 -0
  301. package/src/tests/azure-devops-outbound.test.ts +420 -0
  302. package/src/tests/base-prompt.test.ts +58 -54
  303. package/src/tests/chat-sdk-commands.test.ts +191 -0
  304. package/src/tests/chat-sdk-config.test.ts +334 -0
  305. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  306. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  307. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  308. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  309. package/src/tests/chat-sdk-harness.ts +146 -0
  310. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  311. package/src/tests/chat-sdk-relay.test.ts +470 -0
  312. package/src/tests/chat-sdk-reload.test.ts +128 -0
  313. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  314. package/src/tests/chat-sdk-slack.test.ts +268 -0
  315. package/src/tests/chat-sdk-state.test.ts +163 -0
  316. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  317. package/src/tests/chat-sdk-teams.test.ts +277 -0
  318. package/src/tests/chat-sdk-tools.test.ts +520 -0
  319. package/src/tests/chat-sdk-typing.test.ts +120 -0
  320. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  321. package/src/tests/claude-adapter-binary.test.ts +9 -4
  322. package/src/tests/claude-flag-settings.test.ts +11 -3
  323. package/src/tests/claude-managed-adapter.test.ts +1 -1
  324. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  325. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  326. package/src/tests/claude-worker-parity.test.ts +2 -7
  327. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  328. package/src/tests/config-refs.test.ts +66 -0
  329. package/src/tests/context-key.test.ts +19 -0
  330. package/src/tests/cors-allowlist.test.ts +25 -0
  331. package/src/tests/create-page-tool.test.ts +18 -0
  332. package/src/tests/credential-check.test.ts +139 -0
  333. package/src/tests/credentials.test.ts +21 -0
  334. package/src/tests/db-runtime.test.ts +61 -0
  335. package/src/tests/db-task-read-contract.test.ts +22 -0
  336. package/src/tests/db-task-write-contract.test.ts +11 -0
  337. package/src/tests/deferred-task-wake.test.ts +78 -1
  338. package/src/tests/docs-index-footprint.test.js +3 -3
  339. package/src/tests/dsh-adapter.test.ts +1 -1
  340. package/src/tests/enabled-capabilities.test.ts +20 -0
  341. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  342. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  343. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  344. package/src/tests/env-flag.test.ts +1 -0
  345. package/src/tests/explicit-model-validation.test.ts +105 -1
  346. package/src/tests/extensions-catalog.test.ts +1 -0
  347. package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
  348. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  349. package/src/tests/extensions-loader.test.ts +15 -0
  350. package/src/tests/extensions-post-bridge.test.ts +51 -0
  351. package/src/tests/fetch-resolved-env.test.ts +128 -0
  352. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  353. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  354. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  355. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  356. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  357. package/src/tests/github-handlers.test.ts +35 -0
  358. package/src/tests/harness-provider-resolution.test.ts +12 -0
  359. package/src/tests/heartbeat-checklist.test.ts +3 -2
  360. package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
  361. package/src/tests/heartbeat-jev-gate.test.ts +385 -0
  362. package/src/tests/hook-runtime-close.test.ts +0 -2
  363. package/src/tests/http-api-integration.test.ts +0 -25
  364. package/src/tests/migration-046-budgets.test.ts +17 -16
  365. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  366. package/src/tests/model-profile-backcompat.test.ts +327 -0
  367. package/src/tests/model-profile-providers.test.ts +302 -0
  368. package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
  369. package/src/tests/model-profiles-api.test.ts +614 -0
  370. package/src/tests/model-tier-resolution.test.ts +93 -0
  371. package/src/tests/models-catalog.test.ts +42 -1
  372. package/src/tests/openai-compatible-providers.test.ts +134 -0
  373. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  374. package/src/tests/opencode-adapter.test.ts +216 -2
  375. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  376. package/src/tests/opencode-shell-env.test.ts +145 -0
  377. package/src/tests/openrouter-base-url.test.ts +101 -151
  378. package/src/tests/page-proxy.test.ts +1 -1
  379. package/src/tests/pages-browser-sdk.test.ts +76 -0
  380. package/src/tests/pages-feedback-keys.test.ts +121 -0
  381. package/src/tests/pages-feedback.test.ts +4 -1
  382. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  383. package/src/tests/pi-context-compaction.test.ts +193 -0
  384. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  385. package/src/tests/pi-mono-adapter.test.ts +385 -5
  386. package/src/tests/pi-shell-env.test.ts +170 -0
  387. package/src/tests/profile-claim.test.ts +269 -0
  388. package/src/tests/prompt-template-remaining.test.ts +35 -0
  389. package/src/tests/quickjs-compat.test.ts +213 -0
  390. package/src/tests/rbac-engine.test.ts +32 -3
  391. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  392. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  393. package/src/tests/script-connections.test.ts +6 -0
  394. package/src/tests/script-executor-conformance.test.ts +252 -1
  395. package/src/tests/script-executor-quickjs.test.ts +96 -0
  396. package/src/tests/script-runs-http.test.ts +89 -0
  397. package/src/tests/scripts-http.test.ts +217 -1
  398. package/src/tests/scripts-only-gating.test.ts +2 -3
  399. package/src/tests/scripts-runtime.test.ts +58 -40
  400. package/src/tests/secret-scrubber.test.ts +96 -0
  401. package/src/tests/seed-model-profiles.test.ts +174 -0
  402. package/src/tests/seed-scripts.test.ts +2 -2
  403. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  404. package/src/tests/seed-skills-retire.test.ts +182 -0
  405. package/src/tests/send-task-requester-gate.test.ts +58 -0
  406. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  407. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  408. package/src/tests/skill-invocations.test.ts +4 -2
  409. package/src/tests/slack-blocks.test.ts +36 -0
  410. package/src/tests/slack-inline-output.test.ts +14 -0
  411. package/src/tests/slack-reaction-config.test.ts +20 -0
  412. package/src/tests/slack-render-v2.test.ts +37 -0
  413. package/src/tests/slack-thread-buffer.test.ts +98 -1
  414. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  415. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  416. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  417. package/src/tests/swarm-diff.test.ts +1 -1
  418. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  419. package/src/tests/task-session-prompts.test.ts +132 -0
  420. package/src/tests/task-tool-manifest.test.ts +14 -0
  421. package/src/tests/task-tools-ctx.test.ts +2 -0
  422. package/src/tests/tool-annotations.test.ts +6 -6
  423. package/src/tests/ui-logs-parser.test.ts +43 -0
  424. package/src/tests/used-by-route.test.ts +6 -0
  425. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  426. package/src/tests/workflow-llm.test.ts +61 -0
  427. package/src/tests/workflow-swarm-script.test.ts +38 -0
  428. package/src/tests/workflow-wait-filter.test.ts +5 -3
  429. package/src/tools/chat-access.ts +41 -0
  430. package/src/tools/chat-open-dm.ts +59 -0
  431. package/src/tools/chat-post.ts +74 -0
  432. package/src/tools/chat-read-thread.ts +100 -0
  433. package/src/tools/chat-reply.ts +75 -0
  434. package/src/tools/create-page.ts +3 -13
  435. package/src/tools/model-catalog/index.ts +2 -1
  436. package/src/tools/model-profiles/list-model-profiles.ts +54 -0
  437. package/src/tools/register-chat-channel.ts +139 -0
  438. package/src/tools/request-human-input.ts +11 -3
  439. package/src/tools/script-apis.ts +5 -1
  440. package/src/tools/script-upsert.ts +4 -1
  441. package/src/tools/send-task.ts +48 -1
  442. package/src/tools/skills/skill-get.ts +17 -9
  443. package/src/tools/swarm-config/get-config.ts +2 -1
  444. package/src/tools/swarm-config/list-config.ts +2 -1
  445. package/src/tools/swarm-config/set-config.ts +16 -3
  446. package/src/tools/system-one-decide.ts +104 -0
  447. package/src/tools/task-action.ts +22 -1
  448. package/src/tools/tool-config.ts +14 -6
  449. package/src/types.ts +48 -3
  450. package/src/utils/config-env-export.ts +74 -0
  451. package/src/utils/credentials.test.ts +92 -0
  452. package/src/utils/credentials.ts +52 -4
  453. package/src/utils/harness-provider.ts +15 -6
  454. package/src/utils/runtime-model-catalog.ts +11 -1
  455. package/src/utils/secret-scrubber.ts +86 -10
  456. package/src/utils/task-tool-manifest.ts +19 -2
  457. package/src/utils/vcs-repo-match.ts +29 -0
  458. package/src/workflows/executors/raw-llm.ts +8 -3
  459. package/src/workflows/executors/swarm-script.ts +1 -0
  460. package/src/workflows/executors/system-one-decision.ts +21 -11
  461. package/src/workflows/executors/workflow-llm.ts +22 -0
  462. package/src/workflows/index.ts +21 -12
  463. package/templates/extensions/README.md +1 -0
  464. package/templates/extensions/comms-adherence-gate/README.md +88 -0
  465. package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
  466. package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
  467. package/templates/skills/agent-browser/SKILL.md +1 -1
  468. package/templates/skills/agent-browser/content.md +1 -1
  469. package/templates/skills/apps/SKILL.md +8 -1
  470. package/templates/skills/apps/content.md +8 -1
  471. package/templates/skills/chat-sdk/config.json +14 -0
  472. package/templates/skills/chat-sdk/content.md +84 -0
  473. package/templates/skills/composio-gmail/content.md +7 -0
  474. package/templates/skills/composio-google-docs/content.md +1 -1
  475. package/templates/skills/email-domain-health/config.json +14 -0
  476. package/templates/skills/email-domain-health/content.md +131 -0
  477. package/templates/skills/kv-storage/content.md +2 -2
  478. package/templates/skills/pages/content.md +8 -10
  479. package/templates/skills/swarm-extensions/content.md +2 -0
  480. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  481. package/templates/skills/swarm-scripts/config.json +1 -1
  482. package/templates/skills/swarm-scripts/content.md +4 -2
  483. package/templates/skills/workflow-iterate/content.md +38 -0
  484. package/dist/artifact-eczsknc3.js +0 -3843
  485. package/dist/cli-42qhyvwx.js +0 -33
  486. package/dist/cli-bd957dgb.js +0 -153
  487. package/dist/cli-q51yprfq.js +0 -1104
  488. package/dist/cli-t1ze64h3.js +0 -811
  489. package/dist/cli-tcc4wd5d.js +0 -56471
  490. package/dist/index-9ch1n761.js +0 -1102
  491. package/src/artifact-sdk/index.ts +0 -2
  492. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  493. package/src/artifact-sdk/port.ts +0 -12
  494. package/src/artifact-sdk/server.ts +0 -183
  495. package/src/artifact-sdk/tunnel.ts +0 -19
  496. package/src/commands/artifact.ts +0 -249
  497. package/src/http/ecosystem.ts +0 -77
  498. package/src/tests/artifact-commands.test.ts +0 -92
  499. package/src/tests/artifact-sdk.test.ts +0 -793
  500. package/src/tests/register-service-security.test.ts +0 -194
  501. package/src/tools/list-services.ts +0 -107
  502. package/src/tools/register-service.ts +0 -177
  503. package/src/tools/unregister-service.ts +0 -73
  504. package/src/tools/update-service-status.ts +0 -96
  505. package/templates/skills/artifacts/config.json +0 -14
  506. package/templates/skills/artifacts/content.md +0 -344
  507. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  508. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  509. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  510. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  511. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
@@ -1,7 +1,3 @@
1
- import {
2
- BROWSER_SDK_JS,
3
- SWARM_UI_JS
4
- } from "./cli-t1ze64h3.js";
5
1
  import {
6
2
  SIMILARITY_THRESHOLD,
7
3
  isLeadChannelMonitoringEnabled,
@@ -11,7 +7,7 @@ import {
11
7
  } from "./cli-hz99wfxp.js";
12
8
  import {
13
9
  ACP_TARGET_IDS
14
- } from "./cli-7a0sde2x.js";
10
+ } from "./cli-6cagzc96.js";
15
11
  import {
16
12
  isCodexOAuthConfigKey
17
13
  } from "./cli-bqjfjex4.js";
@@ -23,12 +19,13 @@ import {
23
19
  MAX_RATE_LIMIT_RESET_MS
24
20
  } from "./cli-2pdaysab.js";
25
21
  import {
26
- reasoningCapability
27
- } from "./cli-tmhsxhb8.js";
22
+ reasoningCapability,
23
+ supportedReasoningLevels
24
+ } from "./cli-0veb92a3.js";
28
25
  import {
29
26
  ensureAgentFsCredentialsForAgent,
30
27
  inviteEmailToSharedOrg
31
- } from "./cli-phcqp1b9.js";
28
+ } from "./cli-zm9t2vde.js";
32
29
  import"./cli-6zx22mv6.js";
33
30
  import {
34
31
  DEFAULT_CONNECTOR_CODE_LABEL,
@@ -39,16 +36,17 @@ import {
39
36
  startPoolRouting,
40
37
  stopHeartbeat,
41
38
  stopPoolRouting
42
- } from "./cli-r0byd61h.js";
39
+ } from "./cli-yjmqpxt3.js";
43
40
  import {
44
41
  Anthropic
45
- } from "./cli-gmdajfw5.js";
42
+ } from "./cli-gxzkg1de.js";
46
43
  import {
47
44
  runtimeSupervisorStatus,
48
45
  startRuntimeSupervisor,
49
46
  stopRuntimeSupervisor
50
- } from "./cli-ne7smj1b.js";
51
- import"./cli-ppy4nnnh.js";
47
+ } from "./cli-01xvj1rq.js";
48
+ import"./cli-n8ndw8j7.js";
49
+ import"./cli-knd276ed.js";
52
50
  import {
53
51
  AGENT_MAX_TASKS_CONFIG_KEY,
54
52
  ALL_CAPABILITIES,
@@ -75,6 +73,7 @@ import {
75
73
  SDK_ALLOWLIST,
76
74
  SUPPORTED_PROTOCOL_VERSIONS,
77
75
  TaskCreationBlockedError,
76
+ TelegramBotApiError,
78
77
  WebhookError,
79
78
  activateVersion,
80
79
  agentRanModel,
@@ -88,6 +87,7 @@ import {
88
87
  authorizeAssetKeyWrite,
89
88
  authorizeRoomNamespace,
90
89
  calculateNextRun,
90
+ callTelegramBotApi,
91
91
  canClaim,
92
92
  canReadMemory,
93
93
  cancelApprovalRequest,
@@ -124,6 +124,7 @@ import {
124
124
  enforceContentLengthCap,
125
125
  enqueueAdmissionRow,
126
126
  enqueueAuditRow,
127
+ errorMessage,
127
128
  estimateTokens,
128
129
  executeRawLlm,
129
130
  executeReadOnlyQuery,
@@ -133,6 +134,9 @@ import {
133
134
  extractAndVerifyCookie,
134
135
  flushAuditBuffer,
135
136
  getAgentHarnessCliVersion,
137
+ getChatInstance,
138
+ getChatSdkConfig,
139
+ getChatSdkHealth,
136
140
  getCombConfig,
137
141
  getEnabledCapabilities,
138
142
  getEventCountsFiltered,
@@ -147,6 +151,7 @@ import {
147
151
  getLinksForMemory,
148
152
  getMemoryChunks,
149
153
  getOAuthPreset,
154
+ getPageEditCounter,
150
155
  getPathSegments,
151
156
  getPosteriorsByIds,
152
157
  getRoom,
@@ -198,6 +203,7 @@ import {
198
203
  initAgentMail,
199
204
  initApiTelemetry,
200
205
  initAzureDevOps,
206
+ initAzureDevOpsOutbound,
201
207
  initGitHub,
202
208
  initGitLab,
203
209
  initJira,
@@ -207,6 +213,9 @@ import {
207
213
  isAgentMailEnabled,
208
214
  isApiDraining,
209
215
  isAzureDevOpsEnabled,
216
+ isChatAdapterConfigured,
217
+ isChatSdkEnabled,
218
+ isChatSdkStopping,
210
219
  isGitHubEnabled,
211
220
  isGitLabEnabled,
212
221
  isInboxAllowed,
@@ -218,6 +227,7 @@ import {
218
227
  isLinearEnabled,
219
228
  isOriginAllowedForCredentials,
220
229
  isSenderAllowed,
230
+ isTrustedTeamsServiceUrl,
221
231
  issuePageSessionCookie,
222
232
  json,
223
233
  jsonError,
@@ -241,7 +251,6 @@ import {
241
251
  parseCookieHeader,
242
252
  parseModelTierOverridesHeader,
243
253
  parseQueryParams,
244
- parseSkillContent,
245
254
  patchWorkflowDefinition,
246
255
  poolTaskRunsOnHarness,
247
256
  posteriorMean,
@@ -267,6 +276,7 @@ import {
267
276
  reservedRoomKeyError,
268
277
  resetAgentMaxTasksMirror,
269
278
  resetRoom,
279
+ resolveAdapterApiUrl,
270
280
  resolveHttpAuditUserId,
271
281
  resolveHttpFavoriteOwner,
272
282
  resolveHttpRequestAuth,
@@ -301,17 +311,21 @@ import {
301
311
  snapshotPage,
302
312
  startAuditGc,
303
313
  startAuditWriter,
314
+ startChatSdk,
304
315
  steerTaskHandler,
305
316
  steerTaskOutputSchema,
306
317
  steerTaskUserInputSchema,
307
318
  stopAuditGc,
308
319
  stopAuditWriter,
320
+ stopChatSdk,
309
321
  stopExtensionRuntime,
310
322
  stripSiblingBlock,
311
323
  sweepRooms,
312
324
  syncTelemetryEnabled,
325
+ taskModelPin,
313
326
  toolErr,
314
327
  touchRuntimeInstance,
328
+ trackBackground,
315
329
  triggerSchemaErrorResponse,
316
330
  uninstallExtension,
317
331
  updateExtensionMeta,
@@ -332,13 +346,13 @@ import {
332
346
  wireHttpSpanLifecycle,
333
347
  workflowModelErrors,
334
348
  writeUnhandledError
335
- } from "./cli-rfz2ve55.js";
336
- import"./cli-451ks6aw.js";
349
+ } from "./cli-vgqzp4em.js";
350
+ import"./cli-h1arqn31.js";
337
351
  import {
338
352
  AMP_ESTIMATE_MODELS,
339
353
  ampBillsCacheWrites,
340
354
  resolveAmpModel
341
- } from "./cli-2bw3w47j.js";
355
+ } from "./cli-8wwxw8a3.js";
342
356
  import {
343
357
  RATING_MODEL_MAX_LENGTH,
344
358
  REFERENCES_SOURCE_MAX_LENGTH,
@@ -347,16 +361,12 @@ import {
347
361
  import {
348
362
  isMultiRuntimeEnabled
349
363
  } from "./cli-re8z5p8p.js";
350
- import"./cli-42qhyvwx.js";
351
364
  import {
352
365
  deleteModelCatalogOverlay,
353
366
  loadModelsCatalog,
354
367
  requestModelCatalogRefresh,
355
368
  upsertModelCatalogOverlay
356
- } from "./cli-9xf7fdtp.js";
357
- import {
358
- normalizeModelKey
359
- } from "./cli-jpc4r224.js";
369
+ } from "./cli-0pd8hzhc.js";
360
370
  import {
361
371
  ConnectionAuthInputSchema,
362
372
  MAX_SCRIPT_WALL_CLOCK_MS,
@@ -373,6 +383,7 @@ import {
373
383
  buildScriptCredentialBindingsWithFailures,
374
384
  callMcpServerTool,
375
385
  checkImportAllowlist,
386
+ checkQuickJSCompat,
376
387
  collectScriptReferences,
377
388
  computeExpiresAt,
378
389
  connectionAuthInputFromFlat,
@@ -411,7 +422,9 @@ import {
411
422
  mcpOAuthSsrfOptions,
412
423
  normalizeTokenEndpointAuthMethod,
413
424
  oauthAppToProviderConfig,
425
+ parseSkillContent,
414
426
  publicEndpointSsrfOptions,
427
+ quickJSCompatWarnings,
415
428
  readStreamCapped,
416
429
  refreshMcpToken,
417
430
  refreshScriptConnection,
@@ -434,7 +447,7 @@ import {
434
447
  upsertCredentialBinding,
435
448
  upsertMcpOAuthToken,
436
449
  upsertScriptConnection
437
- } from "./cli-7a93cv8c.js";
450
+ } from "./cli-4mengw46.js";
438
451
  import"./cli-epan13p1.js";
439
452
  import {
440
453
  detachedProcessGroup,
@@ -449,7 +462,12 @@ import {
449
462
  forceRefreshAuthorizationOrThrow,
450
463
  forceRefreshTokenOrThrow,
451
464
  oauthAppRowToProviderConfig
452
- } from "./cli-w1apjecb.js";
465
+ } from "./cli-711vwbxn.js";
466
+ import"./cli-wfnemr1t.js";
467
+ import {
468
+ normalizeModelKey
469
+ } from "./cli-hqr88ew6.js";
470
+ import"./cli-6cvfxs8r.js";
453
471
  import {
454
472
  AgentFsProvider,
455
473
  FilesError,
@@ -468,14 +486,14 @@ import {
468
486
  startWorkflowExecution,
469
487
  validateJsonSchema,
470
488
  workflowSaveWarnings
471
- } from "./cli-1w0vj3gw.js";
489
+ } from "./cli-naqzr17j.js";
472
490
  import {
473
491
  createApprovalFollowUpTask,
474
492
  definitionNodeIds,
475
493
  generateEdges,
476
494
  validateDefinition
477
- } from "./cli-6r2f7edk.js";
478
- import"./cli-6thk4jz3.js";
495
+ } from "./cli-20krdhz5.js";
496
+ import"./cli-1y7yywmj.js";
479
497
  import {
480
498
  CLIENT_ID,
481
499
  MAX_CODEX_OAUTH_SLOT,
@@ -486,10 +504,10 @@ import {
486
504
  getAccountId,
487
505
  getValidCodexOAuth,
488
506
  loadAllCodexOAuthSlots
489
- } from "./cli-7jykv7ef.js";
490
- import"./cli-b07hr3bt.js";
491
- import"./cli-h6vtvt5c.js";
507
+ } from "./cli-rc6pf5kv.js";
492
508
  import"./cli-2x0e561b.js";
509
+ import"./cli-txqb9s38.js";
510
+ import"./cli-h6vtvt5c.js";
493
511
  import"./cli-fad8m16k.js";
494
512
  import"./cli-bsakbbkm.js";
495
513
  import"./cli-zxg80zs9.js";
@@ -509,18 +527,22 @@ import {
509
527
  recordDbRetentionStatement,
510
528
  recordDbRetentionSweep,
511
529
  recordSessionCost,
512
- require_content_type,
513
530
  startSlackApp,
514
531
  startSpan,
515
532
  stopSlackApp,
516
533
  withRemoteContext,
517
534
  withSpan,
518
535
  withSpanContext
519
- } from "./cli-tcc4wd5d.js";
520
- import"./cli-q51yprfq.js";
536
+ } from "./cli-cvv9k0hh.js";
537
+ import"./cli-n2wq4j7m.js";
538
+ import {
539
+ require_content_type
540
+ } from "./cli-wmgrkscj.js";
521
541
  import"./cli-3er6gcsn.js";
522
- import"./cli-yatxqynm.js";
523
- import"./cli-v3zhnxsb.js";
542
+ import"./cli-hjwmhcg1.js";
543
+ import"./cli-rhs9pz33.js";
544
+ import"./cli-9ha6hs2d.js";
545
+ import"./cli-r610p9bs.js";
524
546
  import {
525
547
  InvalidRuntimeTokenError,
526
548
  RuntimePoolError,
@@ -559,11 +581,11 @@ import {
559
581
  unlinkIdentity,
560
582
  updateRuntimeController,
561
583
  updateRuntimePool
562
- } from "./cli-3nxdmjrc.js";
563
- import"./cli-cd0hv9nh.js";
584
+ } from "./cli-mgz4nbgg.js";
585
+ import"./cli-j67mh2vg.js";
564
586
  import {
565
587
  bedrockProviderModule
566
- } from "./cli-h7k0xtry.js";
588
+ } from "./cli-hvqbtna4.js";
567
589
  import"./cli-phre6gz0.js";
568
590
  import"./cli-eh1ytktb.js";
569
591
  import"./cli-txgcknmy.js";
@@ -578,7 +600,7 @@ import"./cli-1m2e086m.js";
578
600
  import"./cli-btbzvq95.js";
579
601
  import"./cli-p7z6q2m6.js";
580
602
  import"./cli-wnb9b2g3.js";
581
- import"./cli-bd957dgb.js";
603
+ import"./cli-f527ce74.js";
582
604
  import {
583
605
  import_websocket,
584
606
  import_websocket_server
@@ -587,7 +609,7 @@ import"./cli-pzxba7gw.js";
587
609
  import {
588
610
  getUserGrant,
589
611
  init_rbac_roles
590
- } from "./cli-anzdk0z4.js";
612
+ } from "./cli-d9286m9h.js";
591
613
  import {
592
614
  LEGACY_RULES,
593
615
  can,
@@ -596,10 +618,12 @@ import {
596
618
  init_rbac,
597
619
  isRbacEnabled,
598
620
  setAuditSink
599
- } from "./cli-7pz3b7x5.js";
621
+ } from "./cli-sksetb2q.js";
600
622
  import {
601
623
  API_DRAINING_HEADER,
602
624
  CANDIDATE_SET_MULTIPLIER,
625
+ CHAT_SDK_ADAPTER_NAMES,
626
+ CHAT_TASK_ONLY_TOOLS,
603
627
  CODEX_AUTH_WATCH_NAMESPACE,
604
628
  CredentialBindingSchema,
605
629
  DB_RETENTION_TUNING_BOUNDS,
@@ -610,6 +634,7 @@ import {
610
634
  KvTypeCollisionError,
611
635
  MAX_DB_RETENTION_DAYS,
612
636
  MEMORY_KEY_MAX_LENGTH,
637
+ MODEL_PROFILE_CONFIG_KEY,
613
638
  NOT_EXTENSION_AGENT_SQL,
614
639
  OpenAI,
615
640
  ReservedAgentRoleError,
@@ -626,6 +651,7 @@ import {
626
651
  cancelTask,
627
652
  catalog_default,
628
653
  channelTopic,
654
+ chatThreadOfContextKey,
629
655
  checkoutPromptTemplate,
630
656
  claimKv,
631
657
  claimMentions,
@@ -656,6 +682,7 @@ import {
656
682
  createLogEntry,
657
683
  createMcpServer,
658
684
  createMetric,
685
+ createModelProfile,
659
686
  createOAuthApp,
660
687
  createPage,
661
688
  createScheduledTask,
@@ -677,6 +704,7 @@ import {
677
704
  deleteKv,
678
705
  deleteMcpServer,
679
706
  deleteMetric,
707
+ deleteModelProfile,
680
708
  deleteOAuthTokens,
681
709
  deletePage,
682
710
  deletePricingRow,
@@ -715,7 +743,6 @@ import {
715
743
  getAllChannelActivityCursors,
716
744
  getAllLogs,
717
745
  getAllPricingRows,
718
- getAllServices,
719
746
  getAllSessionCosts,
720
747
  getAllTasks,
721
748
  getAllUsers,
@@ -752,6 +779,7 @@ import {
752
779
  getMetric,
753
780
  getMetricVersion,
754
781
  getMetricVersions,
782
+ getModelProfileByName,
755
783
  getOAuthApp,
756
784
  getOAuthAppById,
757
785
  getOAuthTokens,
@@ -787,7 +815,6 @@ import {
787
815
  getScriptRunByIdempotencyKey,
788
816
  getScriptRunExecutionArgs,
789
817
  getScriptRunJournalStep,
790
- getServicesByAgentId,
791
818
  getSessionCostSummary,
792
819
  getSessionCostsByAgentId,
793
820
  getSessionCostsByTaskId,
@@ -842,6 +869,7 @@ import {
842
869
  init_human_free,
843
870
  init_key_paths,
844
871
  init_memory,
872
+ init_model_profiles,
845
873
  init_oauth,
846
874
  init_openai,
847
875
  init_org_website,
@@ -850,6 +878,7 @@ import {
850
878
  init_reranker,
851
879
  init_runtime,
852
880
  init_runtime_pools_flag,
881
+ init_schema,
853
882
  init_secret_registry,
854
883
  init_steering,
855
884
  init_swarm_config_guard,
@@ -866,6 +895,9 @@ import {
866
895
  installMcpServer,
867
896
  installSkill,
868
897
  internalConfigKeyError,
898
+ isAdapterEntryEnabled,
899
+ isApiOnlyConfigKey,
900
+ isChatSdkTask,
869
901
  isExtensionAgent,
870
902
  isFilledOrgLogoUrl,
871
903
  isInternalConfigKey,
@@ -886,6 +918,7 @@ import {
886
918
  listKv,
887
919
  listMcpServers,
888
920
  listMetricsByAgent,
921
+ listModelProfiles,
889
922
  listPagesByAgent,
890
923
  listRecentSessions,
891
924
  listScriptApisForScript,
@@ -905,6 +938,8 @@ import {
905
938
  markSteeringHandled,
906
939
  markSteeringUndeliverable,
907
940
  maskSecrets,
941
+ modelProfileRefError,
942
+ modelProfileReferences,
908
943
  moveAssetKey,
909
944
  normalizeDate,
910
945
  normalizeOrgWebsite,
@@ -954,6 +989,7 @@ import {
954
989
  startTask,
955
990
  supersedeTask,
956
991
  sweepExpiredKv,
992
+ taskProfileError,
957
993
  telemetry,
958
994
  touchScratchScriptLastUsed,
959
995
  trackRelayed,
@@ -972,6 +1008,7 @@ import {
972
1008
  updateAgentStatusFromCapacity,
973
1009
  updateAuthorizationIdentity,
974
1010
  updateMetric,
1011
+ updateModelProfile,
975
1012
  updateOAuthAppById,
976
1013
  updatePage,
977
1014
  updateScheduledTask,
@@ -998,9 +1035,10 @@ import {
998
1035
  upsertSkillFiles,
999
1036
  upsertSwarmConfig,
1000
1037
  validateConfigValue,
1038
+ validateExportToEnv,
1001
1039
  withFavoriteFlags
1002
- } from "./cli-fmhj1ehj.js";
1003
- import"./cli-ywm1azg9.js";
1040
+ } from "./cli-y3167wt1.js";
1041
+ import"./cli-9jzdj90z.js";
1004
1042
  import {
1005
1043
  ActiveSessionSchema,
1006
1044
  AgentAcpStatusSchema,
@@ -1059,6 +1097,8 @@ import {
1059
1097
  MetricSchema,
1060
1098
  MetricVersionSchema,
1061
1099
  MetricWidgetSchema,
1100
+ ModelProfileNameSchema,
1101
+ ModelProfileSchema,
1062
1102
  ModelTierSchema,
1063
1103
  OnUnsupportedSchema,
1064
1104
  PageAuthModeSchema,
@@ -1096,7 +1136,6 @@ import {
1096
1136
  ScriptRunStatusSchema,
1097
1137
  ScriptScopeSchema,
1098
1138
  ScriptVersionRecordSchema,
1099
- ServiceSchema,
1100
1139
  SessionCostModelBreakdownSchema,
1101
1140
  SessionCostSchema,
1102
1141
  SessionLogSchema,
@@ -1136,22 +1175,22 @@ import {
1136
1175
  isSafeBundlePath,
1137
1176
  isTerminalTaskStatus,
1138
1177
  splitLegacyModelAlias
1139
- } from "./cli-hsem16hb.js";
1178
+ } from "./cli-pe05f8yk.js";
1140
1179
  import {
1141
1180
  IDENTITY_FIELD_BUDGETS,
1142
1181
  IdentityFieldBudgetError,
1143
1182
  init_identity_field_budget
1144
- } from "./cli-r4fqfpwf.js";
1145
- import {
1146
- init_steering_enabled,
1147
- isSteeringEnabled
1148
- } from "./cli-7mm2r1kp.js";
1183
+ } from "./cli-78vv7446.js";
1149
1184
  import {
1150
1185
  getSlackConfiguration,
1151
1186
  init_config
1152
1187
  } from "./cli-dmtrxqhg.js";
1188
+ import {
1189
+ init_steering_enabled,
1190
+ isSteeringEnabled
1191
+ } from "./cli-7mm2r1kp.js";
1192
+ import"./cli-x1r545x8.js";
1153
1193
  import"./cli-mvzyxwg3.js";
1154
- import"./cli-tadfgd0t.js";
1155
1194
  import {
1156
1195
  getAllTemplateDefinitions,
1157
1196
  getTemplateDefinition,
@@ -1178,10 +1217,7 @@ import {
1178
1217
  getOpenRouterAttributionHeaders,
1179
1218
  init_openrouter_base_url
1180
1219
  } from "./cli-a35hzdve.js";
1181
- import {
1182
- init_src,
1183
- isAlias
1184
- } from "./cli-014vns2y.js";
1220
+ import"./cli-stfs0k5s.js";
1185
1221
  import {
1186
1222
  exports_zod,
1187
1223
  init_zod
@@ -1202,17 +1238,17 @@ import {
1202
1238
  import {
1203
1239
  init_package,
1204
1240
  package_default
1205
- } from "./cli-5er5gjbm.js";
1241
+ } from "./cli-r35rw5de.js";
1206
1242
  import {
1207
1243
  init_event_bus,
1208
1244
  workflowEventBus
1209
- } from "./cli-81r1wjyv.js";
1245
+ } from "./cli-z780ec7c.js";
1210
1246
  import {
1211
1247
  init_bus,
1212
1248
  realtimeBus
1213
1249
  } from "./cli-zf8jjwex.js";
1214
1250
  import"./cli-h8w3fg83.js";
1215
- import"./cli-db3m6zdt.js";
1251
+ import"./cli-t02z59f2.js";
1216
1252
  import {
1217
1253
  MAX_PROFILE_FILE_LENGTH,
1218
1254
  agentFsFileRoute,
@@ -1228,7 +1264,7 @@ import {
1228
1264
  parseEnvFlag
1229
1265
  } from "./cli-ej6gbxfb.js";
1230
1266
  import"./cli-1srmzpr3.js";
1231
- import"./cli-0p3enk35.js";
1267
+ import"./cli-s69nfyeh.js";
1232
1268
  import {
1233
1269
  getApiKey,
1234
1270
  init_api_key
@@ -1239,7 +1275,11 @@ import {
1239
1275
  registerVolatileSecret,
1240
1276
  scrubObject,
1241
1277
  scrubSecrets
1242
- } from "./cli-d161g69q.js";
1278
+ } from "./cli-shk7r5cs.js";
1279
+ import {
1280
+ init_src,
1281
+ isAlias
1282
+ } from "./cli-e3g5mbkz.js";
1243
1283
  import {
1244
1284
  __require,
1245
1285
  __toESM
@@ -4024,6 +4064,7 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
4024
4064
  init_src();
4025
4065
  init_zod();
4026
4066
  init_db3();
4067
+ init_model_profiles();
4027
4068
  init_db2();
4028
4069
  init_telemetry();
4029
4070
  init_types();
@@ -4171,7 +4212,7 @@ var updateAgentRuntimeRoute = route({
4171
4212
  path: "/api/agents/{id}/runtime",
4172
4213
  pattern: ["api", "agents", null, "runtime"],
4173
4214
  summary: "Update an agent's runtime harness and default model",
4174
- description: "Updates `agents.harness_provider` and agent-scoped runtime config. The settings apply to future provider sessions. For `model`, `reasoning_effort`, and `claude.transport`: omit the field to leave it unchanged, send `null` to clear the corresponding override, or send a value to set it. A `model` the model catalog does not list is a 400 unless `allow_custom_model` is true. A `latest:` alias is not accepted here (aliases resolve for a task's model and for `MODEL_TIER_*` values); `reasoning_effort` must be one the harness and model support.",
4215
+ description: "Updates `agents.harness_provider` and agent-scoped runtime config. The settings apply to future provider sessions. For `model`, `reasoning_effort`, and `claude.transport`: omit the field to leave it unchanged, send `null` to clear the corresponding override, or send a value to set it. A `model` the model catalog does not list is a 400 unless `allow_custom_model` is true. A `latest:` alias is not accepted here (aliases resolve for a task's model and for `MODEL_TIER_*` values); `reasoning_effort` must be one the harness and model support. `profile` sets the agent's default model profile (same tri-state): it must exist and run on `harness_provider`, and it cannot be sent with `model`. Sending `model`, or changing the model or effort override by any path, clears the default profile.",
4175
4216
  tags: ["Agents"],
4176
4217
  params: exports_external.object({ id: exports_external.string() }),
4177
4218
  query: exports_external.object({ repoId: exports_external.string().optional() }),
@@ -4180,6 +4221,7 @@ var updateAgentRuntimeRoute = route({
4180
4221
  model: exports_external.string().trim().min(1).nullable().optional(),
4181
4222
  allow_custom_model: exports_external.boolean().optional().default(false),
4182
4223
  reasoning_effort: ReasoningEffortSchema.nullable().optional(),
4224
+ profile: exports_external.string().trim().min(1).nullable().optional(),
4183
4225
  acp: AcpRuntimeConfigSchema.optional(),
4184
4226
  claude: ClaudeRuntimeConfigSchema.optional()
4185
4227
  }),
@@ -4824,7 +4866,22 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
4824
4866
  const parsed = await updateAgentRuntimeRoute.parse(req, res, pathSegments, queryParams);
4825
4867
  if (!parsed)
4826
4868
  return true;
4827
- const { harness_provider, model, allow_custom_model, reasoning_effort, acp, claude } = parsed.body;
4869
+ const { harness_provider, model, allow_custom_model, reasoning_effort, profile, acp, claude } = parsed.body;
4870
+ if (typeof profile === "string") {
4871
+ if (model !== undefined) {
4872
+ jsonError(res, "Send `profile` or `model`, not both", 400);
4873
+ return true;
4874
+ }
4875
+ const picked = await getModelProfileByName(profile);
4876
+ if (!picked) {
4877
+ jsonError(res, `Unknown model profile "${profile}"`, 400);
4878
+ return true;
4879
+ }
4880
+ if (picked.harness !== harness_provider) {
4881
+ jsonError(res, `Model profile "${profile}" runs on the ${picked.harness} harness, not ${harness_provider}`, 400);
4882
+ return true;
4883
+ }
4884
+ }
4828
4885
  if (acp && harness_provider !== "acp") {
4829
4886
  jsonError(res, "ACP configuration requires harness_provider=acp", 400);
4830
4887
  return true;
@@ -4901,6 +4958,18 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
4901
4958
  description: "Set via PATCH /api/agents/{id}/runtime"
4902
4959
  });
4903
4960
  }
4961
+ if (model !== undefined || profile === null) {
4962
+ await deleteSwarmConfigByKey("agent", parsed.params.id, MODEL_PROFILE_CONFIG_KEY);
4963
+ } else if (profile !== undefined) {
4964
+ await upsertSwarmConfig({
4965
+ scope: "agent",
4966
+ scopeId: parsed.params.id,
4967
+ key: MODEL_PROFILE_CONFIG_KEY,
4968
+ value: profile,
4969
+ description: "Default model profile, set via PATCH /api/agents/{id}/runtime",
4970
+ exportToEnv: false
4971
+ });
4972
+ }
4904
4973
  if (claude?.transport === null) {
4905
4974
  await deleteSwarmConfigByKey("agent", parsed.params.id, "CLAUDE_TRANSPORT");
4906
4975
  } else if (claude?.transport !== undefined) {
@@ -4987,6 +5056,10 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
4987
5056
  jsonError(res, error.message, 400);
4988
5057
  return true;
4989
5058
  }
5059
+ if (modelProfileRefError(error) === "missing") {
5060
+ jsonError(res, `Unknown model profile "${profile}"`, 400);
5061
+ return true;
5062
+ }
4990
5063
  throw error;
4991
5064
  }
4992
5065
  if (!agent) {
@@ -7487,6 +7560,7 @@ init_secret_scrubber();
7487
7560
  // src/http/config.ts
7488
7561
  init_zod();
7489
7562
  init_db3();
7563
+ init_model_profiles();
7490
7564
 
7491
7565
  // src/be/org-profile.ts
7492
7566
  init_org_website();
@@ -7522,14 +7596,23 @@ init_request_auth_context();
7522
7596
  init_secret_scrubber();
7523
7597
  var MAX_ENV_PRESENCE_KEYS = 200;
7524
7598
  var SECRETS_FORCE_MASK_NOTE = " (secret values masked: this token cannot read secrets in this scope)";
7525
- var API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY", "SLACK_SIGNING_SECRET"]);
7526
7599
  function stripApiOnlyKeys(configs) {
7527
- return configs.filter((config) => !API_ONLY_CONFIG_KEYS.has(config.key) && !isInternalConfigKey(config.key));
7600
+ return configs.filter((config) => !isApiOnlyConfigKey(config.key) && !isInternalConfigKey(config.key));
7528
7601
  }
7529
7602
  function singleHeader(req, name) {
7530
7603
  const raw = req.headers[name];
7531
7604
  return Array.isArray(raw) ? raw[0] : raw;
7532
7605
  }
7606
+ function readsRepoSecrets(auth, agent) {
7607
+ if (auth?.kind !== "agent" || !auth.runtimeId)
7608
+ return true;
7609
+ return can({
7610
+ principal: { kind: "agent", agentId: auth.agentId, isLead: agent?.isLead ?? false },
7611
+ verb: "config.read.secrets",
7612
+ resource: { kind: "none" },
7613
+ source: "http"
7614
+ }).allow;
7615
+ }
7533
7616
  async function requestSecretReadScope(req, resolvedScope) {
7534
7617
  const auth = getRequestAuth(req);
7535
7618
  if (auth?.kind === "operator")
@@ -7566,6 +7649,9 @@ async function resolveSecretsRead(req, includeSecrets, resolvedScope) {
7566
7649
  };
7567
7650
  }
7568
7651
  async function ensureConfigAdmin(req, res, verb) {
7652
+ return ensureHttpPermission(req, res, verb, verb === "config.write.any" ? "Writing swarm config requires the lead agent" : "Deleting swarm config requires the lead agent");
7653
+ }
7654
+ async function ensureHttpPermission(req, res, verb, forbiddenMessage) {
7569
7655
  const auth = getRequestAuth(req);
7570
7656
  if (auth?.kind === "operator" || auth?.kind === "user")
7571
7657
  return true;
@@ -7582,7 +7668,7 @@ async function ensureConfigAdmin(req, res, verb) {
7582
7668
  source: "http"
7583
7669
  });
7584
7670
  if (!decision.allow) {
7585
- jsonError(res, verb === "config.write.any" ? "Writing swarm config requires the lead agent" : "Deleting swarm config requires the lead agent", 403);
7671
+ jsonError(res, forbiddenMessage, 403);
7586
7672
  return false;
7587
7673
  }
7588
7674
  return true;
@@ -7700,7 +7786,8 @@ var upsertConfig = route({
7700
7786
  value: exports_external.unknown(),
7701
7787
  isSecret: exports_external.boolean().optional(),
7702
7788
  envPath: exports_external.string().nullish(),
7703
- description: exports_external.string().nullish()
7789
+ description: exports_external.string().nullish(),
7790
+ exportToEnv: exports_external.boolean().optional().describe("Export this row into agent process env (default true on create, false for a few server-side secrets; omitted on update keeps the stored flag). Rejected as true for keys only the API server reads.")
7704
7791
  }),
7705
7792
  responses: {
7706
7793
  200: { description: "Config entry upserted", schema: SwarmConfigSchema },
@@ -7839,7 +7926,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
7839
7926
  return true;
7840
7927
  if (!await ensureConfigAdmin(req, res, "config.write.any"))
7841
7928
  return true;
7842
- const { scope, scopeId, key, value, isSecret, envPath, description } = parsed.body;
7929
+ const { scope, scopeId, key, value, isSecret, envPath, description, exportToEnv } = parsed.body;
7843
7930
  if (scope === "global" && scopeId) {
7844
7931
  jsonError(res, "Global scope must not have scopeId", 400);
7845
7932
  return true;
@@ -7856,7 +7943,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
7856
7943
  jsonError(res, internalConfigKeyError(key).message, 400);
7857
7944
  return true;
7858
7945
  }
7859
- const validationError = validateConfigValue(key, value);
7946
+ const validationError = validateConfigValue(key, value) ?? validateExportToEnv(key, exportToEnv);
7860
7947
  if (validationError) {
7861
7948
  jsonError(res, validationError, 400);
7862
7949
  return true;
@@ -7870,7 +7957,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
7870
7957
  value: String(value),
7871
7958
  isSecret: isSecret || false,
7872
7959
  envPath: envPath || null,
7873
- description: description || null
7960
+ description: description || null,
7961
+ exportToEnv
7874
7962
  });
7875
7963
  if (scope === "global" && key === "SWARM_ORG_WEBSITE") {
7876
7964
  await fillOrgLogoFromWebsite(String(value));
@@ -7880,7 +7968,11 @@ async function handleConfig(req, res, pathSegments, queryParams) {
7880
7968
  }
7881
7969
  const result = includeSecrets || !config.isSecret ? config : maskSecrets([config])[0];
7882
7970
  upsertConfig.respond(res, 200, result);
7883
- } catch (_error) {
7971
+ } catch (error) {
7972
+ if (modelProfileRefError(error) === "missing") {
7973
+ jsonError(res, `Unknown model profile "${String(value)}"`, 400);
7974
+ return true;
7975
+ }
7884
7976
  jsonError(res, "Failed to upsert config", 500);
7885
7977
  }
7886
7978
  return true;
@@ -8428,7 +8520,7 @@ async function sendReviewBatch(input, deps = {}) {
8428
8520
  const target = targets[index];
8429
8521
  const isRepair = index >= wonIds.length;
8430
8522
  if (reply.status === "rejected") {
8431
- console.warn(scrubSecrets(`[comb] sent reply failed comment=${target.id} task=${target.taskId}: ${errorMessage(reply.reason)}`));
8523
+ console.warn(scrubSecrets(`[comb] sent reply failed comment=${target.id} task=${target.taskId}: ${errorMessage2(reply.reason)}`));
8432
8524
  if (isRepair)
8433
8525
  skipped.push(alreadySent(target.id, target.taskId));
8434
8526
  } else if (isRepair) {
@@ -8459,7 +8551,7 @@ async function readComment(agentFs, drive, versionOf, id) {
8459
8551
  } catch (error) {
8460
8552
  if (error instanceof FilesError && error.code === "NotFound")
8461
8553
  return { id, thread: null };
8462
- throw new ReviewBatchError(502, scrubSecrets(`agent-fs could not read comment ${id}: ${errorMessage(error)}`));
8554
+ throw new ReviewBatchError(502, scrubSecrets(`agent-fs could not read comment ${id}: ${errorMessage2(error)}`));
8463
8555
  }
8464
8556
  const { comment } = thread;
8465
8557
  const version = comment.fileVersion ?? (comment.parentId ? undefined : await versionOf(comment));
@@ -8519,7 +8611,7 @@ async function settleClaims(ids, claim, next) {
8519
8611
  settled.forEach((result, index) => {
8520
8612
  if (result.status === "fulfilled" && result.value)
8521
8613
  return;
8522
- const why = result.status === "rejected" ? errorMessage(result.reason) : "another send holds the claim";
8614
+ const why = result.status === "rejected" ? errorMessage2(result.reason) : "another send holds the claim";
8523
8615
  console.warn(scrubSecrets(`[comb] could not ${next ? "record" : "release"} the claim on comment=${ids[index]}: ${why}`));
8524
8616
  });
8525
8617
  }
@@ -8621,7 +8713,7 @@ async function requesterLabel(userId) {
8621
8713
  return "an operator (swarm API key)";
8622
8714
  return user.email ? `${user.name} <${user.email}>` : user.name;
8623
8715
  }
8624
- function errorMessage(error) {
8716
+ function errorMessage2(error) {
8625
8717
  return error instanceof Error ? error.message : String(error);
8626
8718
  }
8627
8719
 
@@ -8828,64 +8920,6 @@ async function handleContext(req, res, pathSegments, queryParams, myAgentId) {
8828
8920
  return false;
8829
8921
  }
8830
8922
 
8831
- // src/http/ecosystem.ts
8832
- init_zod();
8833
- init_db3();
8834
- var EcosystemAppSchema = exports_external.object({
8835
- name: exports_external.string(),
8836
- script: exports_external.string(),
8837
- cwd: exports_external.string().optional(),
8838
- interpreter: exports_external.string().optional(),
8839
- args: exports_external.array(exports_external.string()).optional(),
8840
- env: exports_external.record(exports_external.string(), exports_external.string()).optional()
8841
- });
8842
- var getEcosystem = route({
8843
- method: "get",
8844
- path: "/ecosystem",
8845
- pattern: ["ecosystem"],
8846
- summary: "Get PM2 ecosystem config for agent services",
8847
- tags: ["Ecosystem"],
8848
- auth: { apiKey: true, agentId: true },
8849
- responses: {
8850
- 200: {
8851
- description: "PM2 ecosystem config",
8852
- schema: exports_external.object({ apps: exports_external.array(EcosystemAppSchema) })
8853
- },
8854
- 400: { description: "Missing X-Agent-ID" }
8855
- }
8856
- });
8857
- async function handleEcosystem(req, res, pathSegments, myAgentId) {
8858
- if (getEcosystem.match(req.method, pathSegments)) {
8859
- if (!myAgentId) {
8860
- jsonError(res, "Missing X-Agent-ID header", 400);
8861
- return true;
8862
- }
8863
- const services = await getServicesByAgentId(myAgentId);
8864
- const ecosystem = {
8865
- apps: services.filter((s) => s.script).map((s) => {
8866
- const app = {
8867
- name: s.name,
8868
- script: s.script
8869
- };
8870
- if (s.cwd)
8871
- app.cwd = s.cwd;
8872
- if (s.interpreter)
8873
- app.interpreter = s.interpreter;
8874
- if (s.args && s.args.length > 0)
8875
- app.args = s.args;
8876
- if (s.env && Object.keys(s.env).length > 0)
8877
- app.env = s.env;
8878
- if (s.port)
8879
- app.env = { ...app.env || {}, PORT: String(s.port) };
8880
- return app;
8881
- })
8882
- };
8883
- getEcosystem.respond(res, 200, ecosystem);
8884
- return true;
8885
- }
8886
- return false;
8887
- }
8888
-
8889
8923
  // src/http/events.ts
8890
8924
  init_zod();
8891
8925
  init_types();
@@ -9107,6 +9141,7 @@ declare module "swarm-extension" {
9107
9141
  | "linear"
9108
9142
  | "jira"
9109
9143
  | "comb"
9144
+ | "chat"
9110
9145
  | undefined;
9111
9146
  routingReason?:
9112
9147
  | "skill"
@@ -9147,6 +9182,7 @@ declare module "swarm-extension" {
9147
9182
  model?: string | undefined;
9148
9183
  modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;
9149
9184
  effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
9185
+ profile?: string | undefined;
9150
9186
  scheduleId?: string | undefined;
9151
9187
  workflowRunId?: string | undefined;
9152
9188
  workflowRunStepId?: string | undefined;
@@ -9220,7 +9256,8 @@ declare module "swarm-extension" {
9220
9256
  | "system"
9221
9257
  | "linear"
9222
9258
  | "jira"
9223
- | "comb";
9259
+ | "comb"
9260
+ | "chat";
9224
9261
  tags: string[];
9225
9262
  priority: number;
9226
9263
  dependsOn: string[];
@@ -9279,6 +9316,9 @@ declare module "swarm-extension" {
9279
9316
  resolvedModel?: string | undefined;
9280
9317
  modelSource?: string | undefined;
9281
9318
  modelAlias?: string | undefined;
9319
+ resolvedEffort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
9320
+ profile?: string | undefined;
9321
+ resolvedProfile?: string | undefined;
9282
9322
  scheduleId?: string | undefined;
9283
9323
  workflowRunId?: string | null | undefined;
9284
9324
  workflowRunStepId?: string | null | undefined;
@@ -9555,6 +9595,43 @@ declare module "swarm-extension" {
9555
9595
  kind: "broadcast";
9556
9596
  };
9557
9597
  }
9598
+ /** A Chat SDK message after its thread buffer flushed, before routing. */
9599
+ export interface ChatRouteEvent {
9600
+ /** Adapter name, such as \`telegram\` or \`slack\`. */
9601
+ adapter: string;
9602
+ channelId: string;
9603
+ /** The Chat SDK thread id, prefixed with the adapter name. */
9604
+ threadId: string;
9605
+ /** The first buffered sender. */
9606
+ userId: string;
9607
+ userName?: string;
9608
+ /** The buffered messages, joined. */
9609
+ text: string;
9610
+ botMentioned: boolean;
9611
+ isDirectMessage: boolean;
9612
+ /** Set when earlier swarm tasks exist in this thread. */
9613
+ threadContext?: {
9614
+ adapter: string;
9615
+ threadId: string;
9616
+ };
9617
+ }
9618
+ export interface ChatRouteModify {
9619
+ target:
9620
+ | {
9621
+ kind: "agent";
9622
+ agentId: string;
9623
+ }
9624
+ | {
9625
+ kind: "lead";
9626
+ }
9627
+ | {
9628
+ kind: "broadcast";
9629
+ }
9630
+ | {
9631
+ kind: "workflow";
9632
+ workflowId: string;
9633
+ };
9634
+ }
9558
9635
  export type HeartbeatClassification = "no-session" | "stale-session" | "fresh-stalled";
9559
9636
  export type HeartbeatAction = "supersede-resume" | "fail" | "record";
9560
9637
  export interface HeartbeatRemediateEvent {
@@ -9599,6 +9676,18 @@ declare module "swarm-extension" {
9599
9676
  threadTs?: string;
9600
9677
  taskId?: string;
9601
9678
  }
9679
+ /** A Chat SDK message the swarm received or sent. */
9680
+ export interface ChatMessageEvent {
9681
+ adapter: string;
9682
+ channelId: string;
9683
+ threadId: string;
9684
+ /** The sender, for an inbound message. */
9685
+ userId?: string;
9686
+ text: string;
9687
+ direction: "inbound" | "outbound";
9688
+ taskId?: string;
9689
+ messageId?: string;
9690
+ }
9602
9691
  /**
9603
9692
  * A person answered an approval request. Fires for workflow and standalone
9604
9693
  * requests. Timeouts and cancellations do not emit it today.
@@ -9707,6 +9796,11 @@ declare module "swarm-extension" {
9707
9796
  modify: SlackRouteModify;
9708
9797
  result: PreResult<SlackRouteModify>;
9709
9798
  };
9799
+ "pre.chat.route": {
9800
+ event: ChatRouteEvent;
9801
+ modify: ChatRouteModify;
9802
+ result: PreResult<ChatRouteModify>;
9803
+ };
9710
9804
  "pre.heartbeat.remediate": {
9711
9805
  event: HeartbeatRemediateEvent;
9712
9806
  modify: HeartbeatRemediateModify;
@@ -9752,6 +9846,11 @@ declare module "swarm-extension" {
9752
9846
  modify: never;
9753
9847
  result: void;
9754
9848
  };
9849
+ "post.chat.message": {
9850
+ event: ChatMessageEvent;
9851
+ modify: never;
9852
+ result: void;
9853
+ };
9755
9854
  "post.approval.resolved": {
9756
9855
  event: ApprovalResolvedEvent;
9757
9856
  modify: never;
@@ -9899,6 +9998,512 @@ void _configIsValid;
9899
9998
 
9900
9999
  // src/extensions/catalog.generated.json
9901
10000
  var catalog_generated_default = {
10001
+ "comms-adherence-gate": {
10002
+ manifestFile: "manifest.json",
10003
+ manifest: {
10004
+ $schema: "../manifest.schema.json",
10005
+ name: "comms-adherence-gate",
10006
+ description: "Judges every Slack-facing store-progress completion against the requester's comms preferences and latest request with Jev (TypeSafe or OpenRouter), and in enforce blocks a non-adherent completion once per task",
10007
+ version: "1.5.0",
10008
+ runtime: "api",
10009
+ assets: {
10010
+ hooks: "hooks.ts"
10011
+ }
10012
+ },
10013
+ files: {
10014
+ "hooks.ts": `import { type ApiCtx, block, type SwarmExtension } from "swarm-extension";
10015
+ import { z } from "zod";
10016
+
10017
+ /**
10018
+ * comms-adherence-gate
10019
+ *
10020
+ * When an agent calls \`store-progress\` with status "completed" on a task that
10021
+ * reaches a human in Slack, judge the \`output\` against the requesting user's
10022
+ * stored comms preferences (users.metadata.$.comms) with Jev, and against what
10023
+ * that user actually asked for in their latest message. An explicit request
10024
+ * ("tell me a long joke", "explain in detail") outranks the stored default, so
10025
+ * a requested long answer is not a verbosity failure.
10026
+ *
10027
+ * Transport (config.provider): Jev is served by TypeSafe directly and by
10028
+ * OpenRouter's Decisions endpoint. Both take the same {model, state, questions}
10029
+ * body and return the same \`answers\` shape. \`auto\` uses TYPESAFE_API_KEY when
10030
+ * set, else OPENROUTER_API_KEY.
10031
+ *
10032
+ * modes (config.mode):
10033
+ * off -> never evaluate
10034
+ * shadow -> evaluate + record, never block (default)
10035
+ * enforce -> record, and block the completion up to maxBlocksPerTask times
10036
+ * per task with actionable feedback
10037
+ *
10038
+ * Fails OPEN on every error: a judge outage must never wall the swarm.
10039
+ * Every verdict lands in KV namespace \`ext:comms-gate\`.
10040
+ */
10041
+
10042
+ export const config = z.object({
10043
+ mode: z.enum(["off", "shadow", "enforce"]).default("shadow"),
10044
+ /** A dimension scoring below this fails. */
10045
+ threshold: z.number().min(0).max(1).default(0.5),
10046
+ timeoutMs: z.number().int().positive().default(4000),
10047
+ maxBlocksPerTask: z.number().int().min(0).default(1),
10048
+ provider: z.enum(["auto", "typesafe", "openrouter"]).default("auto"),
10049
+ /** Jev model on the TypeSafe transport. */
10050
+ model: z.string().default("jev-latest"),
10051
+ /** Jev model on the OpenRouter transport. */
10052
+ openrouterModel: z.string().default("typesafe/jev-1.13"),
10053
+ /** Explicit TypeSafe key; otherwise TYPESAFE_API_KEY from the API process env. */
10054
+ apiKey: z.string().optional(),
10055
+ });
10056
+
10057
+ type Cfg = z.infer<typeof config>;
10058
+ type Swarm = ApiCtx["swarm"];
10059
+
10060
+ const NS = "ext:comms-gate";
10061
+ const TYPESAFE_ENDPOINT = "https://api.typesafe.ai/v1/systemone";
10062
+ const OPENROUTER_ENDPOINT = "https://openrouter.ai/api/alpha/decisions";
10063
+ const UUID_RE = /^[0-9a-fA-F-]{36}$/;
10064
+ // The dispatcher cancels a handler after 5 s; leave room for the DB read and KV writes.
10065
+ const MAX_TIMEOUT_MS = 4000;
10066
+
10067
+ export const cfgOf = (raw: unknown): Cfg => {
10068
+ const parsed = config.safeParse(raw ?? {});
10069
+ return parsed.success ? parsed.data : config.parse({});
10070
+ };
10071
+
10072
+ // kv keys accept [a-zA-Z0-9._:/%-]
10073
+ const safeKey = (raw: string | undefined | null): string =>
10074
+ (raw && raw.length > 0 ? raw : "unknown").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120);
10075
+
10076
+ /** SDK envelopes: {success, data:{...}} or the bare payload. */
10077
+ type Envelope = {
10078
+ data?: { rows?: unknown[]; value?: unknown };
10079
+ rows?: unknown[];
10080
+ value?: unknown;
10081
+ } | null;
10082
+
10083
+ const cell = (row: unknown, i: number, key: string): unknown =>
10084
+ Array.isArray(row) ? row[i] : (row as Record<string, unknown>)?.[key];
10085
+
10086
+ /**
10087
+ * The newest human message sits at the END of a Slack task: the
10088
+ * \`<thread_context>\` blob comes first and can run to thousands of chars, so a
10089
+ * leading slice of \`agent_tasks.task\` shows the judge everything except the
10090
+ * actual request. Take the tail, then the part after the last \`</thread_context>\`.
10091
+ */
10092
+ const CTX_END = "</thread_context>";
10093
+ export const latestAsk = (tail: string): string => {
10094
+ const end = tail.lastIndexOf(CTX_END);
10095
+ let out = (end >= 0 ? tail.slice(end + CTX_END.length) : tail)
10096
+ .replace(/\\[Thread follow-up[^\\]]*\\]/g, "")
10097
+ .trim();
10098
+ // Some tasks end with the context block; fall back to the last messages in it.
10099
+ if (out.length < 8) out = (end >= 0 ? tail.slice(0, end) : tail).trim().slice(-800);
10100
+ return out.slice(0, 900);
10101
+ };
10102
+
10103
+ // Preferences are the DEFAULT. A direct request outranks them.
10104
+ const JUDGING_RULES = [
10105
+ "The requester's explicit instruction in latest_request OUTRANKS requester_preferences. Preferences describe the DEFAULT; they never override a direct request.",
10106
+ "If latest_request asks for something long, detailed, creative, a story, a joke, a full explanation or a document, then length and structure serving that request are CORRECT and must NOT be penalized.",
10107
+ "Judge whether agent_message serves what latest_request actually asked for, written in the requester's manner. Do not judge it against the default preference when the request overrides it.",
10108
+ ];
10109
+
10110
+ type Prefs = { verbosity?: unknown; tone?: unknown; language?: unknown } & Record<string, unknown>;
10111
+
10112
+ /** One \`noul\` question per stated preference the gate knows how to judge. */
10113
+ export const buildQuestions = (prefs: Prefs): Record<string, unknown> => {
10114
+ const questions: Record<string, unknown> = {};
10115
+ if (prefs.verbosity) {
10116
+ questions.verbosity = {
10117
+ type: "noul",
10118
+ instructions:
10119
+ "Does agent_message respect the requester's verbosity preference, AS OVERRIDDEN by any explicit instruction in latest_request? Apply judging_rules.",
10120
+ criteria: {
10121
+ true: "Length and structure fit what latest_request asked for (or the stated preference when the request does not override it), and it leads with the result.",
10122
+ false:
10123
+ "Too long for what was asked, or it opens with preamble, recaps the request, narrates the process, or ends with an offer to elaborate. Length explicitly requested in latest_request is NOT a failure.",
10124
+ },
10125
+ };
10126
+ }
10127
+ if (prefs.tone) {
10128
+ questions.tone = {
10129
+ type: "noul",
10130
+ instructions:
10131
+ "Does agent_message match the requester's stated tone preference, as overridden by any explicit instruction in latest_request? Apply judging_rules.",
10132
+ criteria: {
10133
+ true: "The tone matches what the requester asked for.",
10134
+ false:
10135
+ "The tone conflicts with it (for example hedging, flattery or filler when the requester asked for direct and plain). A voice appropriate to an explicitly requested creative or narrative form is NOT a failure.",
10136
+ },
10137
+ };
10138
+ }
10139
+ if (prefs.language) {
10140
+ questions.language = {
10141
+ type: "noul",
10142
+ instructions: "Is agent_message written in the requester's stated language?",
10143
+ criteria: {
10144
+ true: "Written in the stated language.",
10145
+ false: "Written in a different language.",
10146
+ },
10147
+ };
10148
+ }
10149
+ return questions;
10150
+ };
10151
+
10152
+ const secretCache = new Map<string, { value: string; at: number }>();
10153
+ const KEY_TTL_MS = 5 * 60 * 1000;
10154
+ const usable = (v: string): boolean => Boolean(v) && !v.startsWith("[REDACTED");
10155
+ const envOf = (name: string): string => String(process.env[name] ?? "");
10156
+
10157
+ /**
10158
+ * Resolve a secret: explicit value, then the API process env. The API process loads
10159
+ * global swarm_config rows into process.env at boot and on config reload.
10160
+ * ctx.swarm.config_get cannot serve it: the SDK scrubs secrets to "[REDACTED:<name>]".
10161
+ */
10162
+ const secret = (name: string, explicit?: string): string => {
10163
+ const cached = secretCache.get(name);
10164
+ if (cached && Date.now() - cached.at < KEY_TTL_MS) return cached.value;
10165
+ const value = [String(explicit ?? ""), envOf(name)].find(usable) ?? "";
10166
+ if (usable(value)) secretCache.set(name, { value, at: Date.now() });
10167
+ return value;
10168
+ };
10169
+
10170
+ /** Test hook: forget cached secrets. */
10171
+ export const resetKeyCache = (): void => {
10172
+ secretCache.clear();
10173
+ };
10174
+
10175
+ export type JevTransport = {
10176
+ provider: "typesafe" | "openrouter";
10177
+ endpoint: string;
10178
+ key: string;
10179
+ model: string;
10180
+ };
10181
+
10182
+ /**
10183
+ * Pick the Jev transport. \`auto\` prefers TypeSafe and falls back to OpenRouter.
10184
+ * Throws when the chosen provider has no usable key; the handler fails open.
10185
+ */
10186
+ export const resolveTransport = (cfg: Cfg): JevTransport => {
10187
+ const typesafe = (): JevTransport | null => {
10188
+ const key = secret("TYPESAFE_API_KEY", cfg.apiKey);
10189
+ return usable(key)
10190
+ ? { provider: "typesafe", endpoint: TYPESAFE_ENDPOINT, key, model: cfg.model }
10191
+ : null;
10192
+ };
10193
+ const openrouter = (): JevTransport | null => {
10194
+ const key = secret("OPENROUTER_API_KEY");
10195
+ return usable(key)
10196
+ ? { provider: "openrouter", endpoint: OPENROUTER_ENDPOINT, key, model: cfg.openrouterModel }
10197
+ : null;
10198
+ };
10199
+ const transport =
10200
+ cfg.provider === "typesafe"
10201
+ ? typesafe()
10202
+ : cfg.provider === "openrouter"
10203
+ ? openrouter()
10204
+ : (typesafe() ?? openrouter());
10205
+ if (transport) return transport;
10206
+ throw new Error(
10207
+ cfg.provider === "typesafe"
10208
+ ? "no TYPESAFE_API_KEY"
10209
+ : cfg.provider === "openrouter"
10210
+ ? "no OPENROUTER_API_KEY"
10211
+ : "no TYPESAFE_API_KEY or OPENROUTER_API_KEY",
10212
+ );
10213
+ };
10214
+
10215
+ /** One Jev call; returns the \`noul\` score per question. Throws on any failure. */
10216
+ export const askJev = async (
10217
+ state: Record<string, unknown>,
10218
+ questions: Record<string, unknown>,
10219
+ transport: JevTransport,
10220
+ cfg: Cfg,
10221
+ outer?: AbortSignal,
10222
+ ): Promise<{ scores: Record<string, number>; latencyMs: number }> => {
10223
+ const controller = new AbortController();
10224
+ const timer = setTimeout(() => controller.abort(), Math.min(cfg.timeoutMs, MAX_TIMEOUT_MS));
10225
+ const onOuterAbort = () => controller.abort();
10226
+ outer?.addEventListener("abort", onOuterAbort);
10227
+ const t0 = Date.now();
10228
+ try {
10229
+ const res = await fetch(transport.endpoint, {
10230
+ method: "POST",
10231
+ headers: { "Content-Type": "application/json", Authorization: \`Bearer \${transport.key}\` },
10232
+ body: JSON.stringify({ state, model: transport.model, questions }),
10233
+ signal: controller.signal,
10234
+ });
10235
+ if (!res.ok) throw new Error(\`\${transport.provider} \${res.status}\`);
10236
+ const body = (await res.json()) as { answers?: Record<string, { noul?: unknown }> } | null;
10237
+ const answers = body?.answers;
10238
+ if (!answers || typeof answers !== "object") throw new Error("no answers in response");
10239
+ const scores: Record<string, number> = {};
10240
+ for (const [id, a] of Object.entries(answers)) {
10241
+ if (typeof a?.noul === "number") scores[id] = a.noul;
10242
+ }
10243
+ if (Object.keys(scores).length === 0) throw new Error("no scores in response");
10244
+ return { scores, latencyMs: Date.now() - t0 };
10245
+ } finally {
10246
+ clearTimeout(timer);
10247
+ outer?.removeEventListener("abort", onOuterAbort);
10248
+ }
10249
+ };
10250
+
10251
+ const SELFTEST = "comms-gate-selftest";
10252
+ const SELFTEST_PREFS = {
10253
+ tone: "direct, plain, no hedging",
10254
+ language: "English",
10255
+ verbosity:
10256
+ "very terse - answer in the first sentence, ~400 chars max. No preamble, no recap, no closing offer.",
10257
+ };
10258
+ const SELFTEST_BAD =
10259
+ "Great question! Let me walk you through what I did here. First I looked at the task, then I reviewed the relevant files, and after careful consideration I decided to go with option B. I hope this helps - let me know if you would like me to elaborate further on any of the above!";
10260
+ const SELFTEST_GOOD =
10261
+ "Went with option B: it is the only one that keeps the migration reversible. PR #123.";
10262
+
10263
+ /**
10264
+ * Run the judge on a known-bad (positive control, must score below threshold) and a
10265
+ * known-good (negative control, must score at or above it) message and record both
10266
+ * under \`selftest:last\`. Proves key, egress and KV from inside the API runtime
10267
+ * without touching real traffic.
10268
+ */
10269
+ const selfTest = async (swarm: Swarm, cfg: Cfg, signal?: AbortSignal): Promise<void> => {
10270
+ const started = Date.now();
10271
+ try {
10272
+ const transport = resolveTransport(cfg);
10273
+ const questions = { verbosity: buildQuestions(SELFTEST_PREFS).verbosity };
10274
+ const judge = (message: string) =>
10275
+ askJev(
10276
+ { requester_preferences: SELFTEST_PREFS, agent_message: message },
10277
+ questions,
10278
+ transport,
10279
+ cfg,
10280
+ signal,
10281
+ ).then((r) => r.scores.verbosity ?? null);
10282
+ const [bad, good] = await Promise.all([judge(SELFTEST_BAD), judge(SELFTEST_GOOD)]);
10283
+ await swarm.kv_set({
10284
+ namespace: NS,
10285
+ key: "selftest:last",
10286
+ value: {
10287
+ at: new Date().toISOString(),
10288
+ provider: transport.provider,
10289
+ threshold: cfg.threshold,
10290
+ positiveControlBadScore: bad,
10291
+ negativeControlGoodScore: good,
10292
+ controlsOk:
10293
+ typeof bad === "number" &&
10294
+ typeof good === "number" &&
10295
+ bad < cfg.threshold &&
10296
+ good >= cfg.threshold,
10297
+ latencyMs: Date.now() - started,
10298
+ },
10299
+ });
10300
+ } catch (err) {
10301
+ await swarm
10302
+ .kv_set({
10303
+ namespace: NS,
10304
+ key: "selftest:last",
10305
+ value: {
10306
+ at: new Date().toISOString(),
10307
+ error: String(err).slice(0, 400),
10308
+ latencyMs: Date.now() - started,
10309
+ },
10310
+ })
10311
+ .catch(() => {});
10312
+ }
10313
+ };
10314
+
10315
+ const TASK_SQL = \`SELECT t.source, t.slackThreadTs, t.agentId, t.requestedByUserId,
10316
+ json_extract(u.metadata, '$.comms') AS comms, u.name,
10317
+ substr(t.task, 1, 400) AS askHead,
10318
+ substr(t.task, -1400) AS askTail
10319
+ FROM agent_tasks t
10320
+ LEFT JOIN users u ON u.id = t.requestedByUserId
10321
+ WHERE t.id = ?\`;
10322
+
10323
+ const LEAD_SQL = "SELECT isLead FROM agents WHERE id = ?";
10324
+
10325
+ /**
10326
+ * Mirrors the \`task.progress.write\` rule store-progress enforces
10327
+ * (src/rbac/legacy-policy.ts): the caller owns the task, the task is
10328
+ * unassigned, or the caller is a lead. Lead status comes from the caller's
10329
+ * agents row, never from tool args. Extension callers return before this.
10330
+ */
10331
+ const mayWriteProgress = async (
10332
+ swarm: Swarm,
10333
+ callerId: string | undefined,
10334
+ taskAgentId: string,
10335
+ ): Promise<boolean> => {
10336
+ if (!callerId) return false;
10337
+ if (!taskAgentId || taskAgentId === callerId) return true;
10338
+ const q = (await swarm.db_query({ sql: LEAD_SQL, params: [callerId] })) as Envelope;
10339
+ const row = (q?.data?.rows ?? q?.rows ?? [])[0];
10340
+ return Number(cell(row, 0, "isLead") ?? 0) === 1;
10341
+ };
10342
+
10343
+ const extension: SwarmExtension = (api) => {
10344
+ api.on("pre.tool.call", async (event, ctx) => {
10345
+ if (event.tool !== "store-progress") return;
10346
+ if (event.requestInfo?.callOrigin === "extension") return;
10347
+
10348
+ const cfg = cfgOf(ctx.config);
10349
+ if (cfg.mode === "off") return;
10350
+
10351
+ const args = (event.args ?? {}) as Record<string, unknown>;
10352
+
10353
+ // Self-test hatch: a progress call whose text starts with the sentinel runs
10354
+ // the controls and records them under \`selftest:last\`.
10355
+ if (args.status !== "completed") {
10356
+ if (typeof args.progress === "string" && args.progress.startsWith(SELFTEST)) {
10357
+ await selfTest(ctx.swarm, cfg, ctx.signal);
10358
+ }
10359
+ return;
10360
+ }
10361
+
10362
+ const output = typeof args.output === "string" ? args.output.trim() : "";
10363
+ if (!output) return;
10364
+
10365
+ const taskId = String(args.taskId ?? event.requestInfo?.sourceTaskId ?? "");
10366
+ if (!UUID_RE.test(taskId)) return;
10367
+
10368
+ try {
10369
+ const q = (await ctx.swarm.db_query({ sql: TASK_SQL, params: [taskId] })) as Envelope;
10370
+ const row = (q?.data?.rows ?? q?.rows ?? [])[0];
10371
+ if (!row) return;
10372
+
10373
+ const source = String(cell(row, 0, "source") ?? "");
10374
+ const slackThreadTs = cell(row, 1, "slackThreadTs");
10375
+ const agentId = String(cell(row, 2, "agentId") ?? "");
10376
+ const requesterId = String(cell(row, 3, "requestedByUserId") ?? "");
10377
+ const commsRaw = cell(row, 4, "comms");
10378
+ const requesterName = String(cell(row, 5, "name") ?? requesterId);
10379
+ const threadOpening = String(cell(row, 6, "askHead") ?? "");
10380
+ const ask = latestAsk(String(cell(row, 7, "askTail") ?? ""));
10381
+
10382
+ // Judge only a completion store-progress will accept, so no agent can ship
10383
+ // another task's thread and preferences to the external judge.
10384
+ if (!(await mayWriteProgress(ctx.swarm, event.requestInfo?.agentId, agentId))) return;
10385
+
10386
+ // Only where the text actually reaches a human in Slack.
10387
+ if (source !== "slack" && !slackThreadTs) return;
10388
+ if (!commsRaw) return; // no stated preferences -> nothing to judge against
10389
+ const prefs = (typeof commsRaw === "string" ? JSON.parse(commsRaw) : commsRaw) as Prefs;
10390
+ if (!prefs || typeof prefs !== "object") return;
10391
+
10392
+ const questions = buildQuestions(prefs);
10393
+ if (Object.keys(questions).length === 0) return;
10394
+
10395
+ const transport = resolveTransport(cfg);
10396
+ const { scores, latencyMs } = await askJev(
10397
+ {
10398
+ role: "You audit an AI agent's final message to the human who requested the work.",
10399
+ requester: requesterName,
10400
+ requester_preferences: prefs,
10401
+ latest_request: ask,
10402
+ thread_opening: threadOpening,
10403
+ judging_rules: JUDGING_RULES,
10404
+ agent_message: output,
10405
+ },
10406
+ questions,
10407
+ transport,
10408
+ cfg,
10409
+ ctx.signal,
10410
+ );
10411
+
10412
+ const failed = Object.entries(scores)
10413
+ .filter(([, v]) => v < cfg.threshold)
10414
+ .map(([k]) => k);
10415
+
10416
+ const verdict = {
10417
+ at: new Date().toISOString(),
10418
+ taskId,
10419
+ agentId,
10420
+ source,
10421
+ requester: requesterName,
10422
+ requesterId,
10423
+ provider: transport.provider,
10424
+ scores,
10425
+ failed,
10426
+ threshold: cfg.threshold,
10427
+ mode: cfg.mode,
10428
+ latencyMs,
10429
+ preferences: prefs,
10430
+ askPreview: ask.slice(0, 400),
10431
+ outputPreview: output.slice(0, 700),
10432
+ };
10433
+
10434
+ await ctx.swarm.kv_incr({ namespace: NS, key: "total" });
10435
+ await ctx.swarm.kv_set({ namespace: NS, key: "last", value: verdict });
10436
+
10437
+ if (failed.length === 0) {
10438
+ await ctx.swarm.kv_incr({ namespace: NS, key: "pass" });
10439
+ return;
10440
+ }
10441
+
10442
+ await ctx.swarm.kv_incr({ namespace: NS, key: "fail" });
10443
+ for (const dim of failed) {
10444
+ await ctx.swarm.kv_incr({ namespace: NS, key: \`dim:\${safeKey(dim)}\` });
10445
+ }
10446
+
10447
+ const blocksKey = \`blocks:\${safeKey(taskId)}\`;
10448
+ // kv_get answers {success, data:{value}}, and {success:false} when absent.
10449
+ const prior = (await ctx.swarm.kv_get({ namespace: NS, key: blocksKey })) as Envelope;
10450
+ const priorBlocks = Number(prior?.data?.value ?? prior?.value ?? 0) || 0;
10451
+ const willBlock = cfg.mode === "enforce" && priorBlocks < cfg.maxBlocksPerTask;
10452
+
10453
+ await ctx.swarm.kv_set({
10454
+ namespace: NS,
10455
+ key: \`fail:\${Date.now()}:\${safeKey(taskId)}\`,
10456
+ value: { ...verdict, action: willBlock ? "blocked" : "recorded" },
10457
+ });
10458
+
10459
+ if (!willBlock) {
10460
+ await ctx.swarm.kv_incr({
10461
+ namespace: NS,
10462
+ key: cfg.mode === "enforce" ? "escaped" : "shadowed",
10463
+ });
10464
+ return;
10465
+ }
10466
+
10467
+ await ctx.swarm.kv_incr({ namespace: NS, key: "blocked" });
10468
+ await ctx.swarm.kv_incr({ namespace: NS, key: blocksKey });
10469
+
10470
+ const detail = failed
10471
+ .map(
10472
+ (d) =>
10473
+ \`\${d} (\${scores[d]!.toFixed(2)} vs \${cfg.threshold}) — requested: \${String(prefs[d] ?? "").slice(0, 220)}\`,
10474
+ )
10475
+ .join("\\n");
10476
+
10477
+ return block(
10478
+ [
10479
+ \`Comms gate: this output does not match \${requesterName}'s stated preferences.\`,
10480
+ detail,
10481
+ "Rewrite the output to match and call store-progress again. Do not change the facts, only how they are said.",
10482
+ ].join("\\n"),
10483
+ );
10484
+ } catch (err) {
10485
+ // Fail open, always.
10486
+ try {
10487
+ await ctx.swarm.kv_incr({ namespace: NS, key: "error" });
10488
+ await ctx.swarm.kv_set({
10489
+ namespace: NS,
10490
+ key: "error:last",
10491
+ value: { at: new Date().toISOString(), taskId, error: String(err).slice(0, 400) },
10492
+ });
10493
+ } catch {
10494
+ /* ignore */
10495
+ }
10496
+ ctx.log.warn("comms-adherence-gate: failing open", { error: String(err) });
10497
+ return;
10498
+ }
10499
+ });
10500
+ };
10501
+
10502
+ export default extension;
10503
+ `
10504
+ },
10505
+ readme: '# comms-adherence-gate\n\nAgents finish Slack tasks with a final message a human reads. This extension judges that message against the requester\'s stated comms preferences and their latest request with Jev, [TypeSafe](https://typesafe.ai)\'s decision model. In `enforce` it blocks a non-adherent completion once and tells the agent what to fix.\n\nInstall config:\n\n```json\n{ "mode": "shadow" }\n```\n\nStart in `shadow`, read the verdicts in KV for a few days, then switch to `enforce`.\n\n## Modes\n\n| `mode` | Effect |\n|---|---|\n| `off` | Never evaluates. |\n| `shadow` (default) | Asks Jev and records the verdict. Never blocks. |\n| `enforce` | Asks Jev, records the verdict, and blocks the `store-progress` call when a dimension fails, at most `maxBlocksPerTask` times per task. The agent gets the failed dimensions, their scores and the stated preference, and calls again. |\n\n## What it gates\n\nHook: `pre.tool.call` on `store-progress` with `status: "completed"` and a non-empty `output`. Calls made by extensions are skipped. The task must:\n\n1. Come from Slack (`source = "slack"` or a `slackThreadTs`). Other tasks pass without a Jev call.\n2. Have a requester with stated preferences in `users.metadata.comms`. A requester with none passes. Set them with the `manage-user` tool.\n\n## Decision\n\nThe gate reads the task row and asks one `noul` question per stated preference it knows: `verbosity`, `tone`, `language`. Any other key in `comms` is context only.\n\nJev sees the preferences, the requester\'s latest message (the text after the last `</thread_context>`, from the last 1,400 characters of the task), the first 400 characters of the task, and the output. Its judging rules say an explicit request outranks the stored default. A requested long answer, story or document is not a verbosity failure.\n\nA dimension fails when its score is below `threshold`. Any failed dimension fails the output.\n\n## Jev transport\n\nJev is reachable two ways. Both take the same request and return the same answer.\n\n| `provider` | Endpoint | Key | Model |\n|---|---|---|---|\n| `typesafe` | `POST https://api.typesafe.ai/v1/systemone` | `TYPESAFE_API_KEY` (or `apiKey`) | `model`, default `jev-latest` |\n| `openrouter` | `POST https://openrouter.ai/api/alpha/decisions` | `OPENROUTER_API_KEY` | `openrouterModel`, default `typesafe/jev-1.13` |\n| `auto` (default) | TypeSafe when its key is set, else OpenRouter | | |\n\nEach verdict row records the `provider` that answered.\n\n## Fail open\n\nEvery error lets the completion through: a missing key, an HTTP error, a network error, a timeout, a malformed answer, or a failed DB read. The handler never throws, so an outage cannot auto-disable the extension. Each failure bumps the `error` counter and overwrites `error:last`.\n\n## Self-test\n\nCall `store-progress` with a non-completed status and `progress` starting with `comms-gate-selftest`. The gate judges a known-bad and a known-good message against a terse preference and writes `selftest:last`: both scores, the threshold, the provider, and `controlsOk` (bad below threshold and good at or above it). Real traffic is untouched.\n\n## Config\n\n| Key | Default | Notes |\n|---|---|---|\n| `mode` | `shadow` | `off`, `shadow`, or `enforce` |\n| `threshold` | `0.5` | A dimension scoring below this fails |\n| `timeoutMs` | `4000` | Jev call timeout, capped at 4000 (the dispatcher cancels a handler at 5 s) |\n| `maxBlocksPerTask` | `1` | Blocks per task in `enforce`; after that the gate records and lets it through |\n| `provider` | `auto` | `auto`, `typesafe`, or `openrouter`. See [Jev transport](#jev-transport) |\n| `model` | `jev-latest` | Jev model on TypeSafe |\n| `openrouterModel` | `typesafe/jev-1.13` | Jev model on OpenRouter |\n| `apiKey` | unset | Explicit TypeSafe key. Prefer the `TYPESAFE_API_KEY` secret below |\n\n## Required secrets\n\nRead from the API process environment at call time. Set them as global `swarm_config` secrets: the API process loads global rows into `process.env` at boot and on config reload.\n\n- `TYPESAFE_API_KEY` or `OPENROUTER_API_KEY`: one is required for any evaluation. Under `auto`, TypeSafe wins when both are set. Without either, every completion fails open.\n\nNo key is written to config, KV, logs, or a block reason.\n\n## KV namespace `ext:comms-gate`\n\n- `last`: the latest verdict, with `scores`, `failed`, `provider`, `latencyMs`, the preferences, a 400-character `askPreview` and a 700-character `outputPreview`.\n- `fail:<epochMs>:<taskId>`: one row per failed verdict, with `action` (`blocked` or `recorded`).\n- `blocks:<taskId>`: blocks issued for that task.\n- `error:last`, `selftest:last`.\n- Counters: `total`, `pass`, `fail`, `blocked`, `escaped` (enforce, block budget spent), `shadowed`, `error`, and `dim:<dimension>`.\n\n## Known limits\n\n- Only the last 1,400 characters of the task are searched for the latest request. A very long final message is cut to its last 900 characters.\n- The block budget is per task, so a retried completion after a block goes through once the budget is spent, even if it still fails.\n'
10506
+ },
9902
10507
  "deploy-awareness": {
9903
10508
  manifestFile: "manifest.json",
9904
10509
  manifest: {
@@ -12808,6 +13413,203 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
12808
13413
 
12809
13414
  // src/http/integrations.ts
12810
13415
  init_zod();
13416
+
13417
+ // src/integrations/chat-sdk/status.ts
13418
+ init_zod();
13419
+ init_constants();
13420
+ init_env_flag();
13421
+ init_secret_scrubber();
13422
+
13423
+ // src/integrations/chat-sdk/discord-api.ts
13424
+ init_secret_scrubber();
13425
+ var DISCORD_TIMEOUT_MS = 5000;
13426
+ var GUILDS_CACHE_MS = 60000;
13427
+ var MAX_GUILD_NAMES = 10;
13428
+ var cache;
13429
+ async function fetchDiscordGuilds(token, now = Date.now()) {
13430
+ if (cache && cache.token === token && now - cache.at < GUILDS_CACHE_MS)
13431
+ return cache.guilds;
13432
+ let guilds;
13433
+ try {
13434
+ const response = await fetch(`${resolveAdapterApiUrl("discord")}/users/@me/guilds`, {
13435
+ headers: { authorization: `Bot ${token}` },
13436
+ signal: AbortSignal.timeout(DISCORD_TIMEOUT_MS)
13437
+ });
13438
+ if (!response.ok)
13439
+ throw new Error(`HTTP ${response.status}`);
13440
+ const payload = await response.json();
13441
+ if (!Array.isArray(payload))
13442
+ throw new Error("unexpected response shape");
13443
+ const names = payload.map((guild) => guild !== null && typeof guild === "object" && typeof guild.name === "string" ? guild.name : undefined).filter((name) => name !== undefined);
13444
+ guilds = { count: payload.length, names: names.slice(0, MAX_GUILD_NAMES) };
13445
+ } catch (error) {
13446
+ console.warn(scrubSecrets(`[chat-sdk] discord: could not list the bot's servers: ${errorMessage(error).replaceAll(token, "[REDACTED]")}`));
13447
+ }
13448
+ cache = { token, at: now, guilds };
13449
+ return guilds;
13450
+ }
13451
+
13452
+ // src/integrations/chat-sdk/status.ts
13453
+ init_schema();
13454
+ var ChatSdkStatusSchema = exports_external.object({
13455
+ enabled: exports_external.boolean(),
13456
+ slackClash: exports_external.boolean(),
13457
+ error: exports_external.string().optional(),
13458
+ adapters: exports_external.array(exports_external.object({
13459
+ name: exports_external.enum(CHAT_SDK_ADAPTER_NAMES),
13460
+ configured: exports_external.boolean(),
13461
+ enabled: exports_external.boolean(),
13462
+ health: exports_external.enum(["ok", "error", "disabled", "incomplete"]),
13463
+ error: exports_external.string().optional(),
13464
+ webhookUrl: exports_external.string(),
13465
+ mode: exports_external.string().optional(),
13466
+ gateway: exports_external.object({
13467
+ state: exports_external.enum(["running", "backoff", "stopped"]),
13468
+ since: exports_external.string(),
13469
+ restarts: exports_external.number()
13470
+ }).optional(),
13471
+ guilds: exports_external.object({ count: exports_external.number(), names: exports_external.array(exports_external.string()) }).optional()
13472
+ }))
13473
+ });
13474
+ var DEFAULT_MODES = {
13475
+ telegram: "webhook",
13476
+ slack: "webhook"
13477
+ };
13478
+ var NOT_RUNNING = "The adapter is starting, or the last configuration save is still applying. Refresh in a few seconds.";
13479
+ var SLACK_CLASH_CAUSE = "the Slack adapter conflicts with the built-in Slack integration (set SLACK_DISABLE=true)";
13480
+ var MAX_STATUS_ERROR_CHARS = 300;
13481
+ function statusError(message) {
13482
+ return scrubSecrets(message).slice(0, MAX_STATUS_ERROR_CHARS);
13483
+ }
13484
+ function readRawAdapters() {
13485
+ const raw = process.env.CHAT_SDK_ADAPTERS?.trim();
13486
+ if (!raw)
13487
+ return {};
13488
+ try {
13489
+ const parsed = JSON.parse(raw);
13490
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
13491
+ return {};
13492
+ const out = {};
13493
+ for (const name of CHAT_SDK_ADAPTER_NAMES) {
13494
+ const value = parsed[name];
13495
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
13496
+ out[name] = value;
13497
+ }
13498
+ }
13499
+ return out;
13500
+ } catch {
13501
+ return {};
13502
+ }
13503
+ }
13504
+ function chatSdkWebhookUrl(name) {
13505
+ return `${getPublicMcpBaseUrl()}/api/integrations/chat/${name}/webhook`;
13506
+ }
13507
+ async function discordGuildsFor(config, health) {
13508
+ if (health?.status !== "ok" || health.gateway?.state !== "running")
13509
+ return;
13510
+ const raw = config.adapters.discord?.botToken;
13511
+ const token = typeof raw === "function" ? await raw() : raw;
13512
+ if (typeof token !== "string" || token.length === 0)
13513
+ return;
13514
+ return fetchDiscordGuilds(token);
13515
+ }
13516
+ async function getChatSdkStatus() {
13517
+ const config = await getChatSdkConfig();
13518
+ const raw = readRawAdapters();
13519
+ const runtimeHealth = getChatSdkHealth();
13520
+ const slackClash = raw.slack !== undefined && !isEnvFlagEnabled("SLACK_DISABLE", false);
13521
+ const blockedCause = config.error ?? (config.enabled && slackClash ? SLACK_CLASH_CAUSE : undefined);
13522
+ const discordGuilds = await discordGuildsFor(config, runtimeHealth.discord);
13523
+ const adapters = CHAT_SDK_ADAPTER_NAMES.map((name) => {
13524
+ const configured = raw[name] !== undefined;
13525
+ const enabled = isAdapterEntryEnabled(raw[name]);
13526
+ const current = runtimeHealth[name];
13527
+ const incomplete = config.incomplete?.[name];
13528
+ const rawMode = raw[name]?.mode;
13529
+ const mode = typeof rawMode === "string" ? rawMode : configured ? DEFAULT_MODES[name] : undefined;
13530
+ let health = "disabled";
13531
+ let error;
13532
+ if (current) {
13533
+ health = current.status;
13534
+ const showWarning = current.status === "error" || current.gateway?.state === "backoff";
13535
+ error = current.error ?? (showWarning ? current.lastWarning : undefined);
13536
+ } else if (incomplete) {
13537
+ health = "incomplete";
13538
+ error = incomplete;
13539
+ } else if (configured && !enabled) {
13540
+ error = "Turned off on the card. The configuration is kept.";
13541
+ } else if (config.enabled && configured) {
13542
+ error = blockedCause ? `Not started: ${blockedCause}` : NOT_RUNNING;
13543
+ }
13544
+ return {
13545
+ name,
13546
+ configured,
13547
+ enabled,
13548
+ health,
13549
+ ...error ? { error: statusError(error) } : {},
13550
+ webhookUrl: chatSdkWebhookUrl(name),
13551
+ ...mode ? { mode } : {},
13552
+ ...current?.gateway ? { gateway: current.gateway } : {},
13553
+ ...name === "discord" && discordGuilds ? { guilds: discordGuilds } : {}
13554
+ };
13555
+ });
13556
+ return {
13557
+ enabled: config.enabled,
13558
+ slackClash,
13559
+ ...config.error ? { error: statusError(config.error) } : {},
13560
+ adapters
13561
+ };
13562
+ }
13563
+
13564
+ class ChatSdkActionError extends Error {
13565
+ status;
13566
+ constructor(message, status) {
13567
+ super(message);
13568
+ this.status = status;
13569
+ this.name = "ChatSdkActionError";
13570
+ }
13571
+ }
13572
+ async function callBotApi(baseUrl, token, method, body) {
13573
+ try {
13574
+ return await callTelegramBotApi(baseUrl, token, method, body);
13575
+ } catch (error) {
13576
+ if (error instanceof TelegramBotApiError)
13577
+ throw new ChatSdkActionError(error.message, 502);
13578
+ throw error;
13579
+ }
13580
+ }
13581
+ async function registerTelegramWebhook() {
13582
+ const config = await getChatSdkConfig();
13583
+ if (!config.enabled) {
13584
+ throw new ChatSdkActionError("The Chat SDK channel is disabled (CHAT_SDK_ENABLED is not true). Enable it and restart the API first", 400);
13585
+ }
13586
+ if (config.error)
13587
+ throw new ChatSdkActionError(statusError(config.error), 400);
13588
+ const telegram = config.adapters.telegram;
13589
+ if (!telegram) {
13590
+ throw new ChatSdkActionError("Telegram is not configured in CHAT_SDK_ADAPTERS", 400);
13591
+ }
13592
+ if (telegram.mode !== "webhook") {
13593
+ throw new ChatSdkActionError("Telegram runs in polling mode; a registered webhook would stop polling", 400);
13594
+ }
13595
+ const secretToken = telegram.secretToken;
13596
+ if (typeof secretToken !== "string" || secretToken.length === 0) {
13597
+ throw new ChatSdkActionError("Telegram webhook mode needs a secretToken", 400);
13598
+ }
13599
+ const rawToken = telegram.botToken;
13600
+ const token = typeof rawToken === "function" ? await rawToken() : rawToken;
13601
+ if (typeof token !== "string" || token.length === 0) {
13602
+ throw new ChatSdkActionError("Telegram botToken is empty", 400);
13603
+ }
13604
+ const baseUrl = resolveAdapterApiUrl("telegram");
13605
+ const webhookUrl = chatSdkWebhookUrl("telegram");
13606
+ await callBotApi(baseUrl, token, "setWebhook", { url: webhookUrl, secret_token: secretToken });
13607
+ const info = await callBotApi(baseUrl, token, "getWebhookInfo", {});
13608
+ if (info === null || typeof info !== "object" || typeof info.url !== "string") {
13609
+ throw new ChatSdkActionError("Telegram getWebhookInfo returned an unexpected result", 502);
13610
+ }
13611
+ return { webhookUrl, webhookInfo: info };
13612
+ }
12811
13613
  // slack-manifest.json
12812
13614
  var slack_manifest_default = {
12813
13615
  display_information: {
@@ -12983,6 +13785,19 @@ var McpUserConfigSchema = exports_external.object({
12983
13785
  mcpUserUrl: exports_external.string()
12984
13786
  });
12985
13787
  var SlackManifestSchema = exports_external.record(exports_external.string(), exports_external.unknown());
13788
+ var TelegramRegisterWebhookSchema = exports_external.object({
13789
+ webhookUrl: exports_external.string(),
13790
+ webhookInfo: exports_external.looseObject({
13791
+ url: exports_external.string(),
13792
+ has_custom_certificate: exports_external.boolean().optional(),
13793
+ pending_update_count: exports_external.number().optional(),
13794
+ last_error_date: exports_external.number().optional(),
13795
+ last_error_message: exports_external.string().optional(),
13796
+ max_connections: exports_external.number().optional(),
13797
+ ip_address: exports_external.string().optional(),
13798
+ allowed_updates: exports_external.array(exports_external.string()).optional()
13799
+ })
13800
+ });
12986
13801
  var claudeManagedTestRoute = route({
12987
13802
  method: "post",
12988
13803
  path: "/api/integrations/claude-managed/test",
@@ -13021,6 +13836,34 @@ var slackManifestRoute = route({
13021
13836
  200: { description: "Slack app manifest", schema: SlackManifestSchema }
13022
13837
  }
13023
13838
  });
13839
+ var chatSdkStatusRoute = route({
13840
+ method: "get",
13841
+ path: "/api/integrations/chat/status",
13842
+ pattern: ["api", "integrations", "chat", "status"],
13843
+ summary: "Chat SDK channel status: enabled flag, Slack clash, per-adapter health and webhook URL.",
13844
+ tags: ["Integrations"],
13845
+ responses: {
13846
+ 200: {
13847
+ description: "Per-adapter status. `health` is `disabled` for an adapter that is not running. Never contains secret values.",
13848
+ schema: ChatSdkStatusSchema
13849
+ }
13850
+ }
13851
+ });
13852
+ var chatSdkTelegramRegisterWebhookRoute = route({
13853
+ method: "post",
13854
+ path: "/api/integrations/chat/telegram/register-webhook",
13855
+ pattern: ["api", "integrations", "chat", "telegram", "register-webhook"],
13856
+ summary: "Register this swarm's public Telegram webhook URL and secret token with the Bot API (setWebhook), then return getWebhookInfo.",
13857
+ tags: ["Integrations"],
13858
+ body: exports_external.object({}).optional(),
13859
+ responses: {
13860
+ 200: { description: "Webhook registered", schema: TelegramRegisterWebhookSchema },
13861
+ 400: { description: "Telegram is not configured for webhook mode" },
13862
+ 403: { description: "Only the lead agent or a dashboard user can register the webhook" },
13863
+ 502: { description: "The Telegram Bot API rejected the call" }
13864
+ },
13865
+ rbac: { permission: "integration.chat.manage" }
13866
+ });
13024
13867
  function createIntegrationsHandler(deps = {}) {
13025
13868
  const buildClient = deps.buildClient ?? ((apiKey) => new Anthropic({ apiKey }));
13026
13869
  return async function handleIntegrations(req, res, pathSegments, queryParams = new URLSearchParams) {
@@ -13031,6 +13874,26 @@ function createIntegrationsHandler(deps = {}) {
13031
13874
  slackManifestRoute.respond(res, 200, buildSlackManifest(parsed.query.name));
13032
13875
  return true;
13033
13876
  }
13877
+ if (chatSdkStatusRoute.match(req.method, pathSegments)) {
13878
+ chatSdkStatusRoute.respond(res, 200, await getChatSdkStatus());
13879
+ return true;
13880
+ }
13881
+ if (chatSdkTelegramRegisterWebhookRoute.match(req.method, pathSegments)) {
13882
+ const parsed = await chatSdkTelegramRegisterWebhookRoute.parse(req, res, pathSegments, queryParams);
13883
+ if (!parsed)
13884
+ return true;
13885
+ if (!await ensureHttpPermission(req, res, "integration.chat.manage", "Registering the Telegram webhook requires the lead agent")) {
13886
+ return true;
13887
+ }
13888
+ try {
13889
+ chatSdkTelegramRegisterWebhookRoute.respond(res, 200, await registerTelegramWebhook());
13890
+ } catch (error) {
13891
+ if (!(error instanceof ChatSdkActionError))
13892
+ throw error;
13893
+ jsonError(res, error.message, error.status);
13894
+ }
13895
+ return true;
13896
+ }
13034
13897
  if (mcpUserConfigRoute.match(req.method, pathSegments)) {
13035
13898
  const mcpBaseUrl = await resolveMcpBaseUrl();
13036
13899
  mcpUserConfigRoute.respond(res, 200, { mcpBaseUrl, mcpUserUrl: `${mcpBaseUrl}/mcp-user` });
@@ -13662,7 +14525,7 @@ var toRequestError = (e) => {
13662
14525
  return new RequestError(e.message, { cause: e });
13663
14526
  };
13664
14527
  var GlobalRequest = global.Request;
13665
- var Request = class extends GlobalRequest {
14528
+ var Request2 = class extends GlobalRequest {
13666
14529
  constructor(input, options) {
13667
14530
  if (typeof input === "object" && getRequestCache in input) {
13668
14531
  input = input[getRequestCache]();
@@ -13693,7 +14556,7 @@ var newRequestFromIncoming = (method, url, headers2, incoming, abortController)
13693
14556
  };
13694
14557
  if (method === "TRACE") {
13695
14558
  init.method = "GET";
13696
- const req = new Request(url, init);
14559
+ const req = new Request2(url, init);
13697
14560
  Object.defineProperty(req, "method", {
13698
14561
  get() {
13699
14562
  return "TRACE";
@@ -13730,7 +14593,7 @@ var newRequestFromIncoming = (method, url, headers2, incoming, abortController)
13730
14593
  init.body = Readable.toWeb(incoming);
13731
14594
  }
13732
14595
  }
13733
- return new Request(url, init);
14596
+ return new Request2(url, init);
13734
14597
  };
13735
14598
  var getRequestCache = Symbol("getRequestCache");
13736
14599
  var requestCache = Symbol("requestCache");
@@ -13796,7 +14659,7 @@ Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom")
13796
14659
  return `Request (lightweight) ${inspectFn(props, { ...options, depth: depth == null ? null : depth - 1 })}`;
13797
14660
  }
13798
14661
  });
13799
- Object.setPrototypeOf(requestPrototype, Request.prototype);
14662
+ Object.setPrototypeOf(requestPrototype, Request2.prototype);
13800
14663
  var newRequest = (incoming, defaultHostname) => {
13801
14664
  const req = Object.create(requestPrototype);
13802
14665
  req[incomingKey] = incoming;
@@ -13865,12 +14728,12 @@ var Response2 = class _Response {
13865
14728
  }
13866
14729
  }
13867
14730
  get headers() {
13868
- const cache = this[cacheKey];
13869
- if (cache) {
13870
- if (!(cache[2] instanceof Headers)) {
13871
- cache[2] = new Headers(cache[2] || { "content-type": "text/plain; charset=UTF-8" });
14731
+ const cache2 = this[cacheKey];
14732
+ if (cache2) {
14733
+ if (!(cache2[2] instanceof Headers)) {
14734
+ cache2[2] = new Headers(cache2[2] || { "content-type": "text/plain; charset=UTF-8" });
13872
14735
  }
13873
- return cache[2];
14736
+ return cache2[2];
13874
14737
  }
13875
14738
  return this[getResponseCache]().headers;
13876
14739
  }
@@ -14158,9 +15021,9 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
14158
15021
  };
14159
15022
  var getRequestListener = (fetchCallback, options = {}) => {
14160
15023
  const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
14161
- if (options.overrideGlobalObjects !== false && global.Request !== Request) {
15024
+ if (options.overrideGlobalObjects !== false && global.Request !== Request2) {
14162
15025
  Object.defineProperty(global, "Request", {
14163
- value: Request
15026
+ value: Request2
14164
15027
  });
14165
15028
  Object.defineProperty(global, "Response", {
14166
15029
  value: Response2
@@ -15200,13 +16063,14 @@ async function handleMcp(req, res, transports, sessionActivity = {}, sessionAgen
15200
16063
  const configValue = configs.find((config) => config.key === "SCRIPTS_ONLY_MCP")?.value;
15201
16064
  const preloadEnabled = parseEnvFlag(configs.find((config) => config.key === "TASK_TOOL_PRELOAD_ENABLED")?.value ?? process.env.TASK_TOOL_PRELOAD_ENABLED, true);
15202
16065
  let preloadedTools = [];
16066
+ const sourceTaskId = headerValue(req.headers["x-source-task-id"]);
16067
+ const sourceTask = sourceTaskId ? await getTaskById(sourceTaskId) : undefined;
16068
+ const excludedTools = sourceTask && !isChatSdkTask(sourceTask) ? [...CHAT_TASK_ONLY_TOOLS] : [];
15203
16069
  if (preloadEnabled) {
15204
- const taskId = headerValue(req.headers["x-source-task-id"]);
15205
- const task = taskId ? await getTaskById(taskId) : undefined;
15206
- if (task?.agentId === agentId) {
16070
+ if (sourceTask?.agentId === agentId) {
15207
16071
  const manifestValue = configs.find((config) => config.key === "TASK_TOOL_MANIFESTS")?.value ?? process.env.TASK_TOOL_MANIFESTS ?? "{}";
15208
16072
  try {
15209
- preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue), task);
16073
+ preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue), sourceTask);
15210
16074
  } catch {
15211
16075
  console.warn("[MCP] Invalid TASK_TOOL_MANIFESTS; using ordinary tool discovery");
15212
16076
  }
@@ -15214,6 +16078,7 @@ async function handleMcp(req, res, transports, sessionActivity = {}, sessionAgen
15214
16078
  }
15215
16079
  const server = await createServer({
15216
16080
  preloadedTools,
16081
+ excludedTools,
15217
16082
  enabledTools: configs.find((config) => config.key === "SWARM_ENABLED_TOOLS")?.value ?? process.env.SWARM_ENABLED_TOOLS,
15218
16083
  scriptsOnly: resolveScriptsOnlyMode({
15219
16084
  env: process.env.SCRIPTS_ONLY_MCP,
@@ -18615,6 +19480,237 @@ async function handleMetrics(req, res, pathSegments, queryParams, myAgentId) {
18615
19480
  return false;
18616
19481
  }
18617
19482
 
19483
+ // src/http/model-profiles.ts
19484
+ init_src();
19485
+ init_zod();
19486
+ init_model_profiles();
19487
+ init_rbac();
19488
+ init_types();
19489
+ var listProfiles = route({
19490
+ method: "get",
19491
+ path: "/api/model-profiles",
19492
+ pattern: ["api", "model-profiles"],
19493
+ summary: "List model profiles",
19494
+ description: "Every model profile, ordered by harness then name. Filter by `harness` to see what an agent on that harness can run.",
19495
+ tags: ["Models"],
19496
+ query: exports_external.object({ harness: ProviderNameSchema.optional() }),
19497
+ responses: {
19498
+ 200: {
19499
+ description: "Model profiles",
19500
+ schema: exports_external.object({ profiles: exports_external.array(ModelProfileSchema) })
19501
+ }
19502
+ }
19503
+ });
19504
+ var getProfile = route({
19505
+ method: "get",
19506
+ path: "/api/model-profiles/{name}",
19507
+ pattern: ["api", "model-profiles", null],
19508
+ summary: "Get one model profile",
19509
+ tags: ["Models"],
19510
+ params: exports_external.object({ name: exports_external.string() }),
19511
+ responses: {
19512
+ 200: { description: "The model profile", schema: ModelProfileSchema },
19513
+ 404: { description: "No profile with that name" }
19514
+ }
19515
+ });
19516
+ var createProfile = route({
19517
+ method: "post",
19518
+ path: "/api/model-profiles",
19519
+ pattern: ["api", "model-profiles"],
19520
+ summary: "Create a model profile",
19521
+ description: "A profile runs on one harness. `model` must run on that harness and be in the model catalog unless `allowCustomModel` is true (a gateway id, a fresh launch); a `latest:` alias is accepted and resolves at claim. `effort` must be a level the harness and model support. Operator only (the shared API key).",
19522
+ tags: ["Models"],
19523
+ rbac: { permission: "models.profiles.write" },
19524
+ body: exports_external.object({
19525
+ name: ModelProfileNameSchema,
19526
+ harness: ProviderNameSchema,
19527
+ model: exports_external.string().trim().min(1),
19528
+ effort: ReasoningEffortSchema.nullable().optional(),
19529
+ allowCustomModel: exports_external.boolean().optional().default(false),
19530
+ description: exports_external.string().trim().max(500).nullable().optional()
19531
+ }),
19532
+ responses: {
19533
+ 201: { description: "Profile created", schema: ModelProfileSchema },
19534
+ 400: { description: "Validation error" },
19535
+ 403: { description: "Forbidden" },
19536
+ 409: { description: "A profile with that name exists" }
19537
+ }
19538
+ });
19539
+ var updateProfile = route({
19540
+ method: "patch",
19541
+ path: "/api/model-profiles/{name}",
19542
+ pattern: ["api", "model-profiles", null],
19543
+ summary: "Update a model profile",
19544
+ description: "Changes the model, effort, custom-model flag or description; the name and harness are fixed. Omit a field to keep it; `effort: null` clears it. Applies to tasks claimed after the change. Operator only (the shared API key).",
19545
+ tags: ["Models"],
19546
+ rbac: { permission: "models.profiles.write" },
19547
+ params: exports_external.object({ name: exports_external.string() }),
19548
+ body: exports_external.object({
19549
+ model: exports_external.string().trim().min(1).optional(),
19550
+ effort: ReasoningEffortSchema.nullable().optional(),
19551
+ allowCustomModel: exports_external.boolean().optional(),
19552
+ description: exports_external.string().trim().max(500).nullable().optional()
19553
+ }),
19554
+ responses: {
19555
+ 200: { description: "Profile updated", schema: ModelProfileSchema },
19556
+ 400: { description: "Validation error" },
19557
+ 403: { description: "Forbidden" },
19558
+ 404: { description: "No profile with that name" }
19559
+ }
19560
+ });
19561
+ var deleteProfile = route({
19562
+ method: "delete",
19563
+ path: "/api/model-profiles/{name}",
19564
+ pattern: ["api", "model-profiles", null],
19565
+ summary: "Delete a model profile",
19566
+ description: "Refused (409) for a shipped `<harness>-<tier>` seed (edit it instead; it would come back at the next boot), and while an agent has the profile as its default or a task holds it (unfinished, or superseded and still owed its resume). Operator only (the shared API key).",
19567
+ tags: ["Models"],
19568
+ rbac: { permission: "models.profiles.write" },
19569
+ params: exports_external.object({ name: exports_external.string() }),
19570
+ responses: {
19571
+ 200: { description: "Profile deleted", schema: exports_external.object({ deleted: exports_external.literal(true) }) },
19572
+ 403: { description: "Forbidden" },
19573
+ 404: { description: "No profile with that name" },
19574
+ 409: { description: "The profile is a seed or still in use" }
19575
+ }
19576
+ });
19577
+ async function profileValueError(input) {
19578
+ const modelError = await explicitModelError({
19579
+ model: input.model,
19580
+ allowCustomModel: input.allowCustomModel,
19581
+ harnessProvider: input.harness
19582
+ });
19583
+ if (modelError)
19584
+ return modelError;
19585
+ if (input.effort && !isAlias(input.model) && !supportedReasoningLevels(input.harness, input.model).includes(input.effort)) {
19586
+ return `Effort "${input.effort}" is not supported by ${input.model} on ${input.harness}.`;
19587
+ }
19588
+ return null;
19589
+ }
19590
+ async function canWriteProfiles(req, res, myAgentId) {
19591
+ const decision = can({
19592
+ principal: await agentFirstPrincipal(req, myAgentId),
19593
+ verb: "models.profiles.write",
19594
+ resource: { kind: "none" },
19595
+ source: "http"
19596
+ });
19597
+ if (!decision.allow) {
19598
+ jsonError(res, "Writing model profiles requires the operator", 403);
19599
+ return false;
19600
+ }
19601
+ return true;
19602
+ }
19603
+ async function handleModelProfiles(req, res, pathSegments, queryParams, myAgentId) {
19604
+ if (listProfiles.match(req.method, pathSegments)) {
19605
+ const parsed = await listProfiles.parse(req, res, pathSegments, queryParams);
19606
+ if (!parsed)
19607
+ return true;
19608
+ listProfiles.respond(res, 200, {
19609
+ profiles: await listModelProfiles({ harness: parsed.query.harness })
19610
+ });
19611
+ return true;
19612
+ }
19613
+ if (getProfile.match(req.method, pathSegments)) {
19614
+ const parsed = await getProfile.parse(req, res, pathSegments, queryParams);
19615
+ if (!parsed)
19616
+ return true;
19617
+ const profile = await getModelProfileByName(parsed.params.name);
19618
+ if (!profile) {
19619
+ jsonError(res, "Model profile not found", 404);
19620
+ return true;
19621
+ }
19622
+ getProfile.respond(res, 200, profile);
19623
+ return true;
19624
+ }
19625
+ if (createProfile.match(req.method, pathSegments)) {
19626
+ const parsed = await createProfile.parse(req, res, pathSegments, queryParams);
19627
+ if (!parsed)
19628
+ return true;
19629
+ if (!await canWriteProfiles(req, res, myAgentId))
19630
+ return true;
19631
+ const body = parsed.body;
19632
+ const error = await profileValueError({ ...body, effort: body.effort ?? null });
19633
+ if (error) {
19634
+ jsonError(res, error, 400);
19635
+ return true;
19636
+ }
19637
+ const created = await createModelProfile(body, {
19638
+ userId: await resolveHttpAuditUserId(req, myAgentId)
19639
+ });
19640
+ if (!created) {
19641
+ jsonError(res, `Model profile "${body.name}" already exists`, 409);
19642
+ return true;
19643
+ }
19644
+ createProfile.respond(res, 201, created);
19645
+ return true;
19646
+ }
19647
+ if (updateProfile.match(req.method, pathSegments)) {
19648
+ const parsed = await updateProfile.parse(req, res, pathSegments, queryParams);
19649
+ if (!parsed)
19650
+ return true;
19651
+ if (!await canWriteProfiles(req, res, myAgentId))
19652
+ return true;
19653
+ const current = await getModelProfileByName(parsed.params.name);
19654
+ if (!current) {
19655
+ jsonError(res, "Model profile not found", 404);
19656
+ return true;
19657
+ }
19658
+ const body = parsed.body;
19659
+ const error = await profileValueError({
19660
+ harness: current.harness,
19661
+ model: body.model ?? current.model,
19662
+ effort: body.effort === undefined ? current.effort : body.effort,
19663
+ allowCustomModel: body.allowCustomModel ?? current.allowCustomModel
19664
+ });
19665
+ if (error) {
19666
+ jsonError(res, error, 400);
19667
+ return true;
19668
+ }
19669
+ const updated = await updateModelProfile(current.name, body, {
19670
+ userId: await resolveHttpAuditUserId(req, myAgentId)
19671
+ });
19672
+ if (!updated) {
19673
+ jsonError(res, "Model profile not found", 404);
19674
+ return true;
19675
+ }
19676
+ updateProfile.respond(res, 200, updated);
19677
+ return true;
19678
+ }
19679
+ if (deleteProfile.match(req.method, pathSegments)) {
19680
+ const parsed = await deleteProfile.parse(req, res, pathSegments, queryParams);
19681
+ if (!parsed)
19682
+ return true;
19683
+ if (!await canWriteProfiles(req, res, myAgentId))
19684
+ return true;
19685
+ const current = await getModelProfileByName(parsed.params.name);
19686
+ if (!current) {
19687
+ jsonError(res, "Model profile not found", 404);
19688
+ return true;
19689
+ }
19690
+ if (current.seededFrom?.startsWith("tier:")) {
19691
+ jsonError(res, `"${current.name}" is a shipped tier profile and comes back at the next boot; edit it instead.`, 409);
19692
+ return true;
19693
+ }
19694
+ const refs = await modelProfileReferences(current.name);
19695
+ if (refs.agentIds.length > 0 || refs.openTaskCount > 0) {
19696
+ jsonError(res, `"${current.name}" is in use: default of ${refs.agentIds.length} agent(s), held by ${refs.openTaskCount} unfinished or awaiting-resume task(s).`, 409);
19697
+ return true;
19698
+ }
19699
+ const outcome = await deleteModelProfile(current.name);
19700
+ if (outcome === "in-use") {
19701
+ jsonError(res, `"${current.name}" is in use by a task or an agent default created just now.`, 409);
19702
+ return true;
19703
+ }
19704
+ if (outcome === "missing") {
19705
+ jsonError(res, "Model profile not found", 404);
19706
+ return true;
19707
+ }
19708
+ deleteProfile.respond(res, 200, { deleted: true });
19709
+ return true;
19710
+ }
19711
+ return false;
19712
+ }
19713
+
18618
19714
  // src/http/models-catalog.ts
18619
19715
  init_zod();
18620
19716
  init_db3();
@@ -20501,12 +21597,51 @@ init_request_auth_context();
20501
21597
  // src/http/tasks.ts
20502
21598
  init_zod();
20503
21599
  init_db3();
21600
+ init_model_profiles();
20504
21601
  init_steering();
20505
21602
  init_users();
20506
21603
  init_rbac();
21604
+ init_context_key();
20507
21605
  init_types();
20508
21606
  init_request_auth_context();
20509
21607
  init_secret_scrubber();
21608
+
21609
+ // src/http/requested-by.ts
21610
+ init_zod();
21611
+ init_db3();
21612
+ init_types();
21613
+ var TaskRequestedBySchema = UserSchema.pick({
21614
+ id: true,
21615
+ name: true,
21616
+ email: true,
21617
+ role: true,
21618
+ notes: true
21619
+ }).extend({
21620
+ id: exports_external.string().optional(),
21621
+ comms: UserCommsPrefsSchema.optional()
21622
+ });
21623
+ function getRequesterNotes(notes) {
21624
+ return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
21625
+ }
21626
+ async function buildTaskRequestedBy(task) {
21627
+ const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
21628
+ if (user) {
21629
+ return {
21630
+ id: user.id,
21631
+ name: user.name,
21632
+ email: user.email,
21633
+ role: user.role,
21634
+ notes: getRequesterNotes(user.notes),
21635
+ comms: getUserCommsPrefs(user)
21636
+ };
21637
+ }
21638
+ if (task.slackUserId) {
21639
+ return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
21640
+ }
21641
+ return;
21642
+ }
21643
+
21644
+ // src/http/tasks.ts
20510
21645
  var AgentTaskSummarySchema = AgentTaskSchema.pick({
20511
21646
  id: true,
20512
21647
  key: true,
@@ -20530,6 +21665,7 @@ var AgentTaskSummarySchema = AgentTaskSchema.pick({
20530
21665
  modelSource: true,
20531
21666
  modelAlias: true,
20532
21667
  effort: true,
21668
+ resolvedEffort: true,
20533
21669
  provider: true,
20534
21670
  requestedByUserId: true,
20535
21671
  progress: true,
@@ -20658,6 +21794,7 @@ var createTask = route({
20658
21794
  modelTier: ModelTierSchema.optional(),
20659
21795
  allowCustomModel: exports_external.boolean().optional(),
20660
21796
  effort: ReasoningEffortSchema.optional(),
21797
+ profile: exports_external.string().trim().min(1).optional(),
20661
21798
  draft: exports_external.boolean().optional()
20662
21799
  }).superRefine((body, ctx) => {
20663
21800
  if ((body.agentId !== undefined || body.offeredTo !== undefined) && !body.routingReason) {
@@ -20673,6 +21810,9 @@ var createTask = route({
20673
21810
  400: {
20674
21811
  description: "Validation error, an unknown `model` (set `allowCustomModel` to store a custom id), or agentId/offeredTo targets an extension identity"
20675
21812
  },
21813
+ 403: {
21814
+ description: "An agent caller names a parentTaskId it neither owns nor created, or a non-lead agent binds the task to a chat thread"
21815
+ },
20676
21816
  422: {
20677
21817
  description: "Task creation blocked by an extension",
20678
21818
  schema: exports_external.object({
@@ -20719,6 +21859,7 @@ var updateSession = route({
20719
21859
  claudeSessionId: exports_external.string().min(1),
20720
21860
  provider: ProviderNameSchema.exclude(["devin"]).optional(),
20721
21861
  model: exports_external.string().optional(),
21862
+ effort: ReasoningEffortSchema.nullable().optional(),
20722
21863
  providerMeta: exports_external.object({ transport: exports_external.enum(["cli", "sdk"]).optional() }).optional(),
20723
21864
  harnessVariant: exports_external.string().optional(),
20724
21865
  harnessVariantMeta: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
@@ -20933,7 +22074,10 @@ var listPausedTasks = route({
20933
22074
  200: {
20934
22075
  description: "Paused task list",
20935
22076
  schema: exports_external.object({
20936
- tasks: exports_external.array(AgentTaskSchema.extend({ attachments: exports_external.array(TaskAttachmentSchema) }))
22077
+ tasks: exports_external.array(AgentTaskSchema.extend({
22078
+ attachments: exports_external.array(TaskAttachmentSchema),
22079
+ requestedBy: TaskRequestedBySchema.optional()
22080
+ }))
20937
22081
  })
20938
22082
  }
20939
22083
  }
@@ -21192,7 +22336,36 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
21192
22336
  jsonError(res, modelError, 400);
21193
22337
  return true;
21194
22338
  }
22339
+ const profileError = await taskProfileError({
22340
+ profile: parsed.body.profile,
22341
+ ...splitLegacyModelAlias({ model: parsed.body.model, modelTier: parsed.body.modelTier }),
22342
+ agentId: defaultAgentId
22343
+ });
22344
+ if (profileError) {
22345
+ jsonError(res, profileError, 400);
22346
+ return true;
22347
+ }
21195
22348
  const parentTask = parsed.body.parentTaskId ? await getTaskById(parsed.body.parentTaskId) : null;
22349
+ const principal = await resolveTaskWritePrincipal(req, myAgentId);
22350
+ if (parentTask && !can({
22351
+ principal,
22352
+ verb: "task.parent.attach",
22353
+ resource: {
22354
+ kind: "task",
22355
+ taskId: parentTask.id,
22356
+ requestedByUserId: parentTask.requestedByUserId,
22357
+ creatorAgentId: parentTask.creatorAgentId,
22358
+ agentId: parentTask.agentId
22359
+ },
22360
+ source: "http"
22361
+ }).allow) {
22362
+ jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
22363
+ return true;
22364
+ }
22365
+ if (principal.kind === "agent" && parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !can({ principal, verb: "integration.chat.post", resource: { kind: "none" }, source: "http" }).allow) {
22366
+ jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
22367
+ return true;
22368
+ }
21196
22369
  let assetKey;
21197
22370
  try {
21198
22371
  const inheritedKey = parentTask?.key;
@@ -21229,7 +22402,8 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
21229
22402
  model: parsed.body.model,
21230
22403
  modelTier: parsed.body.modelTier
21231
22404
  }),
21232
- effort: parsed.body.effort
22405
+ effort: parsed.body.effort,
22406
+ profile: parsed.body.profile
21233
22407
  }, { origin: "rest", allowCustomModel: parsed.body.allowCustomModel });
21234
22408
  ensure({
21235
22409
  id: "created",
@@ -21256,6 +22430,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
21256
22430
  jsonError(res, error.message, 400);
21257
22431
  return true;
21258
22432
  }
22433
+ if (modelProfileRefError(error) === "missing") {
22434
+ jsonError(res, `Unknown model profile "${parsed.body.profile}"`, 400);
22435
+ return true;
22436
+ }
21259
22437
  console.error("[HTTP] Failed to create task:", error);
21260
22438
  jsonError(res, "Failed to create task", 500);
21261
22439
  }
@@ -21287,7 +22465,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
21287
22465
  const parsed = await updateSession.parse(req, res, pathSegments, queryParams);
21288
22466
  if (!parsed)
21289
22467
  return true;
21290
- const task = await updateTaskClaudeSessionId(parsed.params.id, parsed.body.claudeSessionId, parsed.body.provider, parsed.body.providerMeta, parsed.body.model, "harnessVariant" in parsed.body ? parsed.body.harnessVariant : undefined, "harnessVariantMeta" in parsed.body ? parsed.body.harnessVariantMeta : undefined);
22468
+ const task = await updateTaskClaudeSessionId(parsed.params.id, parsed.body.claudeSessionId, parsed.body.provider, parsed.body.providerMeta, parsed.body.model, "harnessVariant" in parsed.body ? parsed.body.harnessVariant : undefined, "harnessVariantMeta" in parsed.body ? parsed.body.harnessVariantMeta : undefined, "effort" in parsed.body ? parsed.body.effort : undefined);
21291
22469
  if (!task) {
21292
22470
  jsonError(res, "Task not found", 404);
21293
22471
  return true;
@@ -21727,10 +22905,14 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
21727
22905
  return true;
21728
22906
  }
21729
22907
  const pausedTasks = await getPausedTasksForAgent(myAgentId);
21730
- const tasks = await Promise.all(pausedTasks.map(async (task) => ({
21731
- ...task,
21732
- attachments: await getTaskAttachments(task.id)
21733
- })));
22908
+ const tasks = await Promise.all(pausedTasks.map(async (task) => {
22909
+ const requestedBy = await buildTaskRequestedBy(task);
22910
+ return {
22911
+ ...task,
22912
+ attachments: await getTaskAttachments(task.id),
22913
+ ...requestedBy && { requestedBy }
22914
+ };
22915
+ }));
21734
22916
  listPausedTasks.respond(res, 200, { tasks });
21735
22917
  return true;
21736
22918
  }
@@ -22236,10 +23418,6 @@ function withShareUrls(page) {
22236
23418
  app_url: `${getAppBaseUrl()}/pages/${page.slug}`
22237
23419
  };
22238
23420
  }
22239
- async function pageEditCounter(pageId) {
22240
- const versions = await getPageVersions(pageId);
22241
- return versions.length > 0 ? versions[0].version + 1 : 1;
22242
- }
22243
23421
  function defuseFeedbackTags(value) {
22244
23422
  return value.replace(/<(\/?page_feedback)/gi, "‹$1");
22245
23423
  }
@@ -22513,7 +23691,7 @@ async function handlePages(req, res, pathSegments, queryParams, myAgentId) {
22513
23691
  updatePageRoute.respond(res, 200, {
22514
23692
  id: updated.id,
22515
23693
  key: updated.key,
22516
- version: await pageEditCounter(updated.id)
23694
+ version: await getPageEditCounter(updated.id)
22517
23695
  });
22518
23696
  return true;
22519
23697
  }
@@ -22635,8 +23813,819 @@ ${defuseFeedbackTags(note)}
22635
23813
 
22636
23814
  // src/http/pages-public.ts
22637
23815
  init_zod();
23816
+ init_db3();
23817
+
23818
+ // src/pages/browser-sdk.ts
23819
+ var BROWSER_SDK_JS = `
23820
+ class SwarmSDK {
23821
+ constructor() {
23822
+ let realtime;
23823
+ const loadRealtime = () => realtime || (realtime = import('/@swarm/realtime.js'));
23824
+ this.room = async (name, options) => (await loadRealtime()).room(name, options);
23825
+ this.channel = async (name) => (await loadRealtime()).channel(name);
23826
+
23827
+ const base = '/@swarm/api';
23828
+ const call = async (method, path, body) => {
23829
+ const init = { method };
23830
+ if (body !== undefined) {
23831
+ init.headers = { 'Content-Type': 'application/json' };
23832
+ init.body = JSON.stringify(body);
23833
+ }
23834
+ const res = await fetch(base + path, init);
23835
+ const text = await res.text();
23836
+ let parsed = null;
23837
+ if (text) {
23838
+ try { parsed = JSON.parse(text); } catch { parsed = text; }
23839
+ }
23840
+ if (!res.ok) {
23841
+ const err = new Error('SwarmSDK ' + method + ' ' + path + ': ' + res.status);
23842
+ err.status = res.status;
23843
+ err.response = parsed;
23844
+ throw err;
23845
+ }
23846
+ return parsed;
23847
+ };
23848
+ const qs = (obj) => {
23849
+ if (!obj) return '';
23850
+ const p = new URLSearchParams();
23851
+ for (const [k, v] of Object.entries(obj)) {
23852
+ if (v === undefined || v === null) continue;
23853
+ p.set(k, String(v));
23854
+ }
23855
+ const s = p.toString();
23856
+ return s ? '?' + s : '';
23857
+ };
23858
+ const enc = encodeURIComponent;
23859
+
23860
+ this.tasks = {
23861
+ create: (body) => call('POST', '/tasks', body),
23862
+ list: (filters) => call('GET', '/tasks' + qs(filters)),
23863
+ get: (id) => call('GET', '/tasks/' + enc(id)),
23864
+ storeProgress: (id, data) => call('POST', '/tasks/' + enc(id) + '/progress', data),
23865
+ };
23866
+
23867
+ this.agents = {
23868
+ list: () => call('GET', '/agents'),
23869
+ get: (id) => call('GET', '/agents/' + enc(id)),
23870
+ };
23871
+
23872
+ this.events = {
23873
+ create: (body) => call('POST', '/events', body),
23874
+ list: (filters) => call('GET', '/events' + qs(filters)),
23875
+ batch: (body) => call('POST', '/events/batch', body),
23876
+ counts: (filters) => call('GET', '/events/counts' + qs(filters)),
23877
+ };
23878
+
23879
+ this.memory = {
23880
+ search: (body) => call('POST', '/memory/search', body),
23881
+ list: (filters) => call('GET', '/memory/list' + qs(filters)),
23882
+ get: (id) => call('GET', '/memory/' + enc(id)),
23883
+ rate: (body) => call('POST', '/memory/rate', body),
23884
+ };
23885
+
23886
+ this.repos = {
23887
+ list: () => call('GET', '/repos'),
23888
+ get: (id) => call('GET', '/repos/' + enc(id)),
23889
+ create: (body) => call('POST', '/repos', body),
23890
+ update: (id, body) => call('PUT', '/repos/' + enc(id), body),
23891
+ delete: (id) => call('DELETE', '/repos/' + enc(id)),
23892
+ };
23893
+
23894
+ this.schedules = {
23895
+ list: () => call('GET', '/schedules'),
23896
+ get: (id) => call('GET', '/schedules/' + enc(id)),
23897
+ create: (body) => call('POST', '/schedules', body),
23898
+ update: (id, body) => call('PUT', '/schedules/' + enc(id), body),
23899
+ delete: (id) => call('DELETE', '/schedules/' + enc(id)),
23900
+ run: (id) => call('POST', '/schedules/' + enc(id) + '/run'),
23901
+ };
23902
+
23903
+ this.approvalRequests = {
23904
+ list: (filters) => call('GET', '/approval-requests' + qs(filters)),
23905
+ get: (id) => call('GET', '/approval-requests/' + enc(id)),
23906
+ create: (body) => call('POST', '/approval-requests', body),
23907
+ respond: (id, body) => call('POST', '/approval-requests/' + enc(id) + '/respond', body),
23908
+ };
23909
+
23910
+ this.assets = {
23911
+ list: (filters) => call('GET', '/assets' + qs(filters)),
23912
+ audit: () => call('GET', '/assets/key-audit'),
23913
+ registerMapping: (body) => call('POST', '/assets/mappings', body),
23914
+ move: (entityType, id, key) => call(
23915
+ 'PATCH',
23916
+ '/assets/' + enc(entityType) + '/' + enc(id) + '/key',
23917
+ { key },
23918
+ ),
23919
+ };
23920
+
23921
+ // KV store. The namespace is FORCED by the page-proxy to \`task:page:<id>\`
23922
+ // (it injects X-Page-Id which the kv handler treats as highest priority).
23923
+ // No namespace argument is exposed — pages cannot read/write any other
23924
+ // namespace via this SDK.
23925
+ this.kv = {
23926
+ get: (key) => call('GET', '/kv/' + enc(key)),
23927
+ set: (key, value, opts) => call('PUT', '/kv/' + enc(key), {
23928
+ value,
23929
+ valueType: opts && opts.valueType,
23930
+ expiresInSec: opts && opts.expiresInSec,
23931
+ }),
23932
+ del: (key) => call('DELETE', '/kv/' + enc(key)),
23933
+ incr: (key, by) => call('POST', '/kv/' + enc(key) + '/incr', { by: by == null ? 1 : by }),
23934
+ list: (opts) => call('GET', '/kv' + qs(opts)),
23935
+ };
23936
+ }
23937
+ }
23938
+
23939
+ // Expose BOTH the class (for \`new SwarmSDK()\`) AND a ready-to-use singleton
23940
+ // on \`window.swarmSdk\` so pages can call e.g. \`window.swarmSdk.agents.list()\`
23941
+ // directly without instantiating.
23942
+ window.SwarmSDK = SwarmSDK;
23943
+ window.swarmSdk = new SwarmSDK();
23944
+ `;
23945
+ var SWARM_UI_JS = `
23946
+ (function() {
23947
+ if (typeof window === 'undefined' || !window.customElements) return;
23948
+ if (window.customElements.get('swarm-diff')) return;
23949
+
23950
+ // Keep page-owned presence fields alongside the cursor. Room.presence.set
23951
+ // replaces its payload, so retain each room's latest page data and merge
23952
+ // the reserved __cursor field into it when the cursor moves.
23953
+ var roomPresenceTrackers = new WeakMap();
23954
+
23955
+ function clonePresenceRecord(data) {
23956
+ return data && typeof data === 'object' && !Array.isArray(data)
23957
+ ? structuredClone(data)
23958
+ : {};
23959
+ }
23960
+
23961
+ function mergeCursorPresence(data, cursor) {
23962
+ var merged = clonePresenceRecord(data);
23963
+ if (cursor) merged.__cursor = { x: cursor.x, y: cursor.y };
23964
+ else delete merged.__cursor;
23965
+ return merged;
23966
+ }
23967
+
23968
+ function trackRoomPresence(room) {
23969
+ if (!room || !room.presence || typeof room.presence.set !== 'function') return null;
23970
+ var existing = roomPresenceTrackers.get(room);
23971
+ if (existing) return existing;
23972
+
23973
+ var presence = room.presence;
23974
+ var originalSet = presence.set;
23975
+ var pageData = {};
23976
+ var cursor = null;
23977
+ var tracker = {
23978
+ setCursor: function(nextCursor) {
23979
+ cursor = nextCursor;
23980
+ return originalSet.call(presence, mergeCursorPresence(pageData, cursor));
23981
+ },
23982
+ clearCursor: function() {
23983
+ if (!cursor) return;
23984
+ return tracker.setCursor(null);
23985
+ },
23986
+ };
23987
+
23988
+ presence.set = function(data) {
23989
+ pageData = clonePresenceRecord(data);
23990
+ return originalSet.call(presence, cursor ? mergeCursorPresence(pageData, cursor) : data);
23991
+ };
23992
+ roomPresenceTrackers.set(room, tracker);
23993
+ return tracker;
23994
+ }
23995
+
23996
+ // Wrap room creation before page scripts run so presence.set calls made by
23997
+ // page code are tracked even when they happen before <swarm-cursors> connects.
23998
+ var cursorRoomRecords = new Map();
23999
+ var sdkRoomMethods = new WeakMap();
24000
+ var observedRoomClosers = new WeakSet();
24001
+ var originalRoom = null;
24002
+ function roomRecordKey(name) {
24003
+ return name === undefined ? 'default' : name;
24004
+ }
24005
+
24006
+ function observeRoom(record, room) {
24007
+ record.hasResolvedRoom = true;
24008
+ if (!cursorRoomRecords.has(record.name)) cursorRoomRecords.set(record.name, record);
24009
+ trackRoomPresence(room);
24010
+ if (room && typeof room.close === 'function' && !observedRoomClosers.has(room)) {
24011
+ var originalClose = room.close;
24012
+ room.close = function() {
24013
+ if (cursorRoomRecords.get(record.name) === record) cursorRoomRecords.delete(record.name);
24014
+ return originalClose.apply(room, arguments);
24015
+ };
24016
+ observedRoomClosers.add(room);
24017
+ }
24018
+ return room;
24019
+ }
24020
+
24021
+ function observeRoomPromise(record, result) {
24022
+ var observed = Promise.resolve(result).then(function(room) {
24023
+ var resolvedRoom = observeRoom(record, room);
24024
+ record.pageRequests--;
24025
+ record.hasPageUse = true;
24026
+ record.pageUsed = true;
24027
+ record.promise = Promise.resolve(resolvedRoom);
24028
+ return resolvedRoom;
24029
+ }).catch(function(error) {
24030
+ record.pageRequests--;
24031
+ record.pageUsed = record.hasPageUse || record.pageRequests > 0;
24032
+ if (!record.hasResolvedRoom
24033
+ && record.pageRequests === 0
24034
+ && cursorRoomRecords.get(record.name) === record)
24035
+ cursorRoomRecords.delete(record.name);
24036
+ if (record.cursorRefs === 0 && record.openedByCursor && !record.pageUsed)
24037
+ closeCursorRoomIfUnused(record);
24038
+ throw error;
24039
+ });
24040
+ record.pageRequests++;
24041
+ record.pageUsed = true;
24042
+ if (!record.promise) record.promise = observed;
24043
+ return observed;
24044
+ }
24045
+
24046
+ function closeCursorRoomIfUnused(record) {
24047
+ if (record.closing || record.cursorRefs || !record.openedByCursor || record.pageUsed) return;
24048
+ record.closing = true;
24049
+ record.promise.then(function(room) {
24050
+ if (record.cursorRefs || record.pageUsed) {
24051
+ record.closing = false;
24052
+ return;
24053
+ }
24054
+ if (cursorRoomRecords.get(record.name) === record) cursorRoomRecords.delete(record.name);
24055
+ if (room && typeof room.close === 'function') return room.close();
24056
+ }).catch(function() {
24057
+ record.closing = false;
24058
+ });
24059
+ }
24060
+
24061
+ function instrumentSdk(sdk) {
24062
+ if (!sdk || typeof sdk.room !== 'function') return null;
24063
+ var existing = sdkRoomMethods.get(sdk);
24064
+ if (existing) return existing;
24065
+
24066
+ var sdkOriginalRoom = sdk.room;
24067
+ var wrappedRoom = function(name, options) {
24068
+ var key = roomRecordKey(name);
24069
+ var record = cursorRoomRecords.get(key);
24070
+ var result = sdkOriginalRoom.call(sdk, name, options);
24071
+ if (!record) {
24072
+ record = {
24073
+ name: key,
24074
+ cursorRefs: 0,
24075
+ openedByCursor: false,
24076
+ pageUsed: false,
24077
+ hasPageUse: false,
24078
+ pageRequests: 0,
24079
+ hasResolvedRoom: false,
24080
+ promise: null,
24081
+ };
24082
+ cursorRoomRecords.set(key, record);
24083
+ }
24084
+ return observeRoomPromise(record, result);
24085
+ };
24086
+ sdk.room = wrappedRoom;
24087
+ var methods = { original: sdkOriginalRoom, wrapped: wrappedRoom };
24088
+ sdkRoomMethods.set(sdk, methods);
24089
+ return methods;
24090
+ }
24091
+
24092
+ if (window.swarmSdk) {
24093
+ var singletonRoomMethods = instrumentSdk(window.swarmSdk);
24094
+ originalRoom = singletonRoomMethods && singletonRoomMethods.original;
24095
+ }
24096
+ if (typeof window.SwarmSDK === 'function') {
24097
+ var OriginalSwarmSDK = window.SwarmSDK;
24098
+ window.SwarmSDK = new Proxy(OriginalSwarmSDK, {
24099
+ construct: function(target, args, newTarget) {
24100
+ var sdk = Reflect.construct(target, args, newTarget);
24101
+ instrumentSdk(sdk);
24102
+ return sdk;
24103
+ },
24104
+ });
24105
+ }
24106
+
24107
+ function acquireCursorRoom(name, options) {
24108
+ var key = roomRecordKey(name);
24109
+ var record = cursorRoomRecords.get(key);
24110
+ if (record) {
24111
+ record.cursorRefs++;
24112
+ return { record: record, promise: record.promise };
24113
+ }
24114
+
24115
+ record = {
24116
+ name: key,
24117
+ cursorRefs: 1,
24118
+ openedByCursor: true,
24119
+ pageUsed: false,
24120
+ hasPageUse: false,
24121
+ pageRequests: 0,
24122
+ hasResolvedRoom: false,
24123
+ closing: false,
24124
+ promise: null,
24125
+ };
24126
+ cursorRoomRecords.set(key, record);
24127
+ record.promise = Promise.resolve().then(function() {
24128
+ return originalRoom(name, options);
24129
+ }).then(function(room) {
24130
+ return observeRoom(record, room);
24131
+ }).catch(function(error) {
24132
+ if (cursorRoomRecords.get(key) === record) cursorRoomRecords.delete(key);
24133
+ throw error;
24134
+ });
24135
+ return { record: record, promise: record.promise };
24136
+ }
24137
+
24138
+ function releaseCursorRoom(record, presence) {
24139
+ if (!record || record.cursorRefs < 1) return;
24140
+ if (record.cursorRefs === 1 && presence) presence.clearCursor();
24141
+ record.cursorRefs--;
24142
+ closeCursorRoomIfUnused(record);
24143
+ }
24144
+
24145
+ var SEV_COLOR = {
24146
+ error: '#ef4444',
24147
+ warn: '#f59e0b',
24148
+ info: '#3b82f6',
24149
+ };
24150
+
24151
+ function esc(s) {
24152
+ return String(s == null ? '' : s)
24153
+ .replace(/&/g, '&amp;')
24154
+ .replace(/</g, '&lt;')
24155
+ .replace(/>/g, '&gt;')
24156
+ .replace(/"/g, '&quot;')
24157
+ .replace(/'/g, '&#39;');
24158
+ }
24159
+
24160
+ function slugifyAttr(s) {
24161
+ return String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
24162
+ }
24163
+
24164
+ function parseHunks(jsonText) {
24165
+ var trimmed = (jsonText || '').trim();
24166
+ if (!trimmed) return [];
24167
+ try {
24168
+ var parsed = JSON.parse(trimmed);
24169
+ if (parsed && Array.isArray(parsed.hunks)) return parsed.hunks;
24170
+ if (Array.isArray(parsed)) return parsed;
24171
+ return [];
24172
+ } catch (e) {
24173
+ console.warn('[swarm-diff] failed to parse JSON body:', e);
24174
+ return [];
24175
+ }
24176
+ }
24177
+
24178
+ function renderAnnotation(ann) {
24179
+ var color = SEV_COLOR[ann && ann.severity] || SEV_COLOR.info;
24180
+ return (
24181
+ '<span class="swarm-diff-annot no-print" '
24182
+ + 'style="display:inline-block;margin-left:8px;padding:1px 6px;'
24183
+ + 'border-radius:4px;font-size:11px;font-weight:600;'
24184
+ + 'background:' + color + '22;color:' + color + ';border:1px solid ' + color + '55;">'
24185
+ + esc((ann && ann.severity ? ann.severity.toUpperCase() : 'INFO')) + ' · ' + esc(ann && ann.text || '')
24186
+ + '</span>'
24187
+ );
24188
+ }
24189
+
24190
+ function renderHunk(hunk, hunkIdx, file) {
24191
+ var oldLines = hunk.old_lines || 0;
24192
+ var newLines = hunk.new_lines || 0;
24193
+ var oldStart = hunk.old_start || 0;
24194
+ var newStart = hunk.new_start || 0;
24195
+ var lines = Array.isArray(hunk.lines) ? hunk.lines : [];
24196
+ var annotations = Array.isArray(hunk.annotations) ? hunk.annotations : [];
24197
+ // Index annotations by new-side line number for fast lookup per row.
24198
+ var annByLine = {};
24199
+ for (var i = 0; i < annotations.length; i++) {
24200
+ var a = annotations[i];
24201
+ if (a && typeof a.line === 'number') {
24202
+ if (!annByLine[a.line]) annByLine[a.line] = [];
24203
+ annByLine[a.line].push(a);
24204
+ }
24205
+ }
24206
+
24207
+ var rowsHtml = '';
24208
+ var oldN = oldStart;
24209
+ var newN = newStart;
24210
+ for (var j = 0; j < lines.length; j++) {
24211
+ var line = lines[j] || {};
24212
+ var type = line.type || 'context';
24213
+ var text = line.text == null ? '' : line.text;
24214
+ var bg, oldCell, newCell, sign;
24215
+ if (type === 'add') {
24216
+ bg = 'rgba(34,197,94,0.10)';
24217
+ oldCell = '';
24218
+ newCell = String(newN++);
24219
+ sign = '+';
24220
+ } else if (type === 'del') {
24221
+ bg = 'rgba(239,68,68,0.10)';
24222
+ oldCell = String(oldN++);
24223
+ newCell = '';
24224
+ sign = '-';
24225
+ } else {
24226
+ bg = 'transparent';
24227
+ oldCell = String(oldN++);
24228
+ newCell = String(newN++);
24229
+ sign = ' ';
24230
+ }
24231
+
24232
+ var annHtml = '';
24233
+ var anns = annByLine[Number(newCell)] || annByLine[Number(oldCell)] || [];
24234
+ for (var k = 0; k < anns.length; k++) annHtml += renderAnnotation(anns[k]);
24235
+
24236
+ rowsHtml += (
24237
+ '<tr style="background:' + bg + ';">'
24238
+ + '<td class="swarm-diff-gutter" style="user-select:none;text-align:right;padding:0 8px;color:#7c8aa6;font-size:12px;width:48px;">' + esc(oldCell) + '</td>'
24239
+ + '<td class="swarm-diff-gutter" style="user-select:none;text-align:right;padding:0 8px;color:#7c8aa6;font-size:12px;width:48px;">' + esc(newCell) + '</td>'
24240
+ + '<td class="swarm-diff-sign" style="user-select:none;text-align:center;padding:0 4px;color:#7c8aa6;font-size:12px;width:18px;">' + esc(sign) + '</td>'
24241
+ + '<td class="swarm-diff-code" style="padding:0 8px;white-space:pre-wrap;word-break:break-word;font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:12px;">' + esc(text) + annHtml + '</td>'
24242
+ + '</tr>'
24243
+ );
24244
+ }
24245
+
24246
+ var anchorSlug = slugifyAttr((file || 'hunk') + '-' + (oldStart || hunkIdx + 1));
24247
+ var anchorId = 'swarm-diff-' + anchorSlug;
24248
+ var header = (
24249
+ '@@ -' + oldStart + ',' + oldLines + ' +' + newStart + ',' + newLines + ' @@'
24250
+ );
24251
+
24252
+ return (
24253
+ '<a id="' + esc(anchorId) + '" class="swarm-diff-anchor" data-hunk="' + esc(anchorSlug) + '"></a>'
24254
+ + '<div class="swarm-diff-hunk-header" style="padding:6px 12px;background:rgba(124,138,166,0.10);color:#7c8aa6;font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:11px;border-top:1px solid var(--swarm-border,#22304a);">'
24255
+ + esc(header)
24256
+ + '</div>'
24257
+ + '<table class="swarm-diff-table" style="width:100%;border-collapse:collapse;table-layout:fixed;">'
24258
+ + '<tbody>' + rowsHtml + '</tbody>'
24259
+ + '</table>'
24260
+ );
24261
+ }
24262
+
24263
+ function renderDiff(rootEl, diffData) {
24264
+ var hunks = (diffData && Array.isArray(diffData.hunks)) ? diffData.hunks : (Array.isArray(diffData) ? diffData : []);
24265
+ var file = rootEl.getAttribute('file') || '';
24266
+ var baseSha = rootEl.getAttribute('base-sha') || '';
24267
+ var headSha = rootEl.getAttribute('head-sha') || '';
24268
+
24269
+ var shaLine = '';
24270
+ if (baseSha || headSha) {
24271
+ shaLine = '<span class="swarm-diff-sha" style="font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:11px;color:#7c8aa6;">'
24272
+ + esc(baseSha) + ' → ' + esc(headSha)
24273
+ + '</span>';
24274
+ }
24275
+
24276
+ var hunksHtml = '';
24277
+ for (var i = 0; i < hunks.length; i++) {
24278
+ hunksHtml += renderHunk(hunks[i] || {}, i, file);
24279
+ }
24280
+
24281
+ rootEl.innerHTML = (
24282
+ '<div class="swarm-diff-root" style="border:1px solid var(--swarm-border,#22304a);border-radius:8px;background:var(--swarm-card,#121826);overflow:hidden;margin:12px 0;break-inside:avoid;">'
24283
+ + '<div class="swarm-diff-header" style="display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:rgba(59,130,246,0.10);border-bottom:1px solid var(--swarm-border,#22304a);">'
24284
+ + '<span class="swarm-diff-file" style="font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:13px;font-weight:700;color:var(--swarm-text,#e6eaf2);">' + esc(file || '(untitled)') + '</span>'
24285
+ + shaLine
24286
+ + '</div>'
24287
+ + hunksHtml
24288
+ + '</div>'
24289
+ );
24290
+ }
24291
+
24292
+ // Public function form lives on window.swarmUi so callers can render an
24293
+ // arbitrary root element programmatically. The custom element below is just
24294
+ // a declarative wrapper around the same render function.
24295
+ window.swarmUi = window.swarmUi || {};
24296
+ window.swarmUi.renderDiff = renderDiff;
24297
+
24298
+ // Defer the parse-and-render so the HTML parser has time to finish
24299
+ // appending JSON text children. \`connectedCallback\` fires on the opening
24300
+ // tag — \`this.textContent\` is empty until children parse. Without a
24301
+ // defer, every declarative <swarm-diff> renders an empty header and the
24302
+ // JSON text remains visible as orphan children.
24303
+ //
24304
+ // queueMicrotask alone is NOT enough — Chrome's streaming parser drains
24305
+ // microtasks between chunks, so the microtask can run BEFORE the JSON
24306
+ // child is appended. We need to wait for the parser to finish the current
24307
+ // document load, then read textContent.
24308
+ //
24309
+ // * \`document.readyState === 'loading'\` ⇒ parser still streaming →
24310
+ // wait for DOMContentLoaded (fires after all children are parsed).
24311
+ // * otherwise (element was created/inserted dynamically post-load) ⇒
24312
+ // queueMicrotask is fine — DOM is stable, just give the caller a tick.
24313
+ //
24314
+ // Re-entrancy (element moved/reconnected) re-fires connectedCallback so
24315
+ // we re-render against current textContent.
24316
+ class SwarmDiffElement extends HTMLElement {
24317
+ connectedCallback() {
24318
+ var self = this;
24319
+ var doRender = function() {
24320
+ if (!self.isConnected) return;
24321
+ var raw = self.textContent || '';
24322
+ renderDiff(self, { hunks: parseHunks(raw) });
24323
+ // Notify <swarm-diff-jumps> instances so they can pick up new anchors.
24324
+ self.dispatchEvent(new CustomEvent('swarm-diff:rendered', { bubbles: true }));
24325
+ };
24326
+ if (typeof document !== 'undefined' && document.readyState === 'loading') {
24327
+ document.addEventListener('DOMContentLoaded', doRender, { once: true });
24328
+ } else {
24329
+ queueMicrotask(doRender);
24330
+ }
24331
+ }
24332
+ }
24333
+ window.customElements.define('swarm-diff', SwarmDiffElement);
24334
+
24335
+ // Sibling-anchor jump list. Walks subsequent siblings, finds every
24336
+ // <swarm-diff data-hunk=...> anchor, and renders a small list of links.
24337
+ //
24338
+ // Same parse-order hazard as <swarm-diff>: <swarm-diff-jumps> usually
24339
+ // appears in the document BEFORE the <swarm-diff> elements it indexes, so
24340
+ // we also defer to a microtask AND re-render whenever a <swarm-diff> in the
24341
+ // document finishes rendering its anchors.
24342
+ class SwarmDiffJumpsElement extends HTMLElement {
24343
+ connectedCallback() {
24344
+ var self = this;
24345
+ var renderJumps = function() {
24346
+ var anchors = document.querySelectorAll('.swarm-diff-anchor[data-hunk]');
24347
+ if (!anchors.length) {
24348
+ self.innerHTML = '<span class="no-print" style="color:#7c8aa6;font-size:12px;">No hunks yet.</span>';
24349
+ return;
24350
+ }
24351
+ var items = '';
24352
+ for (var i = 0; i < anchors.length; i++) {
24353
+ var a = anchors[i];
24354
+ var slug = a.getAttribute('data-hunk') || ('hunk-' + i);
24355
+ // Hunk title = nearest preceding diff's file attribute if available.
24356
+ var diff = a.closest && a.closest('swarm-diff');
24357
+ var file = (diff && diff.getAttribute('file')) || slug;
24358
+ items += '<li style="margin:0;padding:2px 0;"><a href="#' + esc(a.id) + '" style="color:#3b82f6;text-decoration:none;font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:12px;">' + esc(file) + '</a></li>';
24359
+ }
24360
+ self.innerHTML = (
24361
+ '<nav class="swarm-diff-jumps no-print" style="padding:8px 12px;border:1px dashed var(--swarm-border,#22304a);border-radius:8px;background:rgba(124,138,166,0.05);">'
24362
+ + '<div style="font-size:10px;text-transform:uppercase;letter-spacing:0.08em;color:#7c8aa6;margin-bottom:4px;">Jump to</div>'
24363
+ + '<ul style="list-style:none;padding:0;margin:0;">' + items + '</ul>'
24364
+ + '</nav>'
24365
+ );
24366
+ };
24367
+ // Wait for the parser to finish initial load before first query —
24368
+ // <swarm-diff> elements also defer to DOMContentLoaded so we must
24369
+ // run AFTER they finish rendering their anchors. The event listener
24370
+ // below handles the live-update case.
24371
+ if (typeof document !== 'undefined' && document.readyState === 'loading') {
24372
+ document.addEventListener('DOMContentLoaded', function() { queueMicrotask(renderJumps); }, { once: true });
24373
+ } else {
24374
+ queueMicrotask(renderJumps);
24375
+ }
24376
+ // Re-render whenever a sibling <swarm-diff> finishes its async render.
24377
+ self._onDiffRendered = function() { renderJumps(); };
24378
+ document.addEventListener('swarm-diff:rendered', self._onDiffRendered);
24379
+ }
24380
+ disconnectedCallback() {
24381
+ if (this._onDiffRendered) {
24382
+ document.removeEventListener('swarm-diff:rendered', this._onDiffRendered);
24383
+ }
24384
+ }
24385
+ }
24386
+ window.customElements.define('swarm-diff-jumps', SwarmDiffJumpsElement);
24387
+
24388
+ var CURSOR_IDLE_MS = 3000;
24389
+ var CURSOR_SMOOTHING = 0.28;
24390
+
24391
+ function cursorColor(userId) {
24392
+ var hash = 2166136261;
24393
+ var value = String(userId || '');
24394
+ for (var i = 0; i < value.length; i++) {
24395
+ hash ^= value.charCodeAt(i);
24396
+ hash = Math.imul(hash, 16777619);
24397
+ }
24398
+ return 'hsl(' + ((hash >>> 0) % 360) + ', 72%, 42%)';
24399
+ }
24400
+
24401
+ function documentSize() {
24402
+ var root = document.documentElement || {};
24403
+ return {
24404
+ width: Math.max(1, root.scrollWidth || root.clientWidth || window.innerWidth || 1),
24405
+ height: Math.max(1, root.scrollHeight || root.clientHeight || window.innerHeight || 1),
24406
+ };
24407
+ }
24408
+
24409
+ function cursorMarkup(name, color) {
24410
+ // CursorPointer + CursorBody label structure adapted from Kibo UI (MIT):
24411
+ // https://github.com/shadcnblocks/kibo/blob/main/packages/cursor/index.tsx
24412
+ return '<svg aria-hidden="true" focusable="false" height="20" width="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;flex:none;filter:drop-shadow(0 1px 1px rgba(0,0,0,.2));">'
24413
+ + '<path d="M19.438 6.716 1.115.05A.832.832 0 0 0 .05 1.116L6.712 19.45a.834.834 0 0 0 1.557.025l3.198-8 7.995-3.2a.833.833 0 0 0 0-1.559h-.024Z" fill="currentColor"></path>'
24414
+ + '</svg>'
24415
+ + '<span style="position:relative;margin-left:14px;display:flex;flex-direction:column;white-space:nowrap;border-radius:12px;padding:4px 12px 4px 10px;font-family:system-ui,sans-serif;font-size:12px;font-weight:500;line-height:1.4;background:' + color + ';color:#fff;box-shadow:0 2px 6px rgba(0,0,0,.18);">'
24416
+ + esc(name || 'Guest')
24417
+ + '</span>';
24418
+ }
24419
+
24420
+ class SwarmCursorsElement extends HTMLElement {
24421
+ connectedCallback() {
24422
+ if (this._startPromise) return;
24423
+ var roomName = this.getAttribute('room');
24424
+ if (!roomName || !originalRoom) return;
24425
+
24426
+ var self = this;
24427
+ var lifecycle = (this._lifecycle || 0) + 1;
24428
+ this._lifecycle = lifecycle;
24429
+ var begin = function() {
24430
+ self._onDomReady = null;
24431
+ if (!self.isConnected || self._lifecycle !== lifecycle) return;
24432
+ var schemaVersion = self.getAttribute('schema-version');
24433
+ var options = schemaVersion == null ? {} : { schemaVersion: Number(schemaVersion) };
24434
+ var acquired = acquireCursorRoom(roomName, options);
24435
+ self._roomRecord = acquired.record;
24436
+ self._startPromise = acquired.promise.then(function(room) {
24437
+ if (!self.isConnected || self._lifecycle !== lifecycle) return;
24438
+ self._startPromise = null;
24439
+ self._room = room;
24440
+ self._presence = trackRoomPresence(room);
24441
+ if (!self._presence) return;
24442
+ self._pointers = new Map();
24443
+ self._cursorStates = new Map();
24444
+
24445
+ self._overlay = document.createElement('div');
24446
+ self._overlay.setAttribute('aria-hidden', 'true');
24447
+ self._overlay.style.cssText = 'position:fixed;inset:0;z-index:2147483647;pointer-events:none;overflow:hidden;';
24448
+ var parent = document.body || document.documentElement;
24449
+ if (parent) parent.appendChild(self._overlay);
24450
+
24451
+ self._onPresence = function(peers) { self._renderPeers(peers || []); };
24452
+ self._onPointerMove = function(event) {
24453
+ var size = documentSize();
24454
+ var scrollX = window.scrollX || window.pageXOffset || 0;
24455
+ var scrollY = window.scrollY || window.pageYOffset || 0;
24456
+ var pageX = typeof event.pageX === 'number' ? event.pageX : event.clientX + scrollX;
24457
+ var pageY = typeof event.pageY === 'number' ? event.pageY : event.clientY + scrollY;
24458
+ var x = Math.max(0, Math.min(1, pageX / size.width));
24459
+ var y = Math.max(0, Math.min(1, pageY / size.height));
24460
+ self._presence.setCursor({ x: x, y: y });
24461
+ };
24462
+ self._onPointerLeave = function() {
24463
+ self._presence.clearCursor();
24464
+ };
24465
+
24466
+ self._unsubscribe = room.on('presence', self._onPresence);
24467
+ document.addEventListener('pointermove', self._onPointerMove, { passive: true });
24468
+ document.addEventListener('pointerleave', self._onPointerLeave);
24469
+ self._renderPeers(room.presence.peers || []);
24470
+ }).catch(function(error) {
24471
+ self._startPromise = null;
24472
+ console.warn('[swarm-cursors] failed to join room:', error);
24473
+ });
24474
+ };
24475
+ if (document.readyState === 'loading') {
24476
+ this._onDomReady = function() { queueMicrotask(begin); };
24477
+ this._startPromise = Promise.resolve();
24478
+ document.addEventListener('DOMContentLoaded', this._onDomReady, { once: true });
24479
+ } else {
24480
+ begin();
24481
+ }
24482
+ }
24483
+
24484
+ _renderPeers(peers) {
24485
+ if (!this._pointers) return;
24486
+ var seen = new Set();
24487
+ var activePeerIds = new Set();
24488
+ var includeAgents = this.hasAttribute('include-agents');
24489
+ var ownId = this._room && this._room.me && this._room.me.userId;
24490
+ var now = Date.now();
24491
+
24492
+ for (var i = 0; i < peers.length; i++) {
24493
+ var peer = peers[i];
24494
+ if (!peer || !peer.userId || peer.userId === ownId) continue;
24495
+ if (peer.kind === 'agent' && !includeAgents) continue;
24496
+ var data = peer.data && typeof peer.data === 'object' ? peer.data : null;
24497
+ var cursor = data && data.__cursor;
24498
+ if (!cursor || typeof cursor.x !== 'number' || typeof cursor.y !== 'number'
24499
+ || !Number.isFinite(cursor.x) || !Number.isFinite(cursor.y)) continue;
24500
+
24501
+ var id = String(peer.userId);
24502
+ activePeerIds.add(id);
24503
+ var state = this._cursorStates.get(id);
24504
+ if (!state || state.x !== cursor.x || state.y !== cursor.y) {
24505
+ state = { x: cursor.x, y: cursor.y, lastSeen: now };
24506
+ this._cursorStates.set(id, state);
24507
+ }
24508
+ if (now - state.lastSeen > CURSOR_IDLE_MS) continue;
24509
+
24510
+ var entry = this._pointers.get(id);
24511
+ if (!entry) {
24512
+ entry = this._createPointer(peer);
24513
+ this._pointers.set(id, entry);
24514
+ }
24515
+ entry.cursor = { x: state.x, y: state.y };
24516
+ entry.lastSeen = state.lastSeen;
24517
+ if (entry.name !== peer.name) {
24518
+ entry.name = peer.name || 'Guest';
24519
+ entry.node.innerHTML = cursorMarkup(entry.name, entry.color);
24520
+ }
24521
+ seen.add(id);
24522
+ }
24523
+
24524
+ var ids = Array.from(this._pointers.keys());
24525
+ for (var j = 0; j < ids.length; j++) {
24526
+ if (!seen.has(ids[j])) this._removePointer(ids[j]);
24527
+ }
24528
+ var stateIds = Array.from(this._cursorStates.keys());
24529
+ for (var k = 0; k < stateIds.length; k++) {
24530
+ if (!activePeerIds.has(stateIds[k])) this._cursorStates.delete(stateIds[k]);
24531
+ }
24532
+ this._scheduleFrame();
24533
+ }
24534
+
24535
+ _createPointer(peer) {
24536
+ var color = cursorColor(peer.userId);
24537
+ var node = document.createElement('div');
24538
+ node.style.cssText = 'position:absolute;left:0;top:0;display:flex;align-items:flex-start;pointer-events:none;user-select:none;will-change:transform;color:' + color + ';';
24539
+ node.innerHTML = cursorMarkup(peer.name || 'Guest', color);
24540
+ this._overlay.appendChild(node);
24541
+ return {
24542
+ node: node,
24543
+ color: color,
24544
+ name: peer.name || 'Guest',
24545
+ cursor: null,
24546
+ x: null,
24547
+ y: null,
24548
+ lastSeen: Date.now(),
24549
+ };
24550
+ }
24551
+
24552
+ _removePointer(id) {
24553
+ var entry = this._pointers.get(id);
24554
+ if (entry && entry.node.parentNode) entry.node.parentNode.removeChild(entry.node);
24555
+ this._pointers.delete(id);
24556
+ }
24557
+
24558
+ _scheduleFrame() {
24559
+ if (this._frameId != null || !this._pointers || !this._pointers.size
24560
+ || typeof window.requestAnimationFrame !== 'function') return;
24561
+ var self = this;
24562
+ this._frameId = window.requestAnimationFrame(function() { self._animate(); });
24563
+ }
24564
+
24565
+ _animate() {
24566
+ this._frameId = null;
24567
+ if (!this._pointers || !this._overlay) return;
24568
+ var now = Date.now();
24569
+ var size = documentSize();
24570
+ var scrollX = window.scrollX || window.pageXOffset || 0;
24571
+ var scrollY = window.scrollY || window.pageYOffset || 0;
24572
+ var ids = Array.from(this._pointers.keys());
22638
24573
 
22639
- // src/artifact-sdk/feedback-overlay.ts
24574
+ for (var i = 0; i < ids.length; i++) {
24575
+ var id = ids[i];
24576
+ var entry = this._pointers.get(id);
24577
+ if (now - entry.lastSeen > CURSOR_IDLE_MS) {
24578
+ this._removePointer(id);
24579
+ continue;
24580
+ }
24581
+ var targetX = entry.cursor.x * size.width - scrollX;
24582
+ var targetY = entry.cursor.y * size.height - scrollY;
24583
+ if (entry.x == null || entry.y == null) {
24584
+ entry.x = targetX;
24585
+ entry.y = targetY;
24586
+ } else {
24587
+ // Exponential interpolation keeps 50 ms presence updates from jumping.
24588
+ entry.x += (targetX - entry.x) * CURSOR_SMOOTHING;
24589
+ entry.y += (targetY - entry.y) * CURSOR_SMOOTHING;
24590
+ }
24591
+ entry.node.style.transform = 'translate3d(' + Math.round(entry.x) + 'px,' + Math.round(entry.y) + 'px,0)';
24592
+ }
24593
+ this._scheduleFrame();
24594
+ }
24595
+
24596
+ disconnectedCallback() {
24597
+ this._lifecycle = (this._lifecycle || 0) + 1;
24598
+ this._startPromise = null;
24599
+ if (this._onDomReady) {
24600
+ document.removeEventListener('DOMContentLoaded', this._onDomReady);
24601
+ this._onDomReady = null;
24602
+ }
24603
+ if (this._unsubscribe) this._unsubscribe();
24604
+ if (this._onPointerMove) document.removeEventListener('pointermove', this._onPointerMove);
24605
+ if (this._onPointerLeave) document.removeEventListener('pointerleave', this._onPointerLeave);
24606
+ if (this._roomRecord) releaseCursorRoom(this._roomRecord, this._presence);
24607
+ if (this._frameId != null && typeof window.cancelAnimationFrame === 'function')
24608
+ window.cancelAnimationFrame(this._frameId);
24609
+ if (this._pointers) {
24610
+ var ids = Array.from(this._pointers.keys());
24611
+ for (var i = 0; i < ids.length; i++) this._removePointer(ids[i]);
24612
+ }
24613
+ if (this._overlay && this._overlay.parentNode)
24614
+ this._overlay.parentNode.removeChild(this._overlay);
24615
+ this._room = null;
24616
+ this._roomRecord = null;
24617
+ this._presence = null;
24618
+ this._overlay = null;
24619
+ this._pointers = null;
24620
+ this._cursorStates = null;
24621
+ this._frameId = null;
24622
+ }
24623
+ }
24624
+ window.customElements.define('swarm-cursors', SwarmCursorsElement);
24625
+ })();
24626
+ `;
24627
+
24628
+ // src/pages/feedback-overlay.ts
22640
24629
  var PAGE_FEEDBACK_PARAM = "__swarm-feedback";
22641
24630
  function isPageFeedbackRequested(queryParams) {
22642
24631
  if (!queryParams.has(PAGE_FEEDBACK_PARAM))
@@ -22644,6 +24633,33 @@ function isPageFeedbackRequested(queryParams) {
22644
24633
  const value = (queryParams.get(PAGE_FEEDBACK_PARAM) ?? "").toLowerCase();
22645
24634
  return value !== "0" && value !== "false";
22646
24635
  }
24636
+ var FEEDBACK_KEY_GUARD_JS = `
24637
+ (function () {
24638
+ if (window.__swarmFeedbackKeys) return;
24639
+ var keys = window.__swarmFeedbackKeys = { host: null, handle: null };
24640
+ var owned = {};
24641
+ function id(e) { return e.code || e.key; }
24642
+ function fromOverlay(e) {
24643
+ return !!keys.host && typeof e.composedPath === 'function' && e.composedPath().indexOf(keys.host) !== -1;
24644
+ }
24645
+ function guard(e) {
24646
+ var k = id(e);
24647
+ var mine = fromOverlay(e);
24648
+ if (!mine && !owned[k]) return;
24649
+ if (e.type === 'keydown') {
24650
+ if (mine) owned[k] = true;
24651
+ else if (!e.repeat) { delete owned[k]; return; }
24652
+ }
24653
+ if (e.type === 'keyup') delete owned[k];
24654
+ e.stopImmediatePropagation();
24655
+ if (mine && keys.handle) keys.handle(e);
24656
+ }
24657
+ ['keydown', 'keypress', 'keyup'].forEach(function (type) {
24658
+ window.addEventListener(type, guard, true);
24659
+ });
24660
+ window.addEventListener('blur', function () { owned = {}; });
24661
+ })();
24662
+ `;
22647
24663
  var FEEDBACK_OVERLAY_JS = `
22648
24664
  (function () {
22649
24665
  var cfg = window.__swarmFeedback;
@@ -22754,6 +24770,7 @@ var FEEDBACK_OVERLAY_JS = `
22754
24770
  var panel = document.createElement('div');
22755
24771
  panel.className = 'panel';
22756
24772
  var composer = null;
24773
+ var composerInput = null;
22757
24774
  root.appendChild(hl);
22758
24775
  root.appendChild(markers);
22759
24776
  root.appendChild(panel);
@@ -22826,11 +24843,25 @@ var FEEDBACK_OVERLAY_JS = `
22826
24843
  }
22827
24844
  render();
22828
24845
  }
22829
- document.addEventListener('keydown', function (e) {
22830
- if (e.key !== 'Escape') return;
24846
+ function onEscape() {
22831
24847
  if (state.draft) { closeComposer(); return; }
22832
24848
  if (state.selecting) setSelecting(false);
24849
+ }
24850
+ // Escape pressed on the page while selecting. Keys typed in the overlay never
24851
+ // get here: the key guard stops them and calls onOverlayKey instead.
24852
+ document.addEventListener('keydown', function (e) {
24853
+ if (e.key === 'Escape') onEscape();
22833
24854
  }, true);
24855
+ function onOverlayKey(e) {
24856
+ if (e.type !== 'keydown') return;
24857
+ if (e.key === 'Escape') { onEscape(); return; }
24858
+ if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && composerInput && e.composedPath()[0] === composerInput) {
24859
+ e.preventDefault();
24860
+ commitDraft(composerInput.value);
24861
+ }
24862
+ }
24863
+ window.__swarmFeedbackKeys.host = host;
24864
+ window.__swarmFeedbackKeys.handle = onOverlayKey;
22834
24865
 
22835
24866
  // ─── Composer ──────────────────────────────────────────────────────────
22836
24867
  function openComposer(target) {
@@ -22849,9 +24880,7 @@ var FEEDBACK_OVERLAY_JS = `
22849
24880
  add.type = 'button';
22850
24881
  cancel.addEventListener('click', closeComposer);
22851
24882
  add.addEventListener('click', function () { commitDraft(input.value); });
22852
- input.addEventListener('keydown', function (e) {
22853
- if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); commitDraft(input.value); }
22854
- });
24883
+ composerInput = input;
22855
24884
  row.appendChild(cancel);
22856
24885
  row.appendChild(add);
22857
24886
  composer.appendChild(row);
@@ -22872,6 +24901,7 @@ var FEEDBACK_OVERLAY_JS = `
22872
24901
  function closeComposer() {
22873
24902
  if (composer) composer.remove();
22874
24903
  composer = null;
24904
+ composerInput = null;
22875
24905
  state.draft = null;
22876
24906
  hideHighlight();
22877
24907
  }
@@ -23137,11 +25167,13 @@ var FEEDBACK_OVERLAY_JS = `
23137
25167
  `;
23138
25168
  function feedbackOverlayScripts(pageId) {
23139
25169
  const config = JSON.stringify({ pageId }).replace(/</g, "\\u003c");
23140
- return `<script>window.__swarmFeedback = ${config};</script><script>${FEEDBACK_OVERLAY_JS}</script>`;
25170
+ return `<script>window.__swarmFeedback = ${config};</script><script>${FEEDBACK_KEY_GUARD_JS}${FEEDBACK_OVERLAY_JS}</script>`;
25171
+ }
25172
+ function feedbackKeyGuardScript() {
25173
+ return `<script>${FEEDBACK_KEY_GUARD_JS}</script>`;
23141
25174
  }
23142
25175
 
23143
25176
  // src/http/pages-public.ts
23144
- init_db3();
23145
25177
  init_types();
23146
25178
  init_constants();
23147
25179
  init_node_env();
@@ -23246,8 +25278,8 @@ var PAGE_HEAD_DEFAULTS = `<base target="_blank">
23246
25278
  .swarm-card, swarm-diff { break-inside: avoid; }
23247
25279
  }
23248
25280
  </style>`;
23249
- function injectBrowserSdk(html) {
23250
- const injection = `${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
25281
+ function injectBrowserSdk(html, headFirst = "") {
25282
+ const injection = `${headFirst}${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
23251
25283
  const headOpenMatch = html.match(/<head\b[^>]*>/i);
23252
25284
  if (headOpenMatch) {
23253
25285
  const idx = headOpenMatch.index + headOpenMatch[0].length;
@@ -23550,7 +25582,8 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
23550
25582
  res.end();
23551
25583
  return true;
23552
25584
  }
23553
- const html = injectBrowserSdk(page.body) + (wantsPrint ? PRINT_AUTOTRIGGER_SCRIPT : "") + (isPageFeedbackRequested(queryParams) ? feedbackOverlayScripts(page.id) : "");
25585
+ const feedback = isPageFeedbackRequested(queryParams);
25586
+ const html = injectBrowserSdk(page.body, feedback ? feedbackKeyGuardScript() : "") + (wantsPrint ? PRINT_AUTOTRIGGER_SCRIPT : "") + (feedback ? feedbackOverlayScripts(page.id) : "");
23554
25587
  const headers2 = {
23555
25588
  "Content-Type": "text/html; charset=utf-8",
23556
25589
  "Cache-Control": "no-store",
@@ -23682,6 +25715,7 @@ async function fetchAllBotChannels(client) {
23682
25715
  // src/http/poll.ts
23683
25716
  init_telemetry_trigger();
23684
25717
  init_types();
25718
+ init_request_auth_context();
23685
25719
  init_runtime_pools_flag();
23686
25720
  function buildBudgetRefusedTrigger(refusal) {
23687
25721
  const trigger = {
@@ -23709,21 +25743,11 @@ var PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
23709
25743
  mimeType: true,
23710
25744
  sizeBytes: true
23711
25745
  });
23712
- var PollRequestedBySchema = UserSchema.pick({
23713
- id: true,
23714
- name: true,
23715
- email: true,
23716
- role: true,
23717
- notes: true
23718
- }).extend({
23719
- id: exports_external.string().optional(),
23720
- comms: UserCommsPrefsSchema.optional()
23721
- });
23722
25746
  var PollTaskOfferedTriggerSchema = exports_external.object({
23723
25747
  type: exports_external.literal("task_offered"),
23724
25748
  taskId: exports_external.string(),
23725
25749
  task: AgentTaskSchema,
23726
- requestedBy: PollRequestedBySchema.optional()
25750
+ requestedBy: TaskRequestedBySchema.optional()
23727
25751
  });
23728
25752
  var PollTaskAssignedTriggerSchema = exports_external.object({
23729
25753
  type: exports_external.literal("task_assigned"),
@@ -23732,7 +25756,7 @@ var PollTaskAssignedTriggerSchema = exports_external.object({
23732
25756
  task: AgentTaskSchema.extend({
23733
25757
  attachments: exports_external.array(PollTriggerAttachmentSchema)
23734
25758
  }),
23735
- requestedBy: PollRequestedBySchema.optional()
25759
+ requestedBy: TaskRequestedBySchema.optional()
23736
25760
  });
23737
25761
  var PollUnreadMentionsTriggerSchema = exports_external.object({
23738
25762
  type: exports_external.literal("unread_mentions"),
@@ -23780,26 +25804,6 @@ var pollTriggers = route({
23780
25804
  });
23781
25805
  var CHANNEL_ACTIVITY_INTERVAL_MS = 60000;
23782
25806
  var lastChannelActivityCheckAt = 0;
23783
- function getRequesterNotes(notes) {
23784
- return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
23785
- }
23786
- async function buildTriggerRequestedBy(task) {
23787
- const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
23788
- if (user) {
23789
- return {
23790
- id: user.id,
23791
- name: user.name,
23792
- email: user.email,
23793
- role: user.role,
23794
- notes: getRequesterNotes(user.notes),
23795
- comms: getUserCommsPrefs(user)
23796
- };
23797
- }
23798
- if (task.slackUserId) {
23799
- return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
23800
- }
23801
- return;
23802
- }
23803
25807
  async function attachmentsForTrigger(taskId) {
23804
25808
  return (await getTaskAttachments(taskId)).map((a) => ({
23805
25809
  id: a.id,
@@ -23808,12 +25812,25 @@ async function attachmentsForTrigger(taskId) {
23808
25812
  sizeBytes: a.sizeBytes
23809
25813
  }));
23810
25814
  }
23811
- async function claimModelFields(task, agent) {
25815
+ async function claimModelFields(task, agent, auth) {
25816
+ const pin = taskModelPin(task);
25817
+ const cleared = {
25818
+ model: pin,
25819
+ resolvedModel: undefined,
25820
+ modelSource: undefined,
25821
+ modelAlias: undefined,
25822
+ resolvedProfile: undefined
25823
+ };
23812
25824
  try {
23813
- return await recordClaimModelResolution(task, agent);
25825
+ return {
25826
+ ...cleared,
25827
+ ...await recordClaimModelResolution({ ...task, model: pin }, agent, {
25828
+ includeRepoSecrets: readsRepoSecrets(auth, agent)
25829
+ })
25830
+ };
23814
25831
  } catch (error) {
23815
25832
  console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
23816
- return {};
25833
+ return cleared;
23817
25834
  }
23818
25835
  }
23819
25836
  var commitCursorsRoute = route({
@@ -23898,7 +25915,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
23898
25915
  const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
23899
25916
  if (claimedTask) {
23900
25917
  await recordRuntimeTask(claimedTask.id);
23901
- const offeredRequestedBy = await buildTriggerRequestedBy(claimedTask);
25918
+ const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
23902
25919
  return {
23903
25920
  trigger: {
23904
25921
  type: "task_offered",
@@ -23978,7 +25995,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
23978
25995
  agentId: myAgentId
23979
25996
  });
23980
25997
  });
23981
- const assignedRequestedBy = await buildTriggerRequestedBy(pendingTask);
25998
+ const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
23982
25999
  const assignedTriggerSurface = await resolveTriggerSurface(pendingTask.id, pendingTask.source);
23983
26000
  return {
23984
26001
  trigger: {
@@ -23987,7 +26004,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
23987
26004
  triggerSurface: assignedTriggerSurface,
23988
26005
  task: {
23989
26006
  ...pendingTask,
23990
- ...await claimModelFields(pendingTask, agent),
26007
+ ...await claimModelFields(pendingTask, agent, getRequestAuth(req)),
23991
26008
  status: "in_progress",
23992
26009
  attachments: await attachmentsForTrigger(pendingTask.id)
23993
26010
  },
@@ -24069,7 +26086,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
24069
26086
  agentId: myAgentId
24070
26087
  });
24071
26088
  });
24072
- const claimedRequestedBy = await buildTriggerRequestedBy(claimed);
26089
+ const claimedRequestedBy = await buildTaskRequestedBy(claimed);
24073
26090
  const claimedTriggerSurface = await resolveTriggerSurface(claimed.id, claimed.source);
24074
26091
  return {
24075
26092
  trigger: {
@@ -24078,7 +26095,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
24078
26095
  triggerSurface: claimedTriggerSurface,
24079
26096
  task: {
24080
26097
  ...claimed,
24081
- ...await claimModelFields(claimed, agent),
26098
+ ...await claimModelFields(claimed, agent, getRequestAuth(req)),
24082
26099
  attachments: await attachmentsForTrigger(claimed.id)
24083
26100
  },
24084
26101
  ...claimedRequestedBy && { requestedBy: claimedRequestedBy }
@@ -28984,7 +31001,7 @@ async function handleScriptRuns(req, res, pathSegments, queryParams, agentId) {
28984
31001
  return true;
28985
31002
  const result = await executeRawLlm(parsed.body);
28986
31003
  if (result.status === "failed") {
28987
- json(res, { error: result.error }, 500);
31004
+ jsonError(res, result.error, 500);
28988
31005
  return true;
28989
31006
  }
28990
31007
  rawLlmRoute.respond(res, 200, result.output);
@@ -29188,7 +31205,8 @@ var upsertRoute2 = route({
29188
31205
  schema: exports_external.object({
29189
31206
  name: exports_external.string(),
29190
31207
  version: exports_external.number(),
29191
- contentDeduped: exports_external.boolean()
31208
+ contentDeduped: exports_external.boolean(),
31209
+ warnings: exports_external.array(exports_external.string()).optional().describe("Advisory quickjs-executor compatibility findings. Never blocks the save.")
29192
31210
  })
29193
31211
  },
29194
31212
  400: { description: "Validation or typecheck failure" },
@@ -29348,7 +31366,12 @@ var createScriptApiRoute = route({
29348
31366
  params: idParamsSchema4,
29349
31367
  body: createScriptApiBodySchema,
29350
31368
  responses: {
29351
- 201: { description: "Endpoint created", schema: scriptApiWithSecretSchema },
31369
+ 201: {
31370
+ description: "Endpoint created",
31371
+ schema: scriptApiWithSecretSchema.extend({
31372
+ warnings: exports_external.array(exports_external.string()).optional().describe("Advisory quickjs-executor compatibility findings for the script.")
31373
+ })
31374
+ },
29352
31375
  400: { description: "Validation error or script has no owning agent" },
29353
31376
  404: { description: "Script not found" }
29354
31377
  },
@@ -29557,10 +31580,12 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
29557
31580
  isPromotion: Boolean(existingAgentScript)
29558
31581
  });
29559
31582
  }
31583
+ const warnings = quickJSCompatWarnings(checkQuickJSCompat(parsed.body.source, { fsMode: parsed.body.fsMode }));
29560
31584
  upsertRoute2.respond(res, 200, {
29561
31585
  name: result.script.name,
29562
31586
  version: result.script.version,
29563
- contentDeduped: result.contentDeduped
31587
+ contentDeduped: result.contentDeduped,
31588
+ ...warnings.length > 0 ? { warnings } : {}
29564
31589
  });
29565
31590
  return true;
29566
31591
  }
@@ -29605,6 +31630,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
29605
31630
  runtimeInstanceId,
29606
31631
  egressSecrets: credentials.egressSecrets,
29607
31632
  failedBindings: credentials.failedBindings,
31633
+ userConfig: credentials.userConfig,
29608
31634
  apiConnections: getScriptApiConnectionDescriptors({ agentId: agent.id }),
29609
31635
  mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agent.id })
29610
31636
  });
@@ -29856,7 +31882,11 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
29856
31882
  label: parsed.body.label ?? null,
29857
31883
  createdBy: await resolveHttpAuditUserId(req, agentId)
29858
31884
  });
29859
- createScriptApiRoute.respond(res, 201, endpoint);
31885
+ const warnings = quickJSCompatWarnings(checkQuickJSCompat(script.source, { fsMode: script.fsMode }));
31886
+ createScriptApiRoute.respond(res, 201, {
31887
+ ...endpoint,
31888
+ ...warnings.length > 0 ? { warnings } : {}
31889
+ });
29860
31890
  return true;
29861
31891
  }
29862
31892
  if (listScriptApisRoute.match(req.method, pathSegments)) {
@@ -32101,21 +34131,6 @@ var getMetrics = route({
32101
34131
  200: { description: "Swarm metrics counts", schema: SwarmMetricsSchema }
32102
34132
  }
32103
34133
  });
32104
- var listServices = route({
32105
- method: "get",
32106
- path: "/api/services",
32107
- pattern: ["api", "services"],
32108
- summary: "List all registered services",
32109
- tags: ["Stats"],
32110
- query: exports_external.object({
32111
- status: exports_external.string().optional(),
32112
- agentId: exports_external.string().optional(),
32113
- name: exports_external.string().optional()
32114
- }),
32115
- responses: {
32116
- 200: { description: "Service list", schema: exports_external.object({ services: exports_external.array(ServiceSchema) }) }
32117
- }
32118
- });
32119
34134
  var listScheduledTasks = route({
32120
34135
  method: "get",
32121
34136
  path: "/api/scheduled-tasks",
@@ -32201,18 +34216,6 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
32201
34216
  });
32202
34217
  return true;
32203
34218
  }
32204
- if (listServices.match(req.method, pathSegments)) {
32205
- const parsed = await listServices.parse(req, res, pathSegments, queryParams);
32206
- if (!parsed)
32207
- return true;
32208
- const services = await getAllServices({
32209
- status: parsed.query.status || undefined,
32210
- agentId: parsed.query.agentId || undefined,
32211
- name: parsed.query.name || undefined
32212
- });
32213
- listServices.respond(res, 200, { services });
32214
- return true;
32215
- }
32216
34219
  if (listScheduledTasks.match(req.method, pathSegments)) {
32217
34220
  const parsed = await listScheduledTasks.parse(req, res, pathSegments, queryParams);
32218
34221
  if (!parsed)
@@ -34358,6 +36361,8 @@ async function routeKapsoInbound(payload) {
34358
36361
  }
34359
36362
 
34360
36363
  // src/http/webhooks.ts
36364
+ init_constants();
36365
+ init_secret_scrubber();
34361
36366
  init_event_bus();
34362
36367
  var WebhookDispatchResultSchema = exports_external.object({
34363
36368
  created: exports_external.boolean(),
@@ -34453,6 +36458,57 @@ var kapsoWebhook = route({
34453
36458
  503: { description: "Kapso integration not configured" }
34454
36459
  }
34455
36460
  });
36461
+ var chatSdkWebhook = route({
36462
+ method: "post",
36463
+ path: "/api/integrations/chat/{adapter}/webhook",
36464
+ pattern: ["api", "integrations", "chat", null, "webhook"],
36465
+ summary: "Handle Chat SDK platform webhooks (Telegram, Slack, Discord, Teams)",
36466
+ tags: ["Webhooks"],
36467
+ params: exports_external.object({ adapter: exports_external.string().describe("Chat SDK adapter name, e.g. telegram") }),
36468
+ auth: { apiKey: false },
36469
+ rbac: {
36470
+ ungated: "provider webhook: the Chat SDK adapter verifies the platform signature (Telegram secret token, Slack signing secret, Discord Ed25519, Teams JWT)"
36471
+ },
36472
+ responses: {
36473
+ 200: { description: "Adapter response", unstructured: "adapter response proxied as-is" },
36474
+ 401: { description: "Invalid platform signature or secret token" },
36475
+ 403: { description: "Teams activity whose serviceUrl is not a Bot Framework host" },
36476
+ 404: { description: "Chat SDK disabled or adapter not configured" },
36477
+ 413: { description: "Body exceeds 1 MiB" },
36478
+ 503: { description: "Adapter configured but failed to start" }
36479
+ }
36480
+ });
36481
+ var CHAT_SDK_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
36482
+ function hasTrustedTeamsServiceUrl(body) {
36483
+ let serviceUrl;
36484
+ try {
36485
+ serviceUrl = JSON.parse(body.toString("utf-8"))?.serviceUrl;
36486
+ } catch {
36487
+ return true;
36488
+ }
36489
+ if (serviceUrl === undefined || serviceUrl === null || serviceUrl === "")
36490
+ return true;
36491
+ return typeof serviceUrl === "string" && isTrustedTeamsServiceUrl(serviceUrl);
36492
+ }
36493
+ function toFetchRequest(req, rawBody, fallbackPath) {
36494
+ const headers2 = new Headers;
36495
+ if (Array.isArray(req.rawHeaders) && req.rawHeaders.length > 0) {
36496
+ for (let i = 0;i + 1 < req.rawHeaders.length; i += 2) {
36497
+ headers2.append(req.rawHeaders[i], req.rawHeaders[i + 1]);
36498
+ }
36499
+ } else {
36500
+ for (const [name, value] of Object.entries(req.headers)) {
36501
+ for (const item of Array.isArray(value) ? value : value === undefined ? [] : [value]) {
36502
+ headers2.append(name, item);
36503
+ }
36504
+ }
36505
+ }
36506
+ return new Request(`${getPublicMcpBaseUrl()}${req.url ?? fallbackPath}`, {
36507
+ method: req.method ?? "POST",
36508
+ headers: headers2,
36509
+ body: rawBody.length > 0 ? new Uint8Array(rawBody) : undefined
36510
+ });
36511
+ }
34456
36512
  function logKapsoAckResult(action, result) {
34457
36513
  if (result.ok)
34458
36514
  return;
@@ -34536,7 +36592,8 @@ async function handleWebhooks(req, res, pathSegments) {
34536
36592
  res.end(JSON.stringify({ error: "Invalid JSON body" }));
34537
36593
  return true;
34538
36594
  }
34539
- console.log(`[GitHub] Received ${eventType} event`);
36595
+ const deliveryId = req.headers["x-github-delivery"];
36596
+ console.log(`[GitHub] Received ${eventType} event (delivery ${deliveryId ?? "unknown"})`);
34540
36597
  let result = { created: false };
34541
36598
  try {
34542
36599
  result = await runBlockableHandler("GitHub", async () => {
@@ -34610,13 +36667,13 @@ async function handleWebhooks(req, res, pathSegments) {
34610
36667
  }
34611
36668
  githubWebhook.respond(res, 200, result);
34612
36669
  } catch (err) {
34613
- const errorMessage2 = err instanceof Error ? err.message : String(err);
34614
- console.error(`[GitHub] Error handling ${eventType} event: ${errorMessage2}`);
36670
+ const errorMessage3 = err instanceof Error ? err.message : String(err);
36671
+ console.error(`[GitHub] Error handling ${eventType} event: ${errorMessage3}`);
34615
36672
  if (err instanceof Error && err.stack) {
34616
36673
  console.error(err.stack);
34617
36674
  }
34618
36675
  res.writeHead(500, { "Content-Type": "application/json" });
34619
- res.end(JSON.stringify({ error: "Internal server error", message: errorMessage2 }));
36676
+ res.end(JSON.stringify({ error: "Internal server error", message: errorMessage3 }));
34620
36677
  }
34621
36678
  return true;
34622
36679
  }
@@ -34724,8 +36781,8 @@ async function handleWebhooks(req, res, pathSegments) {
34724
36781
  }
34725
36782
  gitlabWebhook.respond(res, 200, result);
34726
36783
  } catch (err) {
34727
- const errorMessage2 = err instanceof Error ? err.message : String(err);
34728
- console.error(`[GitLab] Error handling ${objectKind} event: ${errorMessage2}`);
36784
+ const errorMessage3 = err instanceof Error ? err.message : String(err);
36785
+ console.error(`[GitLab] Error handling ${objectKind} event: ${errorMessage3}`);
34729
36786
  if (err instanceof Error && err.stack) {
34730
36787
  console.error(err.stack);
34731
36788
  }
@@ -34815,8 +36872,8 @@ async function handleWebhooks(req, res, pathSegments) {
34815
36872
  }
34816
36873
  azureDevOpsWebhook.respond(res, 200, result);
34817
36874
  } catch (err) {
34818
- const errorMessage2 = err instanceof Error ? err.message : String(err);
34819
- console.error(`[AzureDevOps] Error handling ${eventType} event: ${errorMessage2}`);
36875
+ const errorMessage3 = err instanceof Error ? err.message : String(err);
36876
+ console.error(`[AzureDevOps] Error handling ${eventType} event: ${errorMessage3}`);
34820
36877
  if (err instanceof Error && err.stack) {
34821
36878
  console.error(err.stack);
34822
36879
  }
@@ -34861,8 +36918,7 @@ async function handleWebhooks(req, res, pathSegments) {
34861
36918
  }
34862
36919
  agentmailWebhook.respond(res, 200, { received: true });
34863
36920
  if (payload.message && !isInboxAllowed(payload.message.inbox_id, process.env.AGENTMAIL_INBOX_DOMAIN_FILTER)) {
34864
- const domain = payload.message.inbox_id.split("@")[1] ?? "unknown";
34865
- console.log(`[AgentMail] Ignoring event for inbox domain "${domain}" (not in AGENTMAIL_INBOX_DOMAIN_FILTER)`);
36921
+ console.log(`[AgentMail] Ignoring event for inbox "${payload.message.inbox_id}" (not in AGENTMAIL_INBOX_DOMAIN_FILTER)`);
34866
36922
  return true;
34867
36923
  }
34868
36924
  if (payload.message && !isSenderAllowed(payload.message.from_, process.env.AGENTMAIL_SENDER_DOMAIN_FILTER)) {
@@ -34884,8 +36940,8 @@ async function handleWebhooks(req, res, pathSegments) {
34884
36940
  console.log(`[AgentMail] Ignoring event type: ${payload.event_type}`);
34885
36941
  }
34886
36942
  } catch (err) {
34887
- const errorMessage2 = err instanceof Error ? err.message : String(err);
34888
- console.error(`[AgentMail] Error handling ${payload.event_type} event: ${errorMessage2}`);
36943
+ const errorMessage3 = err instanceof Error ? err.message : String(err);
36944
+ console.error(`[AgentMail] Error handling ${payload.event_type} event: ${errorMessage3}`);
34889
36945
  if (err instanceof Error && err.stack) {
34890
36946
  console.error(err.stack);
34891
36947
  }
@@ -34945,16 +37001,84 @@ async function handleWebhooks(req, res, pathSegments) {
34945
37001
  }
34946
37002
  kapsoWebhook.respond(res, 200, { received: true, routing: routing.kind });
34947
37003
  } catch (err) {
34948
- const errorMessage2 = err instanceof Error ? err.message : String(err);
37004
+ const errorMessage3 = err instanceof Error ? err.message : String(err);
34949
37005
  if (err instanceof WebhookError) {
34950
- console.warn(`[Kapso] Workflow dispatch rejected: ${errorMessage2}`);
37006
+ console.warn(`[Kapso] Workflow dispatch rejected: ${errorMessage3}`);
34951
37007
  } else {
34952
- console.error(`[Kapso] Error handling inbound event: ${errorMessage2}`);
37008
+ console.error(`[Kapso] Error handling inbound event: ${errorMessage3}`);
34953
37009
  }
34954
37010
  kapsoWebhook.respond(res, 200, { received: true, routing: "error" });
34955
37011
  }
34956
37012
  return true;
34957
37013
  }
37014
+ if (chatSdkWebhook.match(req.method, pathSegments)) {
37015
+ const adapterName = pathSegments[3] ?? "";
37016
+ if (!isChatSdkEnabled() || !isChatAdapterConfigured(adapterName)) {
37017
+ res.writeHead(404, { "Content-Type": "application/json" });
37018
+ res.end(JSON.stringify({ error: "Chat SDK adapter not configured" }));
37019
+ return true;
37020
+ }
37021
+ if (isChatSdkStopping()) {
37022
+ res.writeHead(503, { "Content-Type": "application/json" });
37023
+ res.end(JSON.stringify({ error: "Chat SDK is shutting down" }));
37024
+ return true;
37025
+ }
37026
+ const chat = getChatInstance(adapterName);
37027
+ const handler = chat?.webhooks[adapterName];
37028
+ if (!handler) {
37029
+ res.writeHead(503, { "Content-Type": "application/json" });
37030
+ res.end(JSON.stringify({ error: "Chat SDK adapter failed to start" }));
37031
+ return true;
37032
+ }
37033
+ if (enforceContentLengthCap(req, res, CHAT_SDK_WEBHOOK_MAX_BODY_BYTES) === BODY_TOO_LARGE) {
37034
+ return true;
37035
+ }
37036
+ const chunks = [];
37037
+ let receivedBytes = 0;
37038
+ let tooLarge = false;
37039
+ for await (const chunk of req) {
37040
+ if (tooLarge)
37041
+ continue;
37042
+ receivedBytes += chunk.byteLength;
37043
+ if (receivedBytes > CHAT_SDK_WEBHOOK_MAX_BODY_BYTES) {
37044
+ tooLarge = true;
37045
+ chunks.length = 0;
37046
+ jsonError(res, `Payload too large (max ${CHAT_SDK_WEBHOOK_MAX_BODY_BYTES} bytes)`, 413);
37047
+ continue;
37048
+ }
37049
+ chunks.push(chunk);
37050
+ }
37051
+ if (tooLarge)
37052
+ return true;
37053
+ const rawBody = Buffer.concat(chunks);
37054
+ if (adapterName === "teams" && !hasTrustedTeamsServiceUrl(rawBody)) {
37055
+ console.warn("[chat-sdk] teams webhook refused: serviceUrl is not a Bot Framework host");
37056
+ res.writeHead(403, { "Content-Type": "application/json" });
37057
+ res.end(JSON.stringify({ error: "Untrusted Teams serviceUrl" }));
37058
+ return true;
37059
+ }
37060
+ const request = toFetchRequest(req, rawBody, `/api/integrations/chat/${adapterName}/webhook`);
37061
+ try {
37062
+ const response = await handler(request, {
37063
+ waitUntil: trackBackground
37064
+ });
37065
+ const responseHeaders = {};
37066
+ response.headers.forEach((value, name) => {
37067
+ responseHeaders[name] = value;
37068
+ });
37069
+ const body = Buffer.from(await response.arrayBuffer());
37070
+ res.writeHead(response.status, responseHeaders);
37071
+ res.end(body);
37072
+ } catch (err) {
37073
+ const message = err instanceof Error ? err.message : String(err);
37074
+ console.error(`[chat-sdk] ${adapterName} webhook failed: ${scrubSecrets(message)}`);
37075
+ if (!res.headersSent) {
37076
+ res.writeHead(500, { "Content-Type": "application/json" });
37077
+ }
37078
+ res.end(JSON.stringify({ error: "Chat SDK webhook failed" }));
37079
+ }
37080
+ return true;
37081
+ }
34958
37082
  return false;
34959
37083
  }
34960
37084
 
@@ -35977,6 +38101,7 @@ async function handleX(req, res, pathSegments) {
35977
38101
  timeoutMs,
35978
38102
  egressSecrets: credentials.egressSecrets,
35979
38103
  failedBindings: credentials.failedBindings,
38104
+ userConfig: credentials.userConfig,
35980
38105
  apiConnections: getScriptApiConnectionDescriptors({ agentId: endpoint.agentId }),
35981
38106
  mcpConnections: getScriptMcpConnectionDescriptors({ agentId: endpoint.agentId })
35982
38107
  });
@@ -36125,7 +38250,6 @@ var httpServer = createHttpServer(async (req, res) => {
36125
38250
  () => handleAgentRegister(req, res, pathSegments, myAgentId),
36126
38251
  () => handlePoll(req, res, pathSegments, queryParams, myAgentId),
36127
38252
  () => handleSessionData(req, res, pathSegments, queryParams, myAgentId),
36128
- () => handleEcosystem(req, res, pathSegments, myAgentId),
36129
38253
  () => handleTrackers(req, res, pathSegments),
36130
38254
  () => handleWebhooks(req, res, pathSegments),
36131
38255
  () => handleAgentsRest(req, res, pathSegments, queryParams, myAgentId),
@@ -36157,6 +38281,7 @@ var httpServer = createHttpServer(async (req, res) => {
36157
38281
  () => handleDbQuery(req, res, pathSegments, queryParams),
36158
38282
  () => handleMetrics(req, res, pathSegments, queryParams, myAgentId),
36159
38283
  () => handleModelsCatalog(req, res, pathSegments, queryParams),
38284
+ () => handleModelProfiles(req, res, pathSegments, queryParams, myAgentId),
36160
38285
  () => handleRepos(req, res, pathSegments, queryParams, myAgentId),
36161
38286
  () => handleSkills(req, res, pathSegments, queryParams, myAgentId),
36162
38287
  () => handleScriptConnections(req, res, pathSegments, queryParams, myAgentId),
@@ -36217,7 +38342,7 @@ var httpServer = createHttpServer(async (req, res) => {
36217
38342
  });
36218
38343
  });
36219
38344
  var detachRealtime = attachRealtimeTransport(httpServer);
36220
- var { attachControllerTransport } = await import("./controllers-connect-3mf2btyj.js");
38345
+ var { attachControllerTransport } = await import("./controllers-connect-v9yn8zb9.js");
36221
38346
  var detachControllers = attachControllerTransport(httpServer);
36222
38347
  var stopWorkerNudges = startWorkerNudgeListener();
36223
38348
  var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
@@ -36246,7 +38371,7 @@ async function shutdown() {
36246
38371
  sessionsProcessed: getServerSessionsProcessed()
36247
38372
  });
36248
38373
  if (hasCapability("scheduling")) {
36249
- const { stopScheduler } = await import("./index-fx3jqq1e.js");
38374
+ const { stopScheduler } = await import("./index-3gtmkhc6.js");
36250
38375
  stopScheduler();
36251
38376
  }
36252
38377
  stopHeartbeat();
@@ -36256,8 +38381,9 @@ async function shutdown() {
36256
38381
  await drainApi();
36257
38382
  await stopScriptRunSupervisor();
36258
38383
  await stopSlackApp();
38384
+ await stopChatSdk();
36259
38385
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
36260
- const { stopOAuthKeepalive } = await import("./keepalive-6ywhpja1.js");
38386
+ const { stopOAuthKeepalive } = await import("./keepalive-psj6j54c.js");
36261
38387
  await stopOAuthKeepalive();
36262
38388
  }
36263
38389
  stopOAuthPendingGc();
@@ -36337,15 +38463,21 @@ try {
36337
38463
  console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
36338
38464
  }
36339
38465
  try {
36340
- const { ensurePricingSeeded } = await import("./boot-seeds-d8sp49t2.js");
38466
+ const { ensurePricingSeeded } = await import("./boot-seeds-e9sta0yt.js");
36341
38467
  ensurePricingSeeded();
36342
- const { startPricingRefreshLoop } = await import("./pricing-refresh-gedkjdw0.js");
38468
+ const { startPricingRefreshLoop } = await import("./pricing-refresh-kefx9v3t.js");
36343
38469
  startPricingRefreshLoop();
36344
38470
  } catch (err) {
36345
38471
  console.error("[startup] Failed to seed pricing rows:", err);
36346
38472
  }
36347
38473
  try {
36348
- const { ensureRbacSeeded } = await import("./boot-seeds-d8sp49t2.js");
38474
+ const { ensureModelProfilesSeeded } = await import("./boot-seeds-e9sta0yt.js");
38475
+ await ensureModelProfilesSeeded();
38476
+ } catch (err) {
38477
+ console.error("[startup] Failed to seed model profiles:", err);
38478
+ }
38479
+ try {
38480
+ const { ensureRbacSeeded } = await import("./boot-seeds-e9sta0yt.js");
36349
38481
  ensureRbacSeeded();
36350
38482
  } catch (err) {
36351
38483
  console.error("[startup] Failed to sync RBAC seed rows:", err);
@@ -36353,7 +38485,7 @@ try {
36353
38485
  throw err;
36354
38486
  }
36355
38487
  try {
36356
- const { runAllSeeders } = await import("./index-2ybk7khy.js");
38488
+ const { runAllSeeders } = await import("./index-5wyfjc2c.js");
36357
38489
  await runAllSeeders({ scriptEmbeddingMode: "skip" });
36358
38490
  } catch (err) {
36359
38491
  console.error("[startup] Failed to seed built-in entities:", err);
@@ -36401,49 +38533,53 @@ httpServer.listen(port, async () => {
36401
38533
  } catch (err) {
36402
38534
  console.error("[Slack] Failed to start, continuing boot without Slack:", err);
36403
38535
  }
38536
+ startChatSdk().catch((err) => {
38537
+ console.error("[chat-sdk] Failed to start, continuing without Chat SDK:", scrubSecrets(err instanceof Error ? err.message : String(err)));
38538
+ });
36404
38539
  startQueueStallAlarm();
36405
38540
  initGitHub();
36406
38541
  initGitLab();
36407
38542
  initAzureDevOps();
38543
+ initAzureDevOpsOutbound();
36408
38544
  initAgentMail();
36409
38545
  await initLinear();
36410
38546
  await initJira();
36411
38547
  await initWorkflows();
36412
38548
  await startScriptRunSupervisor(getMcpBaseUrl());
36413
38549
  if (hasCapability("scheduling")) {
36414
- const { startScheduler } = await import("./index-fx3jqq1e.js");
36415
- const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-g2byveez.js");
38550
+ const { startScheduler } = await import("./index-3gtmkhc6.js");
38551
+ const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-5e101240.js");
36416
38552
  const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
36417
38553
  startScheduler(getExecutorRegistry2(), intervalMs, {
36418
38554
  runId: globalState.__runId
36419
38555
  });
36420
38556
  }
36421
38557
  if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
36422
- const { startHeartbeat } = await import("./index-bdsqa799.js");
38558
+ const { startHeartbeat } = await import("./index-3k5hkz7r.js");
36423
38559
  const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
36424
38560
  startHeartbeat(heartbeatMs);
36425
38561
  }
36426
38562
  startPoolRouting();
36427
38563
  startRuntimeSupervisor();
36428
38564
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
36429
- const { startOAuthKeepalive } = await import("./keepalive-6ywhpja1.js");
38565
+ const { startOAuthKeepalive } = await import("./keepalive-psj6j54c.js");
36430
38566
  startOAuthKeepalive();
36431
38567
  }
36432
38568
  if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
36433
- const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-3c6f8vyy.js");
38569
+ const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-0284h93b.js");
36434
38570
  startOAuthRefreshSweep();
36435
38571
  }
36436
38572
  startOAuthPendingGc();
36437
38573
  await startMemoryGc();
36438
38574
  await startDbRetention();
36439
38575
  startHumanFreeDrain();
36440
- import("./boot-reembed-swwzdgd7.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
38576
+ import("./boot-reembed-whzbdthj.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
36441
38577
  console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
36442
38578
  });
36443
- import("./boot-reembed-16rv9843.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
38579
+ import("./boot-reembed-9apm9qw6.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
36444
38580
  console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
36445
38581
  });
36446
- import("./boot-scrub-sweep-zkr6vkqb.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
38582
+ import("./boot-scrub-sweep-ndzmvmgt.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
36447
38583
  console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
36448
38584
  });
36449
38585
  }).on("error", (err) => {