@desplega.ai/agent-swarm 1.173.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
  7. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
  8. package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
  15. package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
  16. package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
  17. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  18. package/dist/cli-2m1ktfzz.js +17795 -0
  19. package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
  20. package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
  21. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  22. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  23. package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
  24. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  25. package/dist/cli-6cvfxs8r.js +47 -0
  26. package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
  27. package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
  28. package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
  29. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  30. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  31. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  32. package/dist/cli-9traj3dn.js +638 -0
  33. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  34. package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
  35. package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
  36. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  37. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  38. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  39. package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
  40. package/dist/cli-e3g5mbkz.js +776 -0
  41. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  42. package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
  43. package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
  44. package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
  45. package/dist/cli-f527ce74.js +781 -0
  46. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  47. package/dist/cli-hjwmhcg1.js +10622 -0
  48. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  49. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  50. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  51. package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
  52. package/dist/cli-jta2eyzc.js +619 -0
  53. package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
  54. package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
  55. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  56. package/dist/cli-kceah1eh.js +92 -0
  57. package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
  58. package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
  59. package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
  60. package/dist/cli-n2wq4j7m.js +6985 -0
  61. package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
  62. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  63. package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
  64. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  65. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  66. package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
  67. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  68. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  69. package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
  70. package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
  71. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  72. package/dist/cli-rhs9pz33.js +294 -0
  73. package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
  74. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  75. package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
  76. package/dist/cli-skxvvpap.js +10140 -0
  77. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  78. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  79. package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
  80. package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
  81. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  82. package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
  83. package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
  84. package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
  85. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  86. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  87. package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
  88. package/dist/cli-wmgrkscj.js +23254 -0
  89. package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
  90. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  91. package/dist/cli.js +29 -45
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  99. package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
  100. package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
  117. package/dist/index-52j1aja4.js +3465 -0
  118. package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
  119. package/dist/index-9gw58n4k.js +193 -0
  120. package/dist/index-9j87mcm8.js +56305 -0
  121. package/dist/index-bsmcresk.js +79424 -0
  122. package/dist/index-mh85qhec.js +475 -0
  123. package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
  124. package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
  125. package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
  126. package/dist/index-we27kd31.js +12810 -0
  127. package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  148. package/openapi.json +523 -344
  149. package/package.json +10 -11
  150. package/src/be/config-refs.ts +60 -0
  151. package/src/be/db/page-versions.ts +15 -0
  152. package/src/be/db/runtime.ts +35 -8
  153. package/src/be/db/skill-name-resolution.ts +36 -0
  154. package/src/be/db/tasks/read.ts +230 -0
  155. package/src/be/db/tasks/write.ts +7 -0
  156. package/src/be/db.ts +52 -7
  157. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  158. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  159. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  160. package/src/be/model-catalog-store.ts +5 -1
  161. package/src/be/model-tier-resolution.ts +12 -1
  162. package/src/be/model-validation.ts +5 -2
  163. package/src/be/models-catalog.ts +26 -1
  164. package/src/be/modelsdev-cache.json +198 -63
  165. package/src/be/modelsdev-cache.ts +9 -1
  166. package/src/be/script-credential-broker.ts +39 -2
  167. package/src/be/scripts/run-saved.ts +1 -0
  168. package/src/be/scripts/typecheck.ts +10 -8
  169. package/src/be/seed/runner.ts +13 -1
  170. package/src/be/seed/state-db.ts +5 -0
  171. package/src/be/seed/types.ts +6 -0
  172. package/src/be/seed-pricing.ts +19 -2
  173. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  174. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  175. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  176. package/src/be/seed-skills/index.ts +104 -3
  177. package/src/be/swarm-config-guard.ts +52 -0
  178. package/src/cli.tsx +0 -16
  179. package/src/commands/onboard/non-interactive.ts +4 -2
  180. package/src/commands/provider-credentials.ts +52 -6
  181. package/src/commands/runner.ts +190 -54
  182. package/src/e2b/env.ts +5 -0
  183. package/src/extensions/contract-types.generated.ts +1 -1
  184. package/src/extensions/contract.ts +45 -0
  185. package/src/extensions/dispatcher.ts +7 -0
  186. package/src/extensions/post-bridge.ts +40 -0
  187. package/src/github/handlers.ts +11 -4
  188. package/src/heartbeat/templates.ts +8 -5
  189. package/src/hooks/hook.ts +0 -26
  190. package/src/http/all-routes.ts +0 -1
  191. package/src/http/config.ts +36 -17
  192. package/src/http/core.ts +4 -0
  193. package/src/http/index.ts +14 -2
  194. package/src/http/integrations.ts +88 -1
  195. package/src/http/mcp.ts +15 -5
  196. package/src/http/page-proxy.ts +2 -4
  197. package/src/http/pages-public.ts +3 -3
  198. package/src/http/pages.ts +3 -13
  199. package/src/http/poll.ts +28 -68
  200. package/src/http/requested-by.ts +54 -0
  201. package/src/http/scripts.ts +1 -0
  202. package/src/http/stats.ts +1 -30
  203. package/src/http/tasks.ts +61 -5
  204. package/src/http/utils.ts +14 -0
  205. package/src/http/webhooks.ts +159 -1
  206. package/src/http/x.ts +1 -0
  207. package/src/integrations/chat-sdk/adapters.ts +129 -0
  208. package/src/integrations/chat-sdk/attachments.ts +232 -0
  209. package/src/integrations/chat-sdk/background.ts +36 -0
  210. package/src/integrations/chat-sdk/commands.ts +194 -0
  211. package/src/integrations/chat-sdk/config.ts +198 -0
  212. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  213. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  214. package/src/integrations/chat-sdk/handlers.ts +28 -0
  215. package/src/integrations/chat-sdk/inbound.ts +426 -0
  216. package/src/integrations/chat-sdk/limits.ts +48 -0
  217. package/src/integrations/chat-sdk/logger.ts +81 -0
  218. package/src/integrations/chat-sdk/outbound.ts +123 -0
  219. package/src/integrations/chat-sdk/relay.ts +172 -0
  220. package/src/integrations/chat-sdk/router.ts +169 -0
  221. package/src/integrations/chat-sdk/runtime.ts +351 -0
  222. package/src/integrations/chat-sdk/schema.ts +245 -0
  223. package/src/integrations/chat-sdk/state.ts +243 -0
  224. package/src/integrations/chat-sdk/status.ts +244 -0
  225. package/src/integrations/chat-sdk/steering.ts +58 -0
  226. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  227. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  228. package/src/integrations/chat-sdk/templates.ts +16 -0
  229. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  230. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  231. package/src/integrations/chat-sdk/typing.ts +65 -0
  232. package/src/pages/version.ts +4 -5
  233. package/src/prompts/base-prompt.ts +111 -45
  234. package/src/providers/acp-target-catalog.ts +6 -0
  235. package/src/providers/acp-targets.ts +55 -2
  236. package/src/providers/amp-adapter.ts +1 -1
  237. package/src/providers/claude-adapter.ts +1 -1
  238. package/src/providers/claude-flag-settings.ts +17 -5
  239. package/src/providers/claude-managed-adapter.ts +1 -1
  240. package/src/providers/claude-session-events.ts +6 -1
  241. package/src/providers/dsh-adapter.ts +1 -1
  242. package/src/providers/opencode-adapter.ts +243 -26
  243. package/src/providers/opencode-swarm-plugin.ts +31 -0
  244. package/src/providers/pi-mono-adapter.ts +398 -153
  245. package/src/providers/pricing-sources.md +33 -2
  246. package/src/providers/reasoning-effort.ts +27 -0
  247. package/src/providers/types.ts +6 -2
  248. package/src/rbac/legacy-policy.ts +8 -2
  249. package/src/rbac/permissions.ts +21 -0
  250. package/src/scheduler/scheduler.ts +4 -2
  251. package/src/scripts-runtime/loader.ts +19 -7
  252. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  253. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  254. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  255. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  256. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  257. package/src/server.ts +52 -18
  258. package/src/slack/event-dedup.ts +1 -1
  259. package/src/tasks/additive-buffer.ts +4 -2
  260. package/src/tasks/context-key.ts +60 -1
  261. package/src/tasks/model-pin.ts +67 -0
  262. package/src/tasks/worker-follow-up.ts +4 -31
  263. package/src/tests/acp-adapter.test.ts +42 -0
  264. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  265. package/src/tests/base-prompt.test.ts +58 -54
  266. package/src/tests/chat-sdk-commands.test.ts +191 -0
  267. package/src/tests/chat-sdk-config.test.ts +334 -0
  268. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  269. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  270. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  271. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  272. package/src/tests/chat-sdk-harness.ts +146 -0
  273. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  274. package/src/tests/chat-sdk-relay.test.ts +470 -0
  275. package/src/tests/chat-sdk-reload.test.ts +128 -0
  276. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  277. package/src/tests/chat-sdk-slack.test.ts +268 -0
  278. package/src/tests/chat-sdk-state.test.ts +163 -0
  279. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  280. package/src/tests/chat-sdk-teams.test.ts +277 -0
  281. package/src/tests/chat-sdk-tools.test.ts +520 -0
  282. package/src/tests/chat-sdk-typing.test.ts +120 -0
  283. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  284. package/src/tests/claude-adapter-binary.test.ts +9 -4
  285. package/src/tests/claude-flag-settings.test.ts +11 -3
  286. package/src/tests/claude-managed-adapter.test.ts +1 -1
  287. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  288. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  289. package/src/tests/claude-worker-parity.test.ts +2 -7
  290. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  291. package/src/tests/config-refs.test.ts +66 -0
  292. package/src/tests/context-key.test.ts +19 -0
  293. package/src/tests/cors-allowlist.test.ts +25 -0
  294. package/src/tests/create-page-tool.test.ts +18 -0
  295. package/src/tests/credential-check.test.ts +139 -0
  296. package/src/tests/credentials.test.ts +21 -0
  297. package/src/tests/db-runtime.test.ts +61 -0
  298. package/src/tests/db-task-read-contract.test.ts +20 -0
  299. package/src/tests/db-task-write-contract.test.ts +10 -0
  300. package/src/tests/docs-index-footprint.test.js +3 -3
  301. package/src/tests/dsh-adapter.test.ts +1 -1
  302. package/src/tests/enabled-capabilities.test.ts +20 -0
  303. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  304. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  305. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  306. package/src/tests/explicit-model-validation.test.ts +105 -1
  307. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  308. package/src/tests/extensions-post-bridge.test.ts +51 -0
  309. package/src/tests/fetch-resolved-env.test.ts +128 -0
  310. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  311. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  312. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  313. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  314. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  315. package/src/tests/github-handlers.test.ts +35 -0
  316. package/src/tests/harness-provider-resolution.test.ts +12 -0
  317. package/src/tests/heartbeat-checklist.test.ts +3 -2
  318. package/src/tests/hook-runtime-close.test.ts +0 -2
  319. package/src/tests/http-api-integration.test.ts +0 -25
  320. package/src/tests/migration-046-budgets.test.ts +17 -16
  321. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  322. package/src/tests/model-tier-resolution.test.ts +93 -0
  323. package/src/tests/models-catalog.test.ts +42 -1
  324. package/src/tests/openai-compatible-providers.test.ts +134 -0
  325. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  326. package/src/tests/opencode-adapter.test.ts +216 -2
  327. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  328. package/src/tests/opencode-shell-env.test.ts +145 -0
  329. package/src/tests/openrouter-base-url.test.ts +101 -151
  330. package/src/tests/page-proxy.test.ts +1 -1
  331. package/src/tests/pages-browser-sdk.test.ts +76 -0
  332. package/src/tests/pages-feedback.test.ts +1 -1
  333. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  334. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  335. package/src/tests/pi-mono-adapter.test.ts +385 -5
  336. package/src/tests/pi-shell-env.test.ts +170 -0
  337. package/src/tests/prompt-template-remaining.test.ts +35 -0
  338. package/src/tests/rbac-engine.test.ts +11 -3
  339. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  340. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  341. package/src/tests/script-connections.test.ts +2 -0
  342. package/src/tests/scripts-http.test.ts +201 -1
  343. package/src/tests/scripts-only-gating.test.ts +2 -3
  344. package/src/tests/scripts-runtime.test.ts +58 -40
  345. package/src/tests/secret-scrubber.test.ts +86 -0
  346. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  347. package/src/tests/seed-skills-retire.test.ts +182 -0
  348. package/src/tests/send-task-requester-gate.test.ts +58 -0
  349. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  350. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  351. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  352. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  353. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  354. package/src/tests/swarm-diff.test.ts +1 -1
  355. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  356. package/src/tests/task-session-prompts.test.ts +132 -0
  357. package/src/tests/task-tool-manifest.test.ts +14 -0
  358. package/src/tests/task-tools-ctx.test.ts +2 -0
  359. package/src/tests/tool-annotations.test.ts +6 -6
  360. package/src/tests/ui-logs-parser.test.ts +43 -0
  361. package/src/tests/used-by-route.test.ts +6 -0
  362. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  363. package/src/tests/workflow-swarm-script.test.ts +38 -0
  364. package/src/tests/workflow-wait-filter.test.ts +5 -3
  365. package/src/tools/chat-access.ts +41 -0
  366. package/src/tools/chat-open-dm.ts +59 -0
  367. package/src/tools/chat-post.ts +74 -0
  368. package/src/tools/chat-read-thread.ts +100 -0
  369. package/src/tools/chat-reply.ts +75 -0
  370. package/src/tools/create-page.ts +3 -13
  371. package/src/tools/model-catalog/index.ts +2 -1
  372. package/src/tools/register-chat-channel.ts +139 -0
  373. package/src/tools/send-task.ts +24 -0
  374. package/src/tools/skills/skill-get.ts +17 -9
  375. package/src/tools/swarm-config/get-config.ts +2 -1
  376. package/src/tools/swarm-config/list-config.ts +2 -1
  377. package/src/tools/swarm-config/set-config.ts +16 -3
  378. package/src/tools/tool-config.ts +8 -6
  379. package/src/types.ts +12 -2
  380. package/src/utils/config-env-export.ts +74 -0
  381. package/src/utils/credentials.test.ts +92 -0
  382. package/src/utils/credentials.ts +52 -4
  383. package/src/utils/harness-provider.ts +15 -6
  384. package/src/utils/runtime-model-catalog.ts +11 -1
  385. package/src/utils/secret-scrubber.ts +71 -2
  386. package/src/utils/task-tool-manifest.ts +19 -2
  387. package/src/workflows/executors/swarm-script.ts +1 -0
  388. package/templates/skills/agent-browser/SKILL.md +1 -1
  389. package/templates/skills/agent-browser/content.md +1 -1
  390. package/templates/skills/apps/SKILL.md +8 -1
  391. package/templates/skills/apps/content.md +8 -1
  392. package/templates/skills/chat-sdk/config.json +14 -0
  393. package/templates/skills/chat-sdk/content.md +84 -0
  394. package/templates/skills/composio-gmail/content.md +7 -0
  395. package/templates/skills/composio-google-docs/content.md +1 -1
  396. package/templates/skills/email-domain-health/config.json +14 -0
  397. package/templates/skills/email-domain-health/content.md +131 -0
  398. package/templates/skills/kv-storage/content.md +2 -2
  399. package/templates/skills/pages/content.md +8 -10
  400. package/templates/skills/swarm-extensions/content.md +2 -0
  401. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  402. package/templates/skills/swarm-scripts/config.json +1 -1
  403. package/templates/skills/swarm-scripts/content.md +4 -2
  404. package/templates/skills/workflow-iterate/content.md +38 -0
  405. package/dist/artifact-eczsknc3.js +0 -3843
  406. package/dist/cli-bd957dgb.js +0 -153
  407. package/dist/cli-q51yprfq.js +0 -1104
  408. package/dist/cli-t1ze64h3.js +0 -811
  409. package/dist/cli-tcc4wd5d.js +0 -56471
  410. package/dist/index-9ch1n761.js +0 -1102
  411. package/src/artifact-sdk/index.ts +0 -2
  412. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  413. package/src/artifact-sdk/port.ts +0 -12
  414. package/src/artifact-sdk/server.ts +0 -183
  415. package/src/artifact-sdk/tunnel.ts +0 -19
  416. package/src/commands/artifact.ts +0 -249
  417. package/src/http/ecosystem.ts +0 -77
  418. package/src/tests/artifact-commands.test.ts +0 -92
  419. package/src/tests/artifact-sdk.test.ts +0 -793
  420. package/src/tests/register-service-security.test.ts +0 -194
  421. package/src/tools/list-services.ts +0 -107
  422. package/src/tools/register-service.ts +0 -177
  423. package/src/tools/unregister-service.ts +0 -73
  424. package/src/tools/update-service-status.ts +0 -96
  425. package/templates/skills/artifacts/config.json +0 -14
  426. package/templates/skills/artifacts/content.md +0 -344
  427. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  428. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  429. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  430. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  431. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  432. /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
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,14 @@ import { poolTaskRunsOnHarness } from "../be/model-validation";
37
35
  import { getRuntimeInstanceById, touchRuntimeInstance } from "../be/multi-runtime";
38
36
  import { hasCapability } from "../server";
39
37
  import { fetchChannelActivity } from "../slack/channel-activity";
38
+ import { type TaskModelPinFields, taskModelPin } from "../tasks/model-pin";
40
39
  import { emitTaskTelemetry, resolveTriggerSurface } from "../telemetry-trigger";
41
- import {
42
- AgentTaskSchema,
43
- BudgetRefusedTriggerSchema,
44
- TaskAttachmentSchema,
45
- UserCommsPrefsSchema,
46
- UserSchema,
47
- } from "../types";
40
+ import { AgentTaskSchema, BudgetRefusedTriggerSchema, TaskAttachmentSchema } from "../types";
48
41
  import { ensure } from "../utils/business-use";
49
42
  import { isLeadChannelMonitoringEnabled } from "../utils/lead-monitor-channels";
50
43
  import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
51
- import { getUserCommsPrefs } from "../utils/requester-comms";
52
44
  import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
45
+ import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
53
46
  import { route, runtimeInstanceHeader } from "./route-def";
54
47
  import { jsonError } from "./utils";
55
48
 
@@ -100,27 +93,11 @@ const PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
100
93
  sizeBytes: true,
101
94
  });
102
95
 
103
- // Requester projection sent on `task_assigned` triggers — either the resolved
104
- // user's identity fields, or (when no `requestedByUserId` is recorded) just a
105
- // rendered `name` for the UNKNOWN-identity sentinel.
106
- const PollRequestedBySchema = UserSchema.pick({
107
- id: true,
108
- name: true,
109
- email: true,
110
- role: true,
111
- notes: true,
112
- }).extend({
113
- // Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
114
- id: z.string().optional(),
115
- // Structured communication preferences from `users.metadata.comms`.
116
- comms: UserCommsPrefsSchema.optional(),
117
- });
118
-
119
96
  const PollTaskOfferedTriggerSchema = z.object({
120
97
  type: z.literal("task_offered"),
121
98
  taskId: z.string(),
122
99
  task: AgentTaskSchema,
123
- requestedBy: PollRequestedBySchema.optional(),
100
+ requestedBy: TaskRequestedBySchema.optional(),
124
101
  });
125
102
 
126
103
  const PollTaskAssignedTriggerSchema = z.object({
@@ -132,7 +109,7 @@ const PollTaskAssignedTriggerSchema = z.object({
132
109
  task: AgentTaskSchema.extend({
133
110
  attachments: z.array(PollTriggerAttachmentSchema),
134
111
  }),
135
- requestedBy: PollRequestedBySchema.optional(),
112
+ requestedBy: TaskRequestedBySchema.optional(),
136
113
  });
137
114
 
138
115
  // `budget_refused` reuses the canonical `BudgetRefusedTriggerSchema` entity
@@ -204,38 +181,6 @@ const pollTriggers = route({
204
181
  const CHANNEL_ACTIVITY_INTERVAL_MS = 60_000; // Check at most once per 60s
205
182
  let lastChannelActivityCheckAt = 0;
206
183
 
207
- function getRequesterNotes(notes: string | undefined): string | undefined {
208
- return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
209
- }
210
-
211
- /**
212
- * Resolve the requester projection for a task trigger. If the task carries a
213
- * machine-recorded external id (today: the Slack user field) but no
214
- * requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
215
- * omitting the section: never a substituted human (Rule 33 /
216
- * provenance-or-silence).
217
- */
218
- async function buildTriggerRequestedBy(task: {
219
- requestedByUserId?: string | null;
220
- slackUserId?: string | null;
221
- }): Promise<z.infer<typeof PollRequestedBySchema> | undefined> {
222
- const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
223
- if (user) {
224
- return {
225
- id: user.id,
226
- name: user.name,
227
- email: user.email,
228
- role: user.role,
229
- notes: getRequesterNotes(user.notes),
230
- comms: getUserCommsPrefs(user),
231
- };
232
- }
233
- if (task.slackUserId) {
234
- return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
235
- }
236
- return undefined;
237
- }
238
-
239
184
  /**
240
185
  * Slim attachment projection for the `task_assigned` poll trigger — just
241
186
  * enough (id, name, mimeType, sizeBytes) for the worker to build a one-shot
@@ -256,16 +201,31 @@ async function attachmentsForTrigger(
256
201
  /**
257
202
  * Claim-time model resolution (runbooks/model-tiers.md). Never blocks a claim:
258
203
  * a resolution failure leaves the worker on its own local resolution.
204
+ *
205
+ * The trigger's `model` is the task's own pin, not the column: a re-claimed
206
+ * row carries the model its previous session reported, and the worker treats
207
+ * `task.model` as a pin that beats the agent's current MODEL_OVERRIDE. The
208
+ * previous claim record is overwritten for the same reason.
259
209
  */
260
210
  async function claimModelFields(
261
- task: { id: string; model?: string | null; modelTier?: string | null },
211
+ task: TaskModelPinFields & { id: string },
262
212
  agent: Parameters<typeof recordClaimModelResolution>[1],
263
- ): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>>> {
213
+ ): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>> & { model: string | undefined }> {
214
+ const pin = taskModelPin(task);
215
+ const cleared = {
216
+ model: pin,
217
+ resolvedModel: undefined,
218
+ modelSource: undefined,
219
+ modelAlias: undefined,
220
+ };
264
221
  try {
265
- return await recordClaimModelResolution(task, agent);
222
+ return {
223
+ ...cleared,
224
+ ...(await recordClaimModelResolution({ ...task, model: pin }, agent)),
225
+ };
266
226
  } catch (error) {
267
227
  console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
268
- return {};
228
+ return cleared;
269
229
  }
270
230
  }
271
231
 
@@ -422,7 +382,7 @@ export async function handlePoll(
422
382
  const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
423
383
  if (claimedTask) {
424
384
  await recordRuntimeTask(claimedTask.id);
425
- const offeredRequestedBy = await buildTriggerRequestedBy(claimedTask);
385
+ const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
426
386
  return {
427
387
  trigger: {
428
388
  type: "task_offered",
@@ -519,8 +479,8 @@ export async function handlePoll(
519
479
  });
520
480
 
521
481
  // Resolve requesting user if available (UNKNOWN sentinel handling
522
- // lives in buildTriggerRequestedBy).
523
- const assignedRequestedBy = await buildTriggerRequestedBy(pendingTask);
482
+ // lives in buildTaskRequestedBy).
483
+ const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
524
484
  // The surface that started the whole chain, so the worker can tag
525
485
  // its session events with it (workers cannot read the task tree).
526
486
  const assignedTriggerSurface = await resolveTriggerSurface(
@@ -667,7 +627,7 @@ export async function handlePoll(
667
627
  agentId: myAgentId,
668
628
  });
669
629
  });
670
- const claimedRequestedBy = await buildTriggerRequestedBy(claimed);
630
+ const claimedRequestedBy = await buildTaskRequestedBy(claimed);
671
631
  const claimedTriggerSurface = await resolveTriggerSurface(
672
632
  claimed.id,
673
633
  claimed.source,
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { getUserById } from "../be/db";
3
+ import { renderIdentity, resolveIdentity } from "../be/identity";
4
+ import { UserCommsPrefsSchema, UserSchema } from "../types";
5
+ import { getUserCommsPrefs } from "../utils/requester-comms";
6
+
7
+ // Requester projection sent with a task to the worker that runs it (poll
8
+ // triggers, paused-task resume) — either the resolved user's identity fields,
9
+ // or (when no `requestedByUserId` is recorded) just a rendered `name` for the
10
+ // UNKNOWN-identity sentinel.
11
+ export const TaskRequestedBySchema = UserSchema.pick({
12
+ id: true,
13
+ name: true,
14
+ email: true,
15
+ role: true,
16
+ notes: true,
17
+ }).extend({
18
+ // Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
19
+ id: z.string().optional(),
20
+ // Structured communication preferences from `users.metadata.comms`.
21
+ comms: UserCommsPrefsSchema.optional(),
22
+ });
23
+
24
+ function getRequesterNotes(notes: string | undefined): string | undefined {
25
+ return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
26
+ }
27
+
28
+ /**
29
+ * Resolve the requester projection for a task. If the task carries a
30
+ * machine-recorded external id (today: the Slack user field) but no
31
+ * requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
32
+ * omitting the section: never a substituted human (Rule 33 /
33
+ * provenance-or-silence).
34
+ */
35
+ export async function buildTaskRequestedBy(task: {
36
+ requestedByUserId?: string | null;
37
+ slackUserId?: string | null;
38
+ }): Promise<z.infer<typeof TaskRequestedBySchema> | undefined> {
39
+ const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
40
+ if (user) {
41
+ return {
42
+ id: user.id,
43
+ name: user.name,
44
+ email: user.email,
45
+ role: user.role,
46
+ notes: getRequesterNotes(user.notes),
47
+ comms: getUserCommsPrefs(user),
48
+ };
49
+ }
50
+ if (task.slackUserId) {
51
+ return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
52
+ }
53
+ return undefined;
54
+ }
@@ -765,6 +765,7 @@ export async function handleScripts(
765
765
  runtimeInstanceId,
766
766
  egressSecrets: credentials.egressSecrets,
767
767
  failedBindings: credentials.failedBindings,
768
+ userConfig: credentials.userConfig,
768
769
  apiConnections: getScriptApiConnectionDescriptors({ agentId: agent.id }),
769
770
  mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agent.id }),
770
771
  });
package/src/http/stats.ts CHANGED
@@ -3,7 +3,6 @@ import { z } from "zod";
3
3
  import {
4
4
  getAgentStatusCounts,
5
5
  getAllLogs,
6
- getAllServices,
7
6
  getConcurrentContext,
8
7
  getLogsByAgentId,
9
8
  getScheduledTasks,
@@ -14,7 +13,7 @@ import {
14
13
  import { getDbRetentionStats } from "../be/db-retention";
15
14
  import { isSteeringEnabled } from "../be/steering";
16
15
  import type { AgentLog } from "../types";
17
- import { AgentLogSchema, ScheduledTaskSchema, ServiceSchema } from "../types";
16
+ import { AgentLogSchema, ScheduledTaskSchema } from "../types";
18
17
  import { isEnvFlagEnabled } from "../utils/env-flag";
19
18
  import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
20
19
  import { resolveHttpFavoriteOwner } from "./favorite-owner";
@@ -166,22 +165,6 @@ const getMetrics = route({
166
165
  },
167
166
  });
168
167
 
169
- const listServices = route({
170
- method: "get",
171
- path: "/api/services",
172
- pattern: ["api", "services"],
173
- summary: "List all registered services",
174
- tags: ["Stats"],
175
- query: z.object({
176
- status: z.string().optional(),
177
- agentId: z.string().optional(),
178
- name: z.string().optional(),
179
- }),
180
- responses: {
181
- 200: { description: "Service list", schema: z.object({ services: z.array(ServiceSchema) }) },
182
- },
183
- });
184
-
185
168
  const listScheduledTasks = route({
186
169
  method: "get",
187
170
  path: "/api/scheduled-tasks",
@@ -295,18 +278,6 @@ export async function handleStats(
295
278
  return true;
296
279
  }
297
280
 
298
- if (listServices.match(req.method, pathSegments)) {
299
- const parsed = await listServices.parse(req, res, pathSegments, queryParams);
300
- if (!parsed) return true;
301
- const services = await getAllServices({
302
- status: (parsed.query.status as import("../types").ServiceStatus) || undefined,
303
- agentId: parsed.query.agentId || undefined,
304
- name: parsed.query.name || undefined,
305
- });
306
- listServices.respond(res, 200, { services });
307
- return true;
308
- }
309
-
310
281
  if (listScheduledTasks.match(req.method, pathSegments)) {
311
282
  const parsed = await listScheduledTasks.parse(req, res, pathSegments, queryParams);
312
283
  if (!parsed) return true;
package/src/http/tasks.ts CHANGED
@@ -45,6 +45,7 @@ import {
45
45
  import { getTaskCitations, TaskCitationSchema } from "../be/task-citations";
46
46
  import { findUserById } from "../be/users";
47
47
  import { can, type RbacPrincipal, type RbacResource } from "../rbac";
48
+ import { chatThreadOfContextKey } from "../tasks/context-key";
48
49
  import { TaskCreationBlockedError } from "../tasks/errors";
49
50
  import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
50
51
  import { guardTerminalTaskResultWrite } from "../tasks/terminal-result-guard";
@@ -76,6 +77,7 @@ import { ensure } from "../utils/business-use";
76
77
  import { getRequestAuth } from "../utils/request-auth-context";
77
78
  import { scrubSecrets } from "../utils/secret-scrubber";
78
79
  import { rejectGuest } from "./request-principal";
80
+ import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
79
81
  import { route } from "./route-def";
80
82
  import { jsonError, parseBody } from "./utils";
81
83
 
@@ -110,6 +112,7 @@ const AgentTaskSummarySchema = AgentTaskSchema.pick({
110
112
  modelSource: true,
111
113
  modelAlias: true,
112
114
  effort: true,
115
+ resolvedEffort: true,
113
116
  provider: true,
114
117
  requestedByUserId: true,
115
118
  progress: true,
@@ -308,6 +311,10 @@ const createTask = route({
308
311
  description:
309
312
  "Validation error, an unknown `model` (set `allowCustomModel` to store a custom id), or agentId/offeredTo targets an extension identity",
310
313
  },
314
+ 403: {
315
+ description:
316
+ "An agent caller names a parentTaskId it neither owns nor created, or a non-lead agent binds the task to a chat thread",
317
+ },
311
318
  422: {
312
319
  description: "Task creation blocked by an extension",
313
320
  schema: z.object({
@@ -357,6 +364,8 @@ const updateSession = route({
357
364
  claudeSessionId: z.string().min(1),
358
365
  provider: ProviderNameSchema.exclude(["devin"]).optional(),
359
366
  model: z.string().optional(),
367
+ // Effort the session runs at; null when the harness default applies.
368
+ effort: ReasoningEffortSchema.nullable().optional(),
360
369
  providerMeta: z.object({ transport: z.enum(["cli", "sdk"]).optional() }).optional(),
361
370
  harnessVariant: z.string().optional(),
362
371
  harnessVariantMeta: z.record(z.string(), z.unknown()).optional(),
@@ -592,7 +601,14 @@ const listPausedTasks = route({
592
601
  200: {
593
602
  description: "Paused task list",
594
603
  schema: z.object({
595
- tasks: z.array(AgentTaskSchema.extend({ attachments: z.array(TaskAttachmentSchema) })),
604
+ tasks: z.array(
605
+ AgentTaskSchema.extend({
606
+ attachments: z.array(TaskAttachmentSchema),
607
+ // Same requester projection a fresh task_assigned trigger carries,
608
+ // so a resumed session gets the same requester-profile prompt.
609
+ requestedBy: TaskRequestedBySchema.optional(),
610
+ }),
611
+ ),
596
612
  }),
597
613
  },
598
614
  },
@@ -954,6 +970,41 @@ export async function handleTasks(
954
970
  const parentTask = parsed.body.parentTaskId
955
971
  ? await getTaskById(parsed.body.parentTaskId)
956
972
  : null;
973
+ // The child inherits the parent's thread context, so an agent caller must own or
974
+ // have created the parent. Operator and user callers keep their access.
975
+ const principal = await resolveTaskWritePrincipal(req, myAgentId);
976
+ if (
977
+ parentTask &&
978
+ !can({
979
+ principal,
980
+ verb: "task.parent.attach",
981
+ resource: {
982
+ kind: "task",
983
+ taskId: parentTask.id,
984
+ requestedByUserId: parentTask.requestedByUserId,
985
+ creatorAgentId: parentTask.creatorAgentId,
986
+ agentId: parentTask.agentId,
987
+ },
988
+ source: "http",
989
+ }).allow
990
+ ) {
991
+ jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
992
+ return true;
993
+ }
994
+ // A chat-sdk contextKey binds the task to a chat thread that its assignee and creator
995
+ // may then read and post to. An agent may only name a thread other than the parent's
996
+ // with the lead's chat post permission.
997
+ if (
998
+ principal.kind === "agent" &&
999
+ parsed.body.contextKey &&
1000
+ parsed.body.contextKey !== parentTask?.contextKey &&
1001
+ chatThreadOfContextKey(parsed.body.contextKey) &&
1002
+ !can({ principal, verb: "integration.chat.post", resource: { kind: "none" }, source: "http" })
1003
+ .allow
1004
+ ) {
1005
+ jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
1006
+ return true;
1007
+ }
957
1008
  let assetKey: string | undefined;
958
1009
  try {
959
1010
  const inheritedKey = parentTask?.key;
@@ -1083,6 +1134,7 @@ export async function handleTasks(
1083
1134
  parsed.body.model,
1084
1135
  "harnessVariant" in parsed.body ? parsed.body.harnessVariant : undefined,
1085
1136
  "harnessVariantMeta" in parsed.body ? parsed.body.harnessVariantMeta : undefined,
1137
+ "effort" in parsed.body ? parsed.body.effort : undefined,
1086
1138
  );
1087
1139
  if (!task) {
1088
1140
  jsonError(res, "Task not found", 404);
@@ -1667,10 +1719,14 @@ export async function handleTasks(
1667
1719
  }
1668
1720
  const pausedTasks = await getPausedTasksForAgent(myAgentId);
1669
1721
  const tasks = await Promise.all(
1670
- pausedTasks.map(async (task) => ({
1671
- ...task,
1672
- attachments: await getTaskAttachments(task.id),
1673
- })),
1722
+ pausedTasks.map(async (task) => {
1723
+ const requestedBy = await buildTaskRequestedBy(task);
1724
+ return {
1725
+ ...task,
1726
+ attachments: await getTaskAttachments(task.id),
1727
+ ...(requestedBy && { requestedBy }),
1728
+ };
1729
+ }),
1674
1730
  );
1675
1731
  listPausedTasks.respond(res, 200, { tasks });
1676
1732
  return true;
package/src/http/utils.ts CHANGED
@@ -2,6 +2,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { getActiveTaskCount } from "../be/db";
3
3
  import type { SwarmSpan } from "../otel";
4
4
  import { isEnvFlagEnabled } from "../utils/env-flag";
5
+ import { getNodeEnv } from "../utils/node-env";
5
6
  import { scrubSecrets } from "../utils/secret-scrubber";
6
7
 
7
8
  export function duplicateGuardedQueryKey(query: URLSearchParams): string | undefined {
@@ -41,6 +42,18 @@ function getAllowedOrigins(): string[] {
41
42
  .filter(Boolean);
42
43
  }
43
44
 
45
+ /**
46
+ * Loopback origins on any port are trusted in local development
47
+ * (`NODE_ENV=development`, set by `start:http` and `dev:http`). The dashboard
48
+ * dev server, Playwright, or a second worktree pick arbitrary ports, and every
49
+ * loopback page already runs on the developer's machine. The production binary
50
+ * and the Docker image run without NODE_ENV, so this never applies there.
51
+ */
52
+ function isDevLoopbackOrigin(origin: string): boolean {
53
+ if (getNodeEnv() !== "development") return false;
54
+ return /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d{1,5})?$/.test(origin);
55
+ }
56
+
44
57
  /**
45
58
  * Decide whether `origin` may receive credentialed CORS headers.
46
59
  * Unset or blank uses the built-in hosted/dev allowlist. Exact entries retain
@@ -50,6 +63,7 @@ function getAllowedOrigins(): string[] {
50
63
  * Bare `*`, `https://*`, other wildcard positions, and non-origin URLs are ignored.
51
64
  */
52
65
  function originAllowedForCredentials(origin: string): boolean {
66
+ if (isDevLoopbackOrigin(origin)) return true;
53
67
  return getAllowedOrigins().some((entry) => {
54
68
  if (!entry.includes("*")) return entry === origin;
55
69