@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
@@ -0,0 +1,303 @@
1
+ // Model profiles CRUD (runbooks/model-tiers.md § Model profiles).
2
+ //
3
+ // A profile is a named harness + model + optional effort. Lead picks one per
4
+ // task (`profile` on send-task / POST /api/tasks) or sets it as an agent's
5
+ // default (PATCH /api/agents/{id}/runtime { profile }); claim resolves it.
6
+ // Reads are open to every caller; writes are operator-only (`models.profiles.write`).
7
+
8
+ import type { IncomingMessage, ServerResponse } from "node:http";
9
+ import { isAlias } from "@desplega/model-catalog";
10
+ import { z } from "zod";
11
+ import { resolveHttpAuditUserId } from "../be/audit-user";
12
+ import {
13
+ createModelProfile,
14
+ deleteModelProfile,
15
+ getModelProfileByName,
16
+ listModelProfiles,
17
+ modelProfileReferences,
18
+ updateModelProfile,
19
+ } from "../be/db/model-profiles";
20
+ import { explicitModelError } from "../be/model-validation";
21
+ import { supportedReasoningLevels } from "../providers/reasoning-effort";
22
+ import { can } from "../rbac";
23
+ import {
24
+ ModelProfileNameSchema,
25
+ ModelProfileSchema,
26
+ type ProviderName,
27
+ ProviderNameSchema,
28
+ type ReasoningEffort,
29
+ ReasoningEffortSchema,
30
+ } from "../types";
31
+ import { agentFirstPrincipal } from "./request-principal";
32
+ import { route } from "./route-def";
33
+ import { jsonError } from "./utils";
34
+
35
+ const listProfiles = route({
36
+ method: "get",
37
+ path: "/api/model-profiles",
38
+ pattern: ["api", "model-profiles"],
39
+ summary: "List model profiles",
40
+ description:
41
+ "Every model profile, ordered by harness then name. Filter by `harness` to see what an agent on that harness can run.",
42
+ tags: ["Models"],
43
+ query: z.object({ harness: ProviderNameSchema.optional() }),
44
+ responses: {
45
+ 200: {
46
+ description: "Model profiles",
47
+ schema: z.object({ profiles: z.array(ModelProfileSchema) }),
48
+ },
49
+ },
50
+ });
51
+
52
+ const getProfile = route({
53
+ method: "get",
54
+ path: "/api/model-profiles/{name}",
55
+ pattern: ["api", "model-profiles", null],
56
+ summary: "Get one model profile",
57
+ tags: ["Models"],
58
+ params: z.object({ name: z.string() }),
59
+ responses: {
60
+ 200: { description: "The model profile", schema: ModelProfileSchema },
61
+ 404: { description: "No profile with that name" },
62
+ },
63
+ });
64
+
65
+ const createProfile = route({
66
+ method: "post",
67
+ path: "/api/model-profiles",
68
+ pattern: ["api", "model-profiles"],
69
+ summary: "Create a model profile",
70
+ description:
71
+ "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).",
72
+ tags: ["Models"],
73
+ rbac: { permission: "models.profiles.write" },
74
+ body: z.object({
75
+ name: ModelProfileNameSchema,
76
+ harness: ProviderNameSchema,
77
+ model: z.string().trim().min(1),
78
+ effort: ReasoningEffortSchema.nullable().optional(),
79
+ allowCustomModel: z.boolean().optional().default(false),
80
+ description: z.string().trim().max(500).nullable().optional(),
81
+ }),
82
+ responses: {
83
+ 201: { description: "Profile created", schema: ModelProfileSchema },
84
+ 400: { description: "Validation error" },
85
+ 403: { description: "Forbidden" },
86
+ 409: { description: "A profile with that name exists" },
87
+ },
88
+ });
89
+
90
+ const updateProfile = route({
91
+ method: "patch",
92
+ path: "/api/model-profiles/{name}",
93
+ pattern: ["api", "model-profiles", null],
94
+ summary: "Update a model profile",
95
+ description:
96
+ "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).",
97
+ tags: ["Models"],
98
+ rbac: { permission: "models.profiles.write" },
99
+ params: z.object({ name: z.string() }),
100
+ body: z.object({
101
+ model: z.string().trim().min(1).optional(),
102
+ effort: ReasoningEffortSchema.nullable().optional(),
103
+ allowCustomModel: z.boolean().optional(),
104
+ description: z.string().trim().max(500).nullable().optional(),
105
+ }),
106
+ responses: {
107
+ 200: { description: "Profile updated", schema: ModelProfileSchema },
108
+ 400: { description: "Validation error" },
109
+ 403: { description: "Forbidden" },
110
+ 404: { description: "No profile with that name" },
111
+ },
112
+ });
113
+
114
+ const deleteProfile = route({
115
+ method: "delete",
116
+ path: "/api/model-profiles/{name}",
117
+ pattern: ["api", "model-profiles", null],
118
+ summary: "Delete a model profile",
119
+ description:
120
+ "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).",
121
+ tags: ["Models"],
122
+ rbac: { permission: "models.profiles.write" },
123
+ params: z.object({ name: z.string() }),
124
+ responses: {
125
+ 200: { description: "Profile deleted", schema: z.object({ deleted: z.literal(true) }) },
126
+ 403: { description: "Forbidden" },
127
+ 404: { description: "No profile with that name" },
128
+ 409: { description: "The profile is a seed or still in use" },
129
+ },
130
+ });
131
+
132
+ /** Why `harness` + `model` + `effort` is not a valid profile, or null. */
133
+ async function profileValueError(input: {
134
+ harness: ProviderName;
135
+ model: string;
136
+ effort: ReasoningEffort | null | undefined;
137
+ allowCustomModel: boolean;
138
+ }): Promise<string | null> {
139
+ const modelError = await explicitModelError({
140
+ model: input.model,
141
+ allowCustomModel: input.allowCustomModel,
142
+ harnessProvider: input.harness,
143
+ });
144
+ if (modelError) return modelError;
145
+ // The levels the harness and model accept (as PATCH /runtime checks them); a
146
+ // supported level may still be a transport no-op, like Opencode `off`. An
147
+ // alias is judged once it resolves.
148
+ if (
149
+ input.effort &&
150
+ !isAlias(input.model) &&
151
+ !supportedReasoningLevels(input.harness, input.model).includes(input.effort)
152
+ ) {
153
+ return `Effort "${input.effort}" is not supported by ${input.model} on ${input.harness}.`;
154
+ }
155
+ return null;
156
+ }
157
+
158
+ /** Gate a write on `models.profiles.write`; writes a 403 and returns false on denial. */
159
+ async function canWriteProfiles(
160
+ req: IncomingMessage,
161
+ res: ServerResponse,
162
+ myAgentId: string | undefined,
163
+ ): Promise<boolean> {
164
+ const decision = can({
165
+ principal: await agentFirstPrincipal(req, myAgentId),
166
+ verb: "models.profiles.write",
167
+ resource: { kind: "none" },
168
+ source: "http",
169
+ });
170
+ if (!decision.allow) {
171
+ jsonError(res, "Writing model profiles requires the operator", 403);
172
+ return false;
173
+ }
174
+ return true;
175
+ }
176
+
177
+ export async function handleModelProfiles(
178
+ req: IncomingMessage,
179
+ res: ServerResponse,
180
+ pathSegments: string[],
181
+ queryParams: URLSearchParams,
182
+ myAgentId: string | undefined,
183
+ ): Promise<boolean> {
184
+ if (listProfiles.match(req.method, pathSegments)) {
185
+ const parsed = await listProfiles.parse(req, res, pathSegments, queryParams);
186
+ if (!parsed) return true;
187
+ listProfiles.respond(res, 200, {
188
+ profiles: await listModelProfiles({ harness: parsed.query.harness }),
189
+ });
190
+ return true;
191
+ }
192
+
193
+ if (getProfile.match(req.method, pathSegments)) {
194
+ const parsed = await getProfile.parse(req, res, pathSegments, queryParams);
195
+ if (!parsed) return true;
196
+ const profile = await getModelProfileByName(parsed.params.name);
197
+ if (!profile) {
198
+ jsonError(res, "Model profile not found", 404);
199
+ return true;
200
+ }
201
+ getProfile.respond(res, 200, profile);
202
+ return true;
203
+ }
204
+
205
+ if (createProfile.match(req.method, pathSegments)) {
206
+ const parsed = await createProfile.parse(req, res, pathSegments, queryParams);
207
+ if (!parsed) return true;
208
+ if (!(await canWriteProfiles(req, res, myAgentId))) return true;
209
+ const body = parsed.body;
210
+ const error = await profileValueError({ ...body, effort: body.effort ?? null });
211
+ if (error) {
212
+ jsonError(res, error, 400);
213
+ return true;
214
+ }
215
+ const created = await createModelProfile(body, {
216
+ userId: await resolveHttpAuditUserId(req, myAgentId),
217
+ });
218
+ if (!created) {
219
+ jsonError(res, `Model profile "${body.name}" already exists`, 409);
220
+ return true;
221
+ }
222
+ createProfile.respond(res, 201, created);
223
+ return true;
224
+ }
225
+
226
+ if (updateProfile.match(req.method, pathSegments)) {
227
+ const parsed = await updateProfile.parse(req, res, pathSegments, queryParams);
228
+ if (!parsed) return true;
229
+ if (!(await canWriteProfiles(req, res, myAgentId))) return true;
230
+ const current = await getModelProfileByName(parsed.params.name);
231
+ if (!current) {
232
+ jsonError(res, "Model profile not found", 404);
233
+ return true;
234
+ }
235
+ const body = parsed.body;
236
+ const error = await profileValueError({
237
+ harness: current.harness,
238
+ model: body.model ?? current.model,
239
+ effort: body.effort === undefined ? current.effort : body.effort,
240
+ allowCustomModel: body.allowCustomModel ?? current.allowCustomModel,
241
+ });
242
+ if (error) {
243
+ jsonError(res, error, 400);
244
+ return true;
245
+ }
246
+ const updated = await updateModelProfile(current.name, body, {
247
+ userId: await resolveHttpAuditUserId(req, myAgentId),
248
+ });
249
+ if (!updated) {
250
+ jsonError(res, "Model profile not found", 404);
251
+ return true;
252
+ }
253
+ updateProfile.respond(res, 200, updated);
254
+ return true;
255
+ }
256
+
257
+ if (deleteProfile.match(req.method, pathSegments)) {
258
+ const parsed = await deleteProfile.parse(req, res, pathSegments, queryParams);
259
+ if (!parsed) return true;
260
+ if (!(await canWriteProfiles(req, res, myAgentId))) return true;
261
+ const current = await getModelProfileByName(parsed.params.name);
262
+ if (!current) {
263
+ jsonError(res, "Model profile not found", 404);
264
+ return true;
265
+ }
266
+ if (current.seededFrom?.startsWith("tier:")) {
267
+ jsonError(
268
+ res,
269
+ `"${current.name}" is a shipped tier profile and comes back at the next boot; edit it instead.`,
270
+ 409,
271
+ );
272
+ return true;
273
+ }
274
+ const refs = await modelProfileReferences(current.name);
275
+ if (refs.agentIds.length > 0 || refs.openTaskCount > 0) {
276
+ jsonError(
277
+ res,
278
+ `"${current.name}" is in use: default of ${refs.agentIds.length} agent(s), held by ${refs.openTaskCount} unfinished or awaiting-resume task(s).`,
279
+ 409,
280
+ );
281
+ return true;
282
+ }
283
+ // The count above is for the message; the delete re-checks in the same
284
+ // statement (migration 220 trigger), so a reference written since is a 409.
285
+ const outcome = await deleteModelProfile(current.name);
286
+ if (outcome === "in-use") {
287
+ jsonError(
288
+ res,
289
+ `"${current.name}" is in use by a task or an agent default created just now.`,
290
+ 409,
291
+ );
292
+ return true;
293
+ }
294
+ if (outcome === "missing") {
295
+ jsonError(res, "Model profile not found", 404);
296
+ return true;
297
+ }
298
+ deleteProfile.respond(res, 200, { deleted: true });
299
+ return true;
300
+ }
301
+
302
+ return false;
303
+ }
@@ -6,10 +6,8 @@ import { route } from "./route-def";
6
6
  import { jsonError } from "./utils";
7
7
 
8
8
  /**
9
- * `/@swarm/api/*` proxy. Cookie-gated equivalent of the artifact-sdk proxy
10
- * (src/artifact-sdk/server.ts:42-69), but lives on the MAIN API server and
11
- * authenticates via the page-session cookie instead of basic-auth + a per-
12
- * artifact tunnel.
9
+ * `/@swarm/api/*` proxy. Lives on the MAIN API server and authenticates via
10
+ * the page-session cookie.
13
11
  *
14
12
  * Flow:
15
13
  * 1. Browser hits `/@swarm/api/<rest>` from inside an iframe of `/p/:id`.
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Scope of THIS module (step-3):
9
9
  * - `auth_mode === 'public'`: ungated. HTML responses inline-inject the
10
- * `BROWSER_SDK_JS` constant from `src/artifact-sdk/browser-sdk.ts` (reused
10
+ * `BROWSER_SDK_JS` constant from `src/pages/browser-sdk.ts` (reused
11
11
  * verbatim — no token-injection hook on the client). SVG responses serve
12
12
  * raw bytes with a restrictive CSP and no injected scripts. JSON responses
13
13
  * 302-redirect to the SPA `/pages/:id` route (the JSON renderer lives
@@ -23,9 +23,13 @@
23
23
  */
24
24
  import type { IncomingMessage, ServerResponse } from "node:http";
25
25
  import { z } from "zod";
26
- import { BROWSER_SDK_JS, SWARM_UI_JS } from "../artifact-sdk/browser-sdk";
27
- import { feedbackOverlayScripts, isPageFeedbackRequested } from "../artifact-sdk/feedback-overlay";
28
26
  import { getPage, incrementPageViewCount } from "../be/db";
27
+ import { BROWSER_SDK_JS, SWARM_UI_JS } from "../pages/browser-sdk";
28
+ import {
29
+ feedbackKeyGuardScript,
30
+ feedbackOverlayScripts,
31
+ isPageFeedbackRequested,
32
+ } from "../pages/feedback-overlay";
29
33
  import { type Page, PageSchema } from "../types";
30
34
  import { getAppUrl, getConfiguredAppUrls } from "../utils/constants";
31
35
  import { getNodeEnv } from "../utils/node-env";
@@ -170,8 +174,8 @@ const PAGE_HEAD_DEFAULTS = `<base target="_blank">
170
174
  }
171
175
  </style>`;
172
176
 
173
- function injectBrowserSdk(html: string): string {
174
- const injection = `${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
177
+ function injectBrowserSdk(html: string, headFirst = ""): string {
178
+ const injection = `${headFirst}${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
175
179
  // Use the first occurrence of `<head>` (case-insensitive). A page that
176
180
  // doesn't have a `<head>` element (raw fragment) still gets the SDK at the
177
181
  // front of the document.
@@ -646,10 +650,12 @@ export async function handlePagesPublic(
646
650
  // text/html — inject SDK + serve. Append the self-print snippet when the
647
651
  // export button requested it, and the feedback overlay when
648
652
  // `?__swarm-feedback` is on (the overlay hides itself in print).
653
+ // The feedback key guard goes first in <head> so it runs before page scripts.
654
+ const feedback = isPageFeedbackRequested(queryParams);
649
655
  const html =
650
- injectBrowserSdk(page.body) +
656
+ injectBrowserSdk(page.body, feedback ? feedbackKeyGuardScript() : "") +
651
657
  (wantsPrint ? PRINT_AUTOTRIGGER_SCRIPT : "") +
652
- (isPageFeedbackRequested(queryParams) ? feedbackOverlayScripts(page.id) : "");
658
+ (feedback ? feedbackOverlayScripts(page.id) : "");
653
659
  const headers: Record<string, string> = {
654
660
  "Content-Type": "text/html; charset=utf-8",
655
661
  "Cache-Control": "no-store",
package/src/http/pages.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  updatePage,
20
20
  withFavoriteFlags,
21
21
  } from "../be/db";
22
+ import { getPageEditCounter } from "../be/db/page-versions";
22
23
  import { snapshotPage } from "../pages/version";
23
24
  import { resolveTemplate } from "../prompts/resolver";
24
25
  // Side-effect import: registers `task.page.feedback`.
@@ -227,7 +228,7 @@ const updatePageRoute = route({
227
228
  /**
228
229
  * POST /api/pages/:id/feedback — turn the element comments collected by the
229
230
  * in-page feedback overlay (`?__swarm-feedback`, see
230
- * `src/artifact-sdk/feedback-overlay.ts`) into one task for the lead. In the
231
+ * `src/pages/feedback-overlay.ts`) into one task for the lead. In the
231
232
  * dashboard the SPA calls it with its bearer for the page it shows. A page
232
233
  * opened directly reaches it through the `/@swarm/api/*` page proxy, which
233
234
  * pins `X-Page-Id` to the cookie's page, so a page can only file feedback on
@@ -482,17 +483,6 @@ function withShareUrls<T extends { id: string; slug: string }>(
482
483
  };
483
484
  }
484
485
 
485
- /**
486
- * Compute the page's "edit counter" — `MAX(page_versions.version) + 1`. Means
487
- * "this is the N-th edit since the page was created". After the first PUT the
488
- * value is 2 (one snapshot row → version 1 → counter becomes 2). This is the
489
- * value POST and PUT return as `version` on the wire.
490
- */
491
- async function pageEditCounter(pageId: string): Promise<number> {
492
- const versions = await getPageVersions(pageId);
493
- return versions.length > 0 ? versions[0]!.version + 1 : 1;
494
- }
495
-
496
486
  /**
497
487
  * Viewer and page-supplied text sits inside a `<page_feedback>` block in the
498
488
  * task prompt. Defuse any tag of that name so the text cannot close the block
@@ -843,7 +833,7 @@ export async function handlePages(
843
833
  updatePageRoute.respond(res, 200, {
844
834
  id: updated.id,
845
835
  key: updated.key,
846
- version: await pageEditCounter(updated.id),
836
+ version: await getPageEditCounter(updated.id),
847
837
  });
848
838
  return true;
849
839
  }
package/src/http/poll.ts CHANGED
@@ -19,7 +19,6 @@ import {
19
19
  getTaskAttachments,
20
20
  getTaskById,
21
21
  getUnassignedTaskIdsForAgent,
22
- getUserById,
23
22
  hasCapacity,
24
23
  isExtensionAgent,
25
24
  recordBudgetRefusalNotification,
@@ -27,7 +26,6 @@ import {
27
26
  updateAgentStatusFromCapacity,
28
27
  upsertChannelActivityCursor,
29
28
  } from "../be/db";
30
- import { renderIdentity, resolveIdentity } from "../be/identity";
31
29
  import {
32
30
  parseModelTierOverridesHeader,
33
31
  recordClaimModelResolution,
@@ -37,19 +35,16 @@ import { poolTaskRunsOnHarness } from "../be/model-validation";
37
35
  import { getRuntimeInstanceById, touchRuntimeInstance } from "../be/multi-runtime";
38
36
  import { hasCapability } from "../server";
39
37
  import { fetchChannelActivity } from "../slack/channel-activity";
38
+ import { type TaskModelPinFields, taskModelPin } from "../tasks/model-pin";
40
39
  import { emitTaskTelemetry, resolveTriggerSurface } from "../telemetry-trigger";
41
- import {
42
- AgentTaskSchema,
43
- BudgetRefusedTriggerSchema,
44
- TaskAttachmentSchema,
45
- UserCommsPrefsSchema,
46
- UserSchema,
47
- } from "../types";
40
+ import { AgentTaskSchema, BudgetRefusedTriggerSchema, TaskAttachmentSchema } from "../types";
48
41
  import { ensure } from "../utils/business-use";
49
42
  import { isLeadChannelMonitoringEnabled } from "../utils/lead-monitor-channels";
50
43
  import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
51
- import { getUserCommsPrefs } from "../utils/requester-comms";
44
+ import { getRequestAuth, type HttpRequestAuth } from "../utils/request-auth-context";
52
45
  import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
46
+ import { readsRepoSecrets } from "./config";
47
+ import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
53
48
  import { route, runtimeInstanceHeader } from "./route-def";
54
49
  import { jsonError } from "./utils";
55
50
 
@@ -100,27 +95,11 @@ const PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
100
95
  sizeBytes: true,
101
96
  });
102
97
 
103
- // Requester projection sent on `task_assigned` triggers — either the resolved
104
- // user's identity fields, or (when no `requestedByUserId` is recorded) just a
105
- // rendered `name` for the UNKNOWN-identity sentinel.
106
- const PollRequestedBySchema = UserSchema.pick({
107
- id: true,
108
- name: true,
109
- email: true,
110
- role: true,
111
- notes: true,
112
- }).extend({
113
- // Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
114
- id: z.string().optional(),
115
- // Structured communication preferences from `users.metadata.comms`.
116
- comms: UserCommsPrefsSchema.optional(),
117
- });
118
-
119
98
  const PollTaskOfferedTriggerSchema = z.object({
120
99
  type: z.literal("task_offered"),
121
100
  taskId: z.string(),
122
101
  task: AgentTaskSchema,
123
- requestedBy: PollRequestedBySchema.optional(),
102
+ requestedBy: TaskRequestedBySchema.optional(),
124
103
  });
125
104
 
126
105
  const PollTaskAssignedTriggerSchema = z.object({
@@ -132,7 +111,7 @@ const PollTaskAssignedTriggerSchema = z.object({
132
111
  task: AgentTaskSchema.extend({
133
112
  attachments: z.array(PollTriggerAttachmentSchema),
134
113
  }),
135
- requestedBy: PollRequestedBySchema.optional(),
114
+ requestedBy: TaskRequestedBySchema.optional(),
136
115
  });
137
116
 
138
117
  // `budget_refused` reuses the canonical `BudgetRefusedTriggerSchema` entity
@@ -204,38 +183,6 @@ const pollTriggers = route({
204
183
  const CHANNEL_ACTIVITY_INTERVAL_MS = 60_000; // Check at most once per 60s
205
184
  let lastChannelActivityCheckAt = 0;
206
185
 
207
- function getRequesterNotes(notes: string | undefined): string | undefined {
208
- return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
209
- }
210
-
211
- /**
212
- * Resolve the requester projection for a task trigger. If the task carries a
213
- * machine-recorded external id (today: the Slack user field) but no
214
- * requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
215
- * omitting the section: never a substituted human (Rule 33 /
216
- * provenance-or-silence).
217
- */
218
- async function buildTriggerRequestedBy(task: {
219
- requestedByUserId?: string | null;
220
- slackUserId?: string | null;
221
- }): Promise<z.infer<typeof PollRequestedBySchema> | undefined> {
222
- const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
223
- if (user) {
224
- return {
225
- id: user.id,
226
- name: user.name,
227
- email: user.email,
228
- role: user.role,
229
- notes: getRequesterNotes(user.notes),
230
- comms: getUserCommsPrefs(user),
231
- };
232
- }
233
- if (task.slackUserId) {
234
- return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
235
- }
236
- return undefined;
237
- }
238
-
239
186
  /**
240
187
  * Slim attachment projection for the `task_assigned` poll trigger — just
241
188
  * enough (id, name, mimeType, sizeBytes) for the worker to build a one-shot
@@ -256,16 +203,40 @@ async function attachmentsForTrigger(
256
203
  /**
257
204
  * Claim-time model resolution (runbooks/model-tiers.md). Never blocks a claim:
258
205
  * a resolution failure leaves the worker on its own local resolution.
206
+ *
207
+ * The trigger's `model` is the task's own pin, not the column: a re-claimed
208
+ * row carries the model its previous session reported, and the worker treats
209
+ * `task.model` as a pin that beats the agent's current MODEL_OVERRIDE. The
210
+ * previous claim record is overwritten for the same reason.
259
211
  */
260
212
  async function claimModelFields(
261
- task: { id: string; model?: string | null; modelTier?: string | null },
262
- agent: Parameters<typeof recordClaimModelResolution>[1],
263
- ): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>>> {
213
+ task: TaskModelPinFields & {
214
+ id: string;
215
+ profile?: string | null;
216
+ effort?: string | null;
217
+ vcsRepo?: string | null;
218
+ },
219
+ agent: Parameters<typeof recordClaimModelResolution>[1] & { isLead?: boolean },
220
+ auth: HttpRequestAuth | null,
221
+ ): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>> & { model: string | undefined }> {
222
+ const pin = taskModelPin(task);
223
+ const cleared = {
224
+ model: pin,
225
+ resolvedModel: undefined,
226
+ modelSource: undefined,
227
+ modelAlias: undefined,
228
+ resolvedProfile: undefined,
229
+ };
264
230
  try {
265
- return await recordClaimModelResolution(task, agent);
231
+ return {
232
+ ...cleared,
233
+ ...(await recordClaimModelResolution({ ...task, model: pin }, agent, {
234
+ includeRepoSecrets: readsRepoSecrets(auth, agent),
235
+ })),
236
+ };
266
237
  } catch (error) {
267
238
  console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
268
- return {};
239
+ return cleared;
269
240
  }
270
241
  }
271
242
 
@@ -422,7 +393,7 @@ export async function handlePoll(
422
393
  const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
423
394
  if (claimedTask) {
424
395
  await recordRuntimeTask(claimedTask.id);
425
- const offeredRequestedBy = await buildTriggerRequestedBy(claimedTask);
396
+ const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
426
397
  return {
427
398
  trigger: {
428
399
  type: "task_offered",
@@ -519,8 +490,8 @@ export async function handlePoll(
519
490
  });
520
491
 
521
492
  // Resolve requesting user if available (UNKNOWN sentinel handling
522
- // lives in buildTriggerRequestedBy).
523
- const assignedRequestedBy = await buildTriggerRequestedBy(pendingTask);
493
+ // lives in buildTaskRequestedBy).
494
+ const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
524
495
  // The surface that started the whole chain, so the worker can tag
525
496
  // its session events with it (workers cannot read the task tree).
526
497
  const assignedTriggerSurface = await resolveTriggerSurface(
@@ -535,7 +506,7 @@ export async function handlePoll(
535
506
  triggerSurface: assignedTriggerSurface,
536
507
  task: {
537
508
  ...pendingTask,
538
- ...(await claimModelFields(pendingTask, agent)),
509
+ ...(await claimModelFields(pendingTask, agent, getRequestAuth(req))),
539
510
  status: "in_progress",
540
511
  attachments: await attachmentsForTrigger(pendingTask.id),
541
512
  },
@@ -667,7 +638,7 @@ export async function handlePoll(
667
638
  agentId: myAgentId,
668
639
  });
669
640
  });
670
- const claimedRequestedBy = await buildTriggerRequestedBy(claimed);
641
+ const claimedRequestedBy = await buildTaskRequestedBy(claimed);
671
642
  const claimedTriggerSurface = await resolveTriggerSurface(
672
643
  claimed.id,
673
644
  claimed.source,
@@ -679,7 +650,7 @@ export async function handlePoll(
679
650
  triggerSurface: claimedTriggerSurface,
680
651
  task: {
681
652
  ...claimed,
682
- ...(await claimModelFields(claimed, agent)),
653
+ ...(await claimModelFields(claimed, agent, getRequestAuth(req))),
683
654
  attachments: await attachmentsForTrigger(claimed.id),
684
655
  },
685
656
  ...(claimedRequestedBy && { requestedBy: claimedRequestedBy }),
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { getUserById } from "../be/db";
3
+ import { renderIdentity, resolveIdentity } from "../be/identity";
4
+ import { UserCommsPrefsSchema, UserSchema } from "../types";
5
+ import { getUserCommsPrefs } from "../utils/requester-comms";
6
+
7
+ // Requester projection sent with a task to the worker that runs it (poll
8
+ // triggers, paused-task resume) — either the resolved user's identity fields,
9
+ // or (when no `requestedByUserId` is recorded) just a rendered `name` for the
10
+ // UNKNOWN-identity sentinel.
11
+ export const TaskRequestedBySchema = UserSchema.pick({
12
+ id: true,
13
+ name: true,
14
+ email: true,
15
+ role: true,
16
+ notes: true,
17
+ }).extend({
18
+ // Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
19
+ id: z.string().optional(),
20
+ // Structured communication preferences from `users.metadata.comms`.
21
+ comms: UserCommsPrefsSchema.optional(),
22
+ });
23
+
24
+ function getRequesterNotes(notes: string | undefined): string | undefined {
25
+ return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
26
+ }
27
+
28
+ /**
29
+ * Resolve the requester projection for a task. If the task carries a
30
+ * machine-recorded external id (today: the Slack user field) but no
31
+ * requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
32
+ * omitting the section: never a substituted human (Rule 33 /
33
+ * provenance-or-silence).
34
+ */
35
+ export async function buildTaskRequestedBy(task: {
36
+ requestedByUserId?: string | null;
37
+ slackUserId?: string | null;
38
+ }): Promise<z.infer<typeof TaskRequestedBySchema> | undefined> {
39
+ const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
40
+ if (user) {
41
+ return {
42
+ id: user.id,
43
+ name: user.name,
44
+ email: user.email,
45
+ role: user.role,
46
+ notes: getRequesterNotes(user.notes),
47
+ comms: getUserCommsPrefs(user),
48
+ };
49
+ }
50
+ if (task.slackUserId) {
51
+ return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
52
+ }
53
+ return undefined;
54
+ }
@@ -611,7 +611,7 @@ export async function handleScriptRuns(
611
611
  if (!parsed) return true;
612
612
  const result = await executeRawLlm(parsed.body);
613
613
  if (result.status === "failed") {
614
- json(res, { error: result.error }, 500);
614
+ jsonError(res, result.error, 500);
615
615
  return true;
616
616
  }
617
617
  rawLlmRoute.respond(res, 200, result.output);